[SEBA-450] (part 2)

Add tox testing support on additional XOS library modules:

- xos-api
- xos-kafka (has no tests)
- xos-migrate (has no tests)
- xos-synchronizer

Change-Id: I98195bc9747971d3515882d517affe058dd86ac5
diff --git a/lib/xos-api/xosapi/.gitignore b/lib/xos-api/xosapi/.gitignore
new file mode 100644
index 0000000..b8cf076
--- /dev/null
+++ b/lib/xos-api/xosapi/.gitignore
@@ -0,0 +1,2 @@
+# setup.py copies this, don't commit it
+xosapi/VERSION
diff --git a/lib/xos-api/xosapi/chameleon_client/asleep.py b/lib/xos-api/xosapi/chameleon_client/asleep.py
index 295f675..cdba793 100644
--- a/lib/xos-api/xosapi/chameleon_client/asleep.py
+++ b/lib/xos-api/xosapi/chameleon_client/asleep.py
@@ -13,6 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
+from __future__ import absolute_import
 from twisted.internet import reactor
 from twisted.internet.defer import Deferred
 
@@ -25,4 +26,4 @@
     """
     d = Deferred()
     reactor.callLater(dt, lambda: d.callback(None))
-    return d
\ No newline at end of file
+    return d
diff --git a/lib/xos-api/xosapi/chameleon_client/grpc_client.py b/lib/xos-api/xosapi/chameleon_client/grpc_client.py
index a8ad69d..7d3839d 100644
--- a/lib/xos-api/xosapi/chameleon_client/grpc_client.py
+++ b/lib/xos-api/xosapi/chameleon_client/grpc_client.py
@@ -20,6 +20,7 @@
 semantics are derived from the recovered schema.
 """
 
+from __future__ import absolute_import
 import os
 import sys
 import time
@@ -33,12 +34,12 @@
 from structlog import get_logger
 from twisted.internet import reactor
 from twisted.internet.defer import inlineCallbacks, returnValue
-from werkzeug.exceptions import ServiceUnavailable
+from twisted.internet.error import ConnectError
 
-from protos.schema_pb2_grpc import SchemaServiceStub
+from .protos.schema_pb2_grpc import SchemaServiceStub
 from google.protobuf.empty_pb2 import Empty
 
-from asleep import asleep
+from .asleep import asleep
 
 log = get_logger()
 
@@ -143,14 +144,14 @@
 
             return
 
-        except _Rendezvous, e:
+        except _Rendezvous as e:
             if e.code() == grpc.StatusCode.UNAVAILABLE:
                 log.info('grpc-endpoint-not-available')
             else:
                 log.exception('rendezvous error', e=e)
             yield self._backoff('not-available')
 
-        except Exception, e:
+        except Exception:
             if not self.shutting_down:
                 log.exception('cannot-connect', endpoint=_endpoint)
             yield self._backoff('unknown-error')
@@ -277,7 +278,7 @@
                       if f.endswith('_pb2.py')]:
             modname = fname[:-len('.py')]
             log.debug('test-import', modname=modname)
-            _ = __import__(modname)
+            _ = __import__(modname)  # noqa: F841
 
     @inlineCallbacks
     def invoke(self, stub, method_name, request, metadata, retry=1):
@@ -291,17 +292,17 @@
         """
 
         if not self.connected:
-            raise ServiceUnavailable()
+            raise ConnectError()
 
         try:
             method = getattr(stub(self.channel), method_name)
             response, rendezvous = method.with_call(request, metadata=metadata)
             returnValue((response, rendezvous.trailing_metadata()))
 
-        except grpc._channel._Rendezvous, e:
+        except grpc._channel._Rendezvous as e:
             code = e.code()
             if code == grpc.StatusCode.UNAVAILABLE:
-                e = ServiceUnavailable()
+                e = ConnectError()
 
                 if self.connected:
                     self.connected = False
diff --git a/lib/xos-api/xosapi/chameleon_client/protoc_plugins/gw_gen.py b/lib/xos-api/xosapi/chameleon_client/protoc_plugins/gw_gen.py
index 9cd2f6f..cafee67 100755
--- a/lib/xos-api/xosapi/chameleon_client/protoc_plugins/gw_gen.py
+++ b/lib/xos-api/xosapi/chameleon_client/protoc_plugins/gw_gen.py
@@ -15,6 +15,7 @@
 # limitations under the License.
 #
 
+from __future__ import absolute_import
 import sys
 
 from google.protobuf.compiler import plugin_pb2 as plugin
@@ -23,7 +24,7 @@
 from jinja2 import Template
 from simplejson import dumps
 
-from xosapi.chameleon_client.protos import annotations_pb2, http_pb2
+from xosapi.chameleon_client.protos import http_pb2
 
 template = Template("""
 # Generated file; please do not edit
diff --git a/lib/xos-api/xosapi/chameleon_client/protos/annotations_pb2.py b/lib/xos-api/xosapi/chameleon_client/protos/annotations_pb2.py
index 5abc663..aa451e5 100644
--- a/lib/xos-api/xosapi/chameleon_client/protos/annotations_pb2.py
+++ b/lib/xos-api/xosapi/chameleon_client/protos/annotations_pb2.py
@@ -18,6 +18,7 @@
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # source: annotations.proto
 
+from __future__ import absolute_import
 import sys
 _b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
 from google.protobuf import descriptor as _descriptor
@@ -30,7 +31,7 @@
 _sym_db = _symbol_database.Default()
 
 
-import http_pb2 as http__pb2
+from . import http_pb2 as http__pb2
 from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2
 
 
diff --git a/lib/xos-api/xosapi/chameleon_client/protos/annotations_pb2_grpc.py b/lib/xos-api/xosapi/chameleon_client/protos/annotations_pb2_grpc.py
index 972edf5..1ec9f40 100644
--- a/lib/xos-api/xosapi/chameleon_client/protos/annotations_pb2_grpc.py
+++ b/lib/xos-api/xosapi/chameleon_client/protos/annotations_pb2_grpc.py
@@ -16,4 +16,5 @@
 #
 
 # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
+from __future__ import absolute_import
 import grpc
diff --git a/lib/xos-api/xosapi/chameleon_client/protos/http_pb2.py b/lib/xos-api/xosapi/chameleon_client/protos/http_pb2.py
index f420e61..8e359f5 100644
--- a/lib/xos-api/xosapi/chameleon_client/protos/http_pb2.py
+++ b/lib/xos-api/xosapi/chameleon_client/protos/http_pb2.py
@@ -18,6 +18,7 @@
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # source: http.proto
 
+from __future__ import absolute_import
 import sys
 _b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
 from google.protobuf import descriptor as _descriptor
diff --git a/lib/xos-api/xosapi/chameleon_client/protos/http_pb2_grpc.py b/lib/xos-api/xosapi/chameleon_client/protos/http_pb2_grpc.py
index 972edf5..1ec9f40 100644
--- a/lib/xos-api/xosapi/chameleon_client/protos/http_pb2_grpc.py
+++ b/lib/xos-api/xosapi/chameleon_client/protos/http_pb2_grpc.py
@@ -16,4 +16,5 @@
 #
 
 # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
+from __future__ import absolute_import
 import grpc
diff --git a/lib/xos-api/xosapi/chameleon_client/protos/schema_pb2.py b/lib/xos-api/xosapi/chameleon_client/protos/schema_pb2.py
index 06909b4..0e0159a 100644
--- a/lib/xos-api/xosapi/chameleon_client/protos/schema_pb2.py
+++ b/lib/xos-api/xosapi/chameleon_client/protos/schema_pb2.py
@@ -18,6 +18,7 @@
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # source: schema.proto
 
+from __future__ import absolute_import
 import sys
 _b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
 from google.protobuf import descriptor as _descriptor
diff --git a/lib/xos-api/xosapi/chameleon_client/protos/schema_pb2_grpc.py b/lib/xos-api/xosapi/chameleon_client/protos/schema_pb2_grpc.py
index 65c9afa..06c1456 100644
--- a/lib/xos-api/xosapi/chameleon_client/protos/schema_pb2_grpc.py
+++ b/lib/xos-api/xosapi/chameleon_client/protos/schema_pb2_grpc.py
@@ -16,10 +16,11 @@
 #
 
 # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
+from __future__ import absolute_import
 import grpc
 
 from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2
-import schema_pb2 as schema__pb2
+from . import schema_pb2 as schema__pb2
 
 
 class SchemaServiceStub(object):
diff --git a/lib/xos-api/xosapi/convenience/addresspool.py b/lib/xos-api/xosapi/convenience/addresspool.py
index 13006c5..e83c818 100644
--- a/lib/xos-api/xosapi/convenience/addresspool.py
+++ b/lib/xos-api/xosapi/convenience/addresspool.py
@@ -13,6 +13,8 @@
 # limitations under the License.
 
 
+from __future__ import absolute_import
+
 from xosapi.orm import ORMWrapper, register_convenience_wrapper
 
 
diff --git a/lib/xos-api/xosapi/convenience/controller.py b/lib/xos-api/xosapi/convenience/controller.py
index 437b54a..4e13dac 100644
--- a/lib/xos-api/xosapi/convenience/controller.py
+++ b/lib/xos-api/xosapi/convenience/controller.py
@@ -12,6 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+from __future__ import absolute_import
+
 from xosapi.orm import ORMWrapper, register_convenience_wrapper
 
 
diff --git a/lib/xos-api/xosapi/convenience/instance.py b/lib/xos-api/xosapi/convenience/instance.py
index 3941643..f373f12 100644
--- a/lib/xos-api/xosapi/convenience/instance.py
+++ b/lib/xos-api/xosapi/convenience/instance.py
@@ -13,6 +13,8 @@
 # limitations under the License.
 
 
+from __future__ import absolute_import
+
 from xosapi.orm import ORMWrapper, register_convenience_wrapper
 
 
diff --git a/lib/xos-api/xosapi/convenience/network.py b/lib/xos-api/xosapi/convenience/network.py
index ffa89f7..95cb972 100644
--- a/lib/xos-api/xosapi/convenience/network.py
+++ b/lib/xos-api/xosapi/convenience/network.py
@@ -12,7 +12,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from xosapi.orm import ORMWrapper, ORMLocalObjectManager, register_convenience_wrapper
+from __future__ import absolute_import
+
+from xosapi.orm import (ORMLocalObjectManager, ORMWrapper,
+                        register_convenience_wrapper)
 
 
 class ORMWrapperNetwork(ORMWrapper):
diff --git a/lib/xos-api/xosapi/convenience/onosapp.py b/lib/xos-api/xosapi/convenience/onosapp.py
index fd791a8..2970581 100644
--- a/lib/xos-api/xosapi/convenience/onosapp.py
+++ b/lib/xos-api/xosapi/convenience/onosapp.py
@@ -12,8 +12,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from xosapi.orm import register_convenience_wrapper
+from __future__ import absolute_import
+
 from xosapi.convenience.serviceinstance import ORMWrapperServiceInstance
+from xosapi.orm import register_convenience_wrapper
 
 
 class ORMWrapperONOSApp(ORMWrapperServiceInstance):
diff --git a/lib/xos-api/xosapi/convenience/port.py b/lib/xos-api/xosapi/convenience/port.py
index 61e7377..2c9b9a6 100644
--- a/lib/xos-api/xosapi/convenience/port.py
+++ b/lib/xos-api/xosapi/convenience/port.py
@@ -12,6 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+from __future__ import absolute_import
+
 from xosapi.orm import ORMWrapper, register_convenience_wrapper
 
 
diff --git a/lib/xos-api/xosapi/convenience/privilege.py b/lib/xos-api/xosapi/convenience/privilege.py
index 9bdb635..0071842 100644
--- a/lib/xos-api/xosapi/convenience/privilege.py
+++ b/lib/xos-api/xosapi/convenience/privilege.py
@@ -13,6 +13,8 @@
 # limitations under the License.
 
 
+from __future__ import absolute_import
+
 from xosapi.orm import ORMWrapper, register_convenience_wrapper
 
 
diff --git a/lib/xos-api/xosapi/convenience/service.py b/lib/xos-api/xosapi/convenience/service.py
index ab82908..8c9985a 100644
--- a/lib/xos-api/xosapi/convenience/service.py
+++ b/lib/xos-api/xosapi/convenience/service.py
@@ -12,6 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+from __future__ import absolute_import
+
 from xosapi.orm import ORMWrapper, register_convenience_wrapper
 
 
diff --git a/lib/xos-api/xosapi/convenience/serviceinstance.py b/lib/xos-api/xosapi/convenience/serviceinstance.py
index 33ab553..10adb16 100644
--- a/lib/xos-api/xosapi/convenience/serviceinstance.py
+++ b/lib/xos-api/xosapi/convenience/serviceinstance.py
@@ -11,10 +11,11 @@
 # limitations under the License.
 
 
-from xosapi.orm import ORMWrapper, register_convenience_wrapper
+from __future__ import absolute_import
 
-from xosconfig import Config
 from multistructlog import create_logger
+from xosapi.orm import ORMWrapper, register_convenience_wrapper
+from xosconfig import Config
 
 log = create_logger(Config().get("logging"))
 
@@ -28,11 +29,6 @@
         return attrs
 
     @property
-    def tenantattribute_dict(self):
-        log.warn("tenantattribute_dict method is deprecated")
-        return self.serviceinstanceattribute_dict
-
-    @property
     def westbound_service_instances(self):
         links = self.provided_links.all()
         instances = []
@@ -50,32 +46,6 @@
 
         return instances
 
-    def create_eastbound_instance(self):
-
-        # Already has a chain
-        if len(self.eastbound_service_instances) > 0 and not self.is_new:
-            log.debug("MODEL_POLICY: Subscriber %s is already part of a chain" % si.id)
-            return
-
-        # if it does not have a chain,
-        # Find links to the next element in the service chain
-        # and create one
-
-        links = self.owner.subscribed_dependencies.all()
-
-        for link in links:
-            si_class = link.provider_service.get_service_instance_class_name()
-            log.info(" %s creating %s" % (self.model_name, si_class))
-
-            eastbound_si_class = model_accessor.get_model_class(si_class)
-            eastbound_si = eastbound_si_class()
-            eastbound_si.owner_id = link.provider_service_id
-            eastbound_si.save()
-            link = ServiceInstanceLink(
-                provider_service_instance=eastbound_si, subscriber_service_instance=si
-            )
-            link.save()
-
     def get_westbound_service_instance_properties(self, prop_name, include_self=False):
         if include_self and hasattr(self, prop_name):
             return getattr(self, prop_name)
diff --git a/lib/xos-api/xosapi/convenience/slice.py b/lib/xos-api/xosapi/convenience/slice.py
index abdd485..0a10311 100644
--- a/lib/xos-api/xosapi/convenience/slice.py
+++ b/lib/xos-api/xosapi/convenience/slice.py
@@ -12,7 +12,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from xosapi.orm import ORMWrapper, ORMLocalObjectManager, register_convenience_wrapper
+from __future__ import absolute_import
+
+from xosapi.orm import (ORMLocalObjectManager, ORMWrapper,
+                        register_convenience_wrapper)
 
 
 class ORMWrapperSlice(ORMWrapper):
diff --git a/lib/xos-api/xosapi/convenience/tag.py b/lib/xos-api/xosapi/convenience/tag.py
index aaeaf65..eb6490f 100644
--- a/lib/xos-api/xosapi/convenience/tag.py
+++ b/lib/xos-api/xosapi/convenience/tag.py
@@ -12,6 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+from __future__ import absolute_import
+
 from xosapi.orm import ORMWrapper, register_convenience_wrapper
 
 
diff --git a/lib/xos-api/xosapi/convenience/user.py b/lib/xos-api/xosapi/convenience/user.py
index 3205680..7dda414 100644
--- a/lib/xos-api/xosapi/convenience/user.py
+++ b/lib/xos-api/xosapi/convenience/user.py
@@ -12,7 +12,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+from __future__ import absolute_import
+
 import hashlib
+
 from xosapi.orm import ORMWrapper, register_convenience_wrapper
 
 
diff --git a/lib/xos-api/xosapi/fake_stub.py b/lib/xos-api/xosapi/fake_stub.py
deleted file mode 100644
index abfce00..0000000
--- a/lib/xos-api/xosapi/fake_stub.py
+++ /dev/null
@@ -1,593 +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.
-
-""" fake_stub.py
-
-    Implements a simple fake grpc stub to use for unit testing.
-"""
-
-import functools
-
-ContentTypeMap = {}
-
-
-class FakeObj(object):
-    BASES = []
-
-    def __init__(self, fields=[], **kwargs):
-        super(FakeObj, self).__setattr__("is_set", {})
-        super(FakeObj, self).__setattr__("fields", [])
-
-        for f in fields:
-            name = f["name"]
-            self.fields.append(name)
-            setattr(self, name, f["default"])
-
-        super(FakeObj, self).__setattr__("is_set", {})
-        for (k, v) in kwargs.items():
-            setattr(self, k, v)
-
-    def __repr__(self):
-        lines = []
-        for k in self.fields:
-            if self.is_set.get(k, False):
-                lines.append('%s: "%s"' % (k, getattr(self, k)))
-        if lines:
-            return "\n".join(lines) + "\n"
-        else:
-            return ""
-
-    def __setattr__(self, name, value):
-        self.is_set[name] = True
-        super(FakeObj, self).__setattr__(name, value)
-
-    def HasField(self, name):
-        """ Return True if the field is set in the protobuf. """
-
-        # gRPC throws a valueerror if the field doesn't exist in the schema
-        if name not in self.fields:
-            raise ValueError("Field %s does not exist in schema" % name)
-
-        # Fields that are always set
-        if name in ["leaf_model_name"]:
-            return True
-
-        field = self.DESCRIPTOR.fields_by_name[name].field_decl
-
-        # Reverse foreign keys lists are never None, they are an empty list
-        if field.get("fk_reverse", None):
-            return True
-
-        return self.is_set.get(name, False)
-
-    def ListFields(self):
-        fbn = self.DESCRIPTOR.fields_by_name
-        fieldlist = []
-        for (k, v) in fbn.items():
-            if self.is_set.get(k, False):
-                fieldlist.append((v, getattr(self, k)))
-        return fieldlist
-
-    @property
-    def self_content_type_id(self):
-        return "xos.%s" % self.__class__.__name__.lower()
-
-
-class FakeExtensionManager(object):
-    def __init__(self, obj, extensions):
-        self.obj = obj
-        self.extensions = extensions
-
-    def _FindExtensionByName(self, name):
-        return name
-
-    def __getitem__(self, name, default=None):
-        if name in self.extensions:
-            return self.extensions[name]
-        return default
-
-
-class FakeFieldOption(object):
-    def __init__(self, modelName=None, reverseFieldName=None):
-        self.modelName = modelName
-        self.reverseFieldName = reverseFieldName
-
-
-class FakeField(object):
-    def __init__(self, field):
-        extensions = {}
-
-        self.field_decl = field
-        self.name = field["name"]
-
-        fk_model = field.get("fk_model", None)
-        if fk_model:
-            reverseFieldName = field.get("fk_reverseFieldName", None)
-            extensions["xos.foreignKey"] = FakeFieldOption(
-                modelName=fk_model, reverseFieldName=reverseFieldName
-            )
-
-        fk_reverse = field.get("fk_reverse", None)
-        if fk_reverse:
-            extensions["xos.reverseForeignKey"] = FakeFieldOption(modelName=fk_reverse)
-
-        self.Extensions = FakeExtensionManager(self, extensions)
-
-    def GetOptions(self):
-        return self
-
-
-class FakeDescriptor(object):
-    def __init__(self, objName):
-        global ContentTypeIdCounter
-        global ContentTypeMap
-        self.objName = objName
-        if objName in ContentTypeMap:
-            ct = ContentTypeMap[objName]
-        else:
-            ct = "xos.%s" % objName.lower()
-            ContentTypeMap[objName] = ct
-        self.Extensions = FakeExtensionManager(self, {"xos.contentTypeId": ct})
-
-    def GetOptions(self):
-        return self
-
-    @property
-    def fields_by_name(self):
-        cls = globals()[self.objName]
-        fbn = {}
-        for field in cls.FIELDS:
-            fake_field = FakeField(field)
-            fbn[field["name"]] = fake_field
-
-        return fbn
-
-
-class Controller(FakeObj):
-    FIELDS = (
-        {"name": "id", "default": 0},
-        {"name": "name", "default": ""},
-        {"name": "deployment_id", "default": 0, "fk_model": "Deployment"},
-        {"name": "class_names", "default": "Controller"},
-    )
-
-    def __init__(self, **kwargs):
-        return super(Controller, self).__init__(self.FIELDS, **kwargs)
-
-    DESCRIPTOR = FakeDescriptor("Controller")
-
-
-class Deployment(FakeObj):
-    FIELDS = (
-        {"name": "id", "default": 0},
-        {"name": "name", "default": ""},
-        {"name": "class_names", "default": "Deployment"},
-    )
-
-    def __init__(self, **kwargs):
-        return super(Deployment, self).__init__(self.FIELDS, **kwargs)
-
-    DESCRIPTOR = FakeDescriptor("Controller")
-
-
-class User(FakeObj):
-    FIELDS = (
-        {"name": "id", "default": 0},
-        {"name": "email", "default": ""},
-        {"name": "site_id", "default": 0, "fk_model": "Site"},
-        {"name": "class_names", "default": "User"},
-    )
-
-    def __init__(self, **kwargs):
-        return super(User, self).__init__(self.FIELDS, **kwargs)
-
-    DESCRIPTOR = FakeDescriptor("User")
-
-
-class Slice(FakeObj):
-    FIELDS = (
-        {"name": "id", "default": 0},
-        {"name": "name", "default": ""},
-        {
-            "name": "site_id",
-            "default": 0,
-            "fk_model": "Site",
-            "fk_reverseFieldName": "slices",
-        },
-        {"name": "service_id", "default": 0, "fk_model": "Service"},
-        {"name": "creator_id", "default": 0, "fk_model": "User"},
-        {"name": "networks_ids", "default": [], "fk_reverse": "Network"},
-        {"name": "network", "default": ""},
-        {"name": "leaf_model_name", "default": "Slice"},
-        {"name": "class_names", "default": "Slice"},
-    )
-
-    def __init__(self, **kwargs):
-        return super(Slice, self).__init__(self.FIELDS, **kwargs)
-
-    DESCRIPTOR = FakeDescriptor("Slice")
-
-
-class Site(FakeObj):
-    FIELDS = (
-        {"name": "id", "default": 0},
-        {"name": "name", "default": ""},
-        {"name": "login_base", "default": ""},
-        {"name": "slices_ids", "default": [], "fk_reverse": "Slice"},
-        {"name": "leaf_model_name", "default": "Site"},
-        {"name": "class_names", "default": "Site"},
-    )
-
-    def __init__(self, **kwargs):
-        return super(Site, self).__init__(self.FIELDS, **kwargs)
-
-    DESCRIPTOR = FakeDescriptor("Site")
-
-
-class Service(FakeObj):
-    FIELDS = (
-        {"name": "id", "default": 0},
-        {"name": "name", "default": ""},
-        {"name": "slices_ids", "default": [], "fk_reverse": "Slice"},
-        {"name": "leaf_model_name", "default": "Service"},
-        {"name": "class_names", "default": "Service"},
-    )
-
-    def __init__(self, **kwargs):
-        return super(Service, self).__init__(self.FIELDS, **kwargs)
-
-    DESCRIPTOR = FakeDescriptor("Service")
-
-
-class ServiceInstance(FakeObj):
-    FIELDS = (
-        {"name": "id", "default": 0},
-        {"name": "owher", "default": 0, "fk_model": "Service"},
-        {"name": "leaf_model_name", "default": "ServiceInstance"},
-        {"name": "class_names", "default": "ServiceInstance"},
-    )
-
-    def __init__(self, **kwargs):
-        return super(ServiceInstance, self).__init__(self.FIELDS, **kwargs)
-
-    DESCRIPTOR = FakeDescriptor("ServiceInstance")
-
-
-class ONOSService(FakeObj):
-    FIELDS = (
-        {"name": "id", "default": 0},
-        {"name": "name", "default": ""},
-        {"name": "leaf_model_name", "default": "ONOSService"},
-        {"name": "class_names", "default": "ONOSService,Service"},
-    )
-
-    BASES = ["Service"]
-
-    def __init__(self, **kwargs):
-        return super(ONOSService, self).__init__(self.FIELDS, **kwargs)
-
-    DESCRIPTOR = FakeDescriptor("ONOSService")
-
-
-class Network(FakeObj):
-    FIELDS = (
-        {"name": "id", "default": 0},
-        {"name": "name", "default": ""},
-        {"name": "owner_id", "default": 0, "fk_model": "Slice"},
-        {"name": "template_id", "default": 0, "fk_model": "NetworkTemplate"},
-        {
-            "name": "controllernetworks_ids",
-            "default": [],
-            "fk_reverse": "ControllerNetwork",
-        },
-        {"name": "leaf_model_name", "default": "Network"},
-        {"name": "class_names", "default": "Network"},
-    )
-
-    def __init__(self, **kwargs):
-        return super(Network, self).__init__(self.FIELDS, **kwargs)
-
-    DESCRIPTOR = FakeDescriptor("Network")
-
-
-class NetworkTemplate(FakeObj):
-    FIELDS = (
-        {"name": "id", "default": 0},
-        {"name": "name", "default": ""},
-        {"name": "vtn_kind", "default": ""},
-        {"name": "leaf_model_name", "default": "NetworkTemplate"},
-        {"name": "class_names", "default": "NetworkTemplate"},
-    )
-
-    def __init__(self, **kwargs):
-        return super(NetworkTemplate, self).__init__(self.FIELDS, **kwargs)
-
-    DESCRIPTOR = FakeDescriptor("NetworkTemplate")
-
-
-class ControllerNetwork(FakeObj):
-    FIELDS = (
-        {"name": "id", "default": 0},
-        {"name": "network_id", "default": 0, "fk_model": "Network"},
-        {"name": "controller_id", "default": 0, "fk_model": "Controller"},
-        {"name": "leaf_model_name", "default": "ControllerNetwork"},
-        {"name": "class_names", "default": "ControllerNetwork"},
-    )
-
-    def __init__(self, **kwargs):
-        return super(ControllerNetwork, self).__init__(self.FIELDS, **kwargs)
-
-    DESCRIPTOR = FakeDescriptor("ControllerNetwork")
-
-
-class NetworkSlice(FakeObj):
-    FIELDS = (
-        {"name": "id", "default": 0},
-        {"name": "network_id", "default": 0, "fk_model": "Network"},
-        {"name": "slice_id", "default": 0, "fk_model": "Slice"},
-        {"name": "leaf_model_name", "default": "NetworkSlice"},
-        {"name": "class_names", "default": "NetworkSlice"},
-    )
-
-    def __init__(self, **kwargs):
-        return super(NetworkSlice, self).__init__(self.FIELDS, **kwargs)
-
-    DESCRIPTOR = FakeDescriptor("NetworkSlice")
-
-
-class Tag(FakeObj):
-    FIELDS = (
-        {"name": "id", "default": 0},
-        {"name": "service_id", "default": None},
-        {"name": "name", "default": ""},
-        {"name": "value", "default": ""},
-        {"name": "content_type", "default": None},
-        {"name": "object_id", "default": None},
-        {"name": "leaf_model_name", "default": "Tag"},
-        {"name": "class_names", "default": "Tag"},
-    )
-
-    def __init__(self, **kwargs):
-        return super(Tag, self).__init__(self.FIELDS, **kwargs)
-
-    DESCRIPTOR = FakeDescriptor("Tag")
-
-
-class TestModel(FakeObj):
-    FIELDS = (
-        {"name": "id", "default": 0},
-        {"name": "intfield", "default": 0},
-        {"name": "stringfield", "default": "somestring"},
-        {"name": "testmodeltwos_ids", "default": [], "fk_reverse": "TestModelTwo"},
-        {"name": "class_names", "default": "TestModel"},
-    )
-
-    def __init__(self, **kwargs):
-        return super(TestModel, self).__init__(self.FIELDS, **kwargs)
-
-    DESCRIPTOR = FakeDescriptor("TestModel")
-
-
-class TestModelTwo(FakeObj):
-    FIELDS = (
-        {"name": "id", "default": 0},
-        {"name": "intfieldtwo", "default": 0},
-        {"name": "stringfieldtwo", "default": "somestringtwo"},
-        {
-            "name": "testmodel_id",
-            "default": 0,
-            "fk_model": "TestModel",
-            "fk_reverseFieldName": "testmodeltwos",
-        },
-        {"name": "class_names", "default": "TestModel"},
-    )
-
-    def __init__(self, **kwargs):
-        return super(TestModelTwo, self).__init__(self.FIELDS, **kwargs)
-
-    DESCRIPTOR = FakeDescriptor("TestModelTwo")
-
-
-class ID(FakeObj):
-    pass
-
-
-class FakeItemList(object):
-    def __init__(self, items):
-        self.items = items
-
-
-class FakeElement(object):
-    EQUAL = "equal"
-    IEXACT = "iexact"
-
-    def __init__(self):
-        pass
-
-
-class FakeElements(object):
-    def __init__(self):
-        self.items = []
-
-    def add(self):
-        el = FakeElement()
-        self.items.append(el)
-        return el
-
-    def __getitem__(self, index):
-        return self.items[index]
-
-    def __len__(self):
-        return len(self.items)
-
-
-class FakeQuery(object):
-    DEFAULT = 0
-    ALL = 1
-    SYNCHRONIZER_DIRTY_OBJECTS = 2
-    SYNCHRONIZER_DELETED_OBJECTS = 3
-    SYNCHRONIZER_DIRTY_POLICIES = 4
-    SYNCHRONIZER_DELETED_POLICIES = 5
-
-    def __init__(self):
-        self.elements = FakeElements()
-
-
-class FakeStub(object):
-    def __init__(self):
-        self.id_counter = 1
-        self.objs = {}
-        self.deleted_objs = {}
-        for name in [
-            "Controller",
-            "Deployment",
-            "Slice",
-            "Site",
-            "Tag",
-            "Service",
-            "ServiceInstance",
-            "ONOSService",
-            "User",
-            "Network",
-            "NetworkTemplate",
-            "ControllerNetwork",
-            "NetworkSlice",
-            "TestModel",
-            "TestModelTwo",
-        ]:
-            setattr(self, "Get%s" % name, functools.partial(self.get, name))
-            setattr(self, "List%s" % name, functools.partial(self.list, name))
-            setattr(self, "Create%s" % name, functools.partial(self.create, name))
-            setattr(self, "Delete%s" % name, functools.partial(self.delete, name))
-            setattr(self, "Update%s" % name, functools.partial(self.update, name))
-            setattr(self, "Filter%s" % name, functools.partial(self.filter, name))
-
-    def make_key(self, name, id):
-        return "%s:%d" % (name, id.id)
-
-    def get(self, classname, id, metadata=None):
-        obj = self.objs.get(self.make_key(classname, id), None)
-        return obj
-
-    def list(self, classname, empty, metadata=None):
-        items = []
-        for (k, v) in self.objs.items():
-            (this_classname, id) = k.split(":")
-            if this_classname == classname:
-                items.append(v)
-        return FakeItemList(items)
-
-    def filter(self, classname, query, metadata=None):
-        items = []
-
-        if query.kind == FakeQuery.SYNCHRONIZER_DELETED_OBJECTS:
-            objs = self.deleted_objs.items()
-        else:
-            objs = self.objs.items()
-
-        for (k, v) in objs:
-            (this_classname, id) = k.split(":")
-            if this_classname != classname:
-                continue
-            match = True
-            for q in query.elements.items:
-                iValue = getattr(q, "iValue", None)
-                if (iValue is not None) and getattr(v, q.name) != iValue:
-                    match = False
-                sValue = getattr(q, "sValue", None)
-                if (sValue is not None) and getattr(v, q.name) != sValue:
-                    match = False
-            if match:
-                items.append(v)
-        return FakeItemList(items)
-
-    def create(self, classname, obj, metadata=None):
-        obj.id = self.id_counter
-        self.id_counter = self.id_counter + 1
-        k = self.make_key(classname, FakeObj(id=obj.id))
-        self.objs[k] = obj
-
-        for base_classname in obj.BASES:
-            base_class = globals()[base_classname]
-            base_obj = base_class(id=obj.id, leaf_model_name=classname)
-            k = self.make_key(base_classname, base_obj)
-            self.objs[k] = base_obj
-
-        return obj
-
-    def update(self, classname, obj, metadata=None):
-        # TODO: partial update support?
-        k = self.make_key(classname, FakeObj(id=obj.id))
-        self.objs[k] = obj
-        return obj
-
-    def delete(self, classname, id, metadata=None):
-        k = self.make_key(classname, id)
-        obj = self.objs[k]
-        del self.objs[k]
-        self.deleted_objs[k] = obj
-
-
-class FakeCommonProtos(object):
-    def __init__(self):
-        self.ID = ID
-        self.Query = FakeQuery
-
-
-class FakeProtos(object):
-    def __init__(self):
-        for name in [
-            "Controller",
-            "Deployment",
-            "Slice",
-            "Site",
-            "ID",
-            "Tag",
-            "Service",
-            "ServiceInstance",
-            "ONOSService",
-            "User",
-            "Network",
-            "NetworkTemplate",
-            "ControllerNetwork",
-            "NetworkSlice",
-            "TestModel",
-            "TestModelTwo",
-        ]:
-            setattr(self, name, globals()[name])
-            self.common__pb2 = FakeCommonProtos()
-
-
-class FakeSymDb(object):
-    def __init__(self):
-        self._classes = {}
-        for name in [
-            "Controller",
-            "Deployment",
-            "Slice",
-            "Site",
-            "ID",
-            "Tag",
-            "Service",
-            "ServiceInstance",
-            "ONOSService",
-            "User",
-            "Network",
-            "NetworkTemplate",
-            "ControllerNetwork",
-            "NetworkSlice",
-            "TestModel",
-            "TestModelTwo",
-        ]:
-            self._classes["xos.%s" % name] = globals()[name]
diff --git a/lib/xos-api/xosapi/orm.py b/lib/xos-api/xosapi/orm.py
index 41d775f..08b73e3 100644
--- a/lib/xos-api/xosapi/orm.py
+++ b/lib/xos-api/xosapi/orm.py
@@ -12,15 +12,17 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from __future__ import print_function
+from __future__ import absolute_import, print_function
+
+import imp
 import os
 import sys
 import threading
 import time
-import imp
 import traceback
-from xosconfig import Config
+
 from multistructlog import create_logger
+from xosconfig import Config
 
 """
 Django-like ORM layer for gRPC
@@ -43,23 +45,26 @@
 u=c.xos_orm.User.objects.get(id=1)
 """
 
-
 log = create_logger(Config().get("logging"))
 
 convenience_wrappers = {}
 
-# Find the topmost synchronizer-specific function in the call stack
+
 def get_synchronizer_function():
+    """
+    Find the topmost synchronizer-specific function in the call stack
+    """
     result = None
-    for file,line,func,stmt in traceback.extract_stack():
+    for file, line, func, stmt in traceback.extract_stack():
         if file.startswith("/opt/xos/synchronizers"):
             if not result:
-                result = "%s:%s()" % (file,func)
+                result = "%s:%s()" % (file, func)
             if not file.startswith("/opt/xos/synchronizers/new_base"):
-                result = "%s:%s()" % (file,func)
+                result = "%s:%s()" % (file, func)
                 break
     return result
 
+
 class ORMGenericContentNotFoundException(Exception):
     pass
 
@@ -106,7 +111,7 @@
     def diff(self):
         d1 = self._initial
         d2 = self._dict
-        all_field_names = self._wrapped_class.DESCRIPTOR.fields_by_name.keys()
+        all_field_names = list(self._wrapped_class.DESCRIPTOR.fields_by_name.keys())
         diffs = []
         for k in all_field_names:
             if d1.get(k, None) != d2.get(k, None):
@@ -127,11 +132,11 @@
             that are set to default values.
         """
         if self.is_new:
-            return self._dict.keys()
-        return self.diff.keys()
+            return list(self._dict.keys())
+        return list(self.diff.keys())
 
     def has_field_changed(self, field_name):
-        return field_name in self.diff.keys()
+        return field_name in list(self.diff.keys())
 
     def get_field_diff(self, field_name):
         return self.diff.get(field_name, None)
@@ -166,7 +171,7 @@
     def gen_fkmap(self):
         fkmap = {}
 
-        all_field_names = self._wrapped_class.DESCRIPTOR.fields_by_name.keys()
+        all_field_names = list(self._wrapped_class.DESCRIPTOR.fields_by_name.keys())
 
         for (name, field) in self._wrapped_class.DESCRIPTOR.fields_by_name.items():
             if name.endswith("_id"):
@@ -349,10 +354,10 @@
         if name == "pk":
             name = "id"
 
-        if name in self._fkmap.keys():
+        if name in list(self._fkmap.keys()):
             return self.fk_resolve(name)
 
-        if name in self._reverse_fkmap.keys():
+        if name in list(self._reverse_fkmap.keys()):
             return self.reverse_fk_resolve(name)
 
         try:
@@ -368,7 +373,7 @@
         return getattr(self._wrapped_class, name, *args, **kwargs)
 
     def __setattr__(self, name, value):
-        if name in self._fkmap.keys():
+        if name in list(self._fkmap.keys()):
             self.fk_set(name, value)
         elif name in self.__dict__:
             super(ORMWrapper, self).__setattr__(name, value)
@@ -423,17 +428,28 @@
     ):
         classname = self._wrapped_class.__class__.__name__
         if self.is_new:
-            log.debug("save(): is new", classname=classname, syncstep=get_synchronizer_function())
-            new_class = self.stub.invoke(
-                "Create%s" % classname, self._wrapped_class
+            log.debug(
+                "save(): is new",
+                classname=classname,
+                syncstep=get_synchronizer_function(),
             )
+            new_class = self.stub.invoke("Create%s" % classname, self._wrapped_class)
             self._wrapped_class = new_class
             self.is_new = False
         else:
             if self.has_changed:
-              log.debug("save(): updated", classname=classname, changed_fields=self.changed_fields, syncstep=get_synchronizer_function())
+                log.debug(
+                    "save(): updated",
+                    classname=classname,
+                    changed_fields=self.changed_fields,
+                    syncstep=get_synchronizer_function(),
+                )
             else:
-              log.debug("save(): no changes", classname=classname, syncstep=get_synchronizer_function())
+                log.debug(
+                    "save(): no changes",
+                    classname=classname,
+                    syncstep=get_synchronizer_function(),
+                )
             metadata = []
             if update_fields:
                 metadata.append(("update_fields", ",".join(update_fields)))
@@ -444,9 +460,7 @@
             if is_sync_save:
                 metadata.append(("is_sync_save", "1"))
             self.stub.invoke(
-                "Update%s" % classname,
-                self._wrapped_class,
-                metadata=metadata,
+                "Update%s" % classname, self._wrapped_class, metadata=metadata
             )
         self.do_post_save_fixups()
 
@@ -648,7 +662,7 @@
         return self.wrap_list(self._stub.invoke("Filter%s" % self._modelName, q))
 
     def get(self, **kwargs):
-        if kwargs.keys() == ["id"]:
+        if list(kwargs.keys()) == ["id"]:
             # the fast and easy case, look it up by id
             return self.wrap_single(
                 self._stub.invoke(
diff --git a/lib/xos-api/xosapi/test_config.yaml b/lib/xos-api/xosapi/test_config.yaml
deleted file mode 100644
index ae389f4..0000000
--- a/lib/xos-api/xosapi/test_config.yaml
+++ /dev/null
@@ -1,26 +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.
-
-
-name: test-coreapi
-logging:
-  version: 1
-  handlers:
-    console:
-      class: logging.StreamHandler
-  loggers:
-    'multistructlog':
-      handlers:
-          - console
diff --git a/lib/xos-api/xosapi/test_orm.py b/lib/xos-api/xosapi/test_orm.py
deleted file mode 100644
index 88bf6d4..0000000
--- a/lib/xos-api/xosapi/test_orm.py
+++ /dev/null
@@ -1,1082 +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 __future__ import print_function
-import exceptions
-import os
-import random
-import string
-import sys
-import unittest
-from mock import patch
-from StringIO import StringIO
-
-# by default, use fake stub rather than real core
-USE_FAKE_STUB = True
-
-PARENT_DIR = os.path.join(os.path.dirname(__file__), "..")
-
-
-class TestORM(unittest.TestCase):
-    def setUp(self):
-        from xosconfig import Config
-
-        test_path = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
-        config = os.path.join(test_path, "test_config.yaml")
-        Config.clear()
-        Config.init(config, "synchronizer-config-schema.yaml")
-        if USE_FAKE_STUB:
-            sys.path.append(PARENT_DIR)
-
-        # Import these after config, in case they depend on config
-        from xosapi.orm import ORMQuerySet, ORMLocalObjectManager
-
-        self.ORMQuerySet = ORMQuerySet
-        self.ORMLocalObjectManager = ORMLocalObjectManager
-
-    def tearDown(self):
-        if USE_FAKE_STUB:
-            sys.path.remove(PARENT_DIR)
-
-    def make_coreapi(self):
-        if USE_FAKE_STUB:
-            import xosapi.orm
-            from fake_stub import FakeStub, FakeProtos, FakeObj
-
-            xosapi.orm.import_convenience_methods()
-
-            stub = FakeStub()
-            api = xosapi.orm.ORMStub(
-                stub=stub,
-                package_name="xos",
-                protos=FakeProtos(),
-                empty=FakeObj,
-                enable_backoff=False,
-            )
-            return api
-        else:
-            return xos_grpc_client.coreapi
-
-    def test_repr_name(self):
-        orm = self.make_coreapi()
-        s = orm.Slice(name="foo")
-        self.assertNotEqual(s, None)
-        self.assertEqual(repr(s), "<Slice: foo>")
-
-    def test_str_name(self):
-        orm = self.make_coreapi()
-        s = orm.Slice(name="foo")
-        self.assertNotEqual(s, None)
-        self.assertEqual(str(s), "foo")
-
-    def test_dumpstr_name(self):
-        orm = self.make_coreapi()
-        s = orm.Slice(name="foo")
-        self.assertNotEqual(s, None)
-        self.assertEqual(s.dumpstr(), 'name: "foo"\n')
-
-    def test_repr_noname(self):
-        orm = self.make_coreapi()
-        s = orm.Slice()
-        self.assertNotEqual(s, None)
-        self.assertEqual(repr(s), "<Slice: id-0>")
-
-    def test_str_noname(self):
-        orm = self.make_coreapi()
-        s = orm.Slice()
-        self.assertNotEqual(s, None)
-        self.assertEqual(str(s), "Slice-0")
-
-    def test_dumpstr_noname(self):
-        orm = self.make_coreapi()
-        s = orm.Slice()
-        self.assertNotEqual(s, None)
-        self.assertEqual(s.dumpstr(), "")
-
-    def test_dump(self):
-        """ dump() is like dumpstr() but prints to stdout. Mock stdout by using a stringIO. """
-
-        orm = self.make_coreapi()
-        s = orm.Slice(name="foo")
-        self.assertNotEqual(s, None)
-        with patch("sys.stdout", new_callable=StringIO) as mock_stdout:
-            s.dump()
-            self.assertEqual(mock_stdout.getvalue(), 'name: "foo"\n\n')
-
-    def test_create(self):
-        orm = self.make_coreapi()
-        site = orm.Site(name="mysite")
-        site.save()
-        self.assertTrue(site.id > 0)
-
-    def test_get(self):
-        orm = self.make_coreapi()
-        site = orm.Site(name="mysite")
-        site.save()
-        self.assertTrue(site.id > 0)
-        got_site = orm.Site.objects.get(id=site.id)
-        self.assertNotEqual(got_site, None)
-        self.assertEqual(got_site.id, site.id)
-
-    def test_invalidate_cache(self):
-        orm = self.make_coreapi()
-        testModel = orm.TestModel()
-
-        # populate the caches with some placeholders we can test for
-        testModel.cache = {"a": 1}
-        testModel.reverse_cache = {"b": 2}
-
-        testModel.invalidate_cache()
-
-        self.assertEqual(testModel.cache, {})
-        self.assertEqual(testModel.reverse_cache, {})
-
-    def test_save_new(self):
-        orm = self.make_coreapi()
-        site = orm.Site(name="mysite")
-        site.save()
-        self.assertTrue(site.id > 0)
-
-    def test_save_existing(self):
-        orm = self.make_coreapi()
-        orig_len_sites = len(orm.Site.objects.all())
-        site = orm.Site(name="mysite")
-        site.save()
-        self.assertTrue(site.id > 0)
-
-        # there should be one new site
-        self.assertEqual(len(orm.Site.objects.all()), orig_len_sites + 1)
-
-        # retrieve the site, and update it
-        created_site_id = site.id
-        site = orm.Site.objects.get(id=created_site_id)
-        site.name = "mysitetwo"
-        site.save()
-
-        # the site_id should not have changed
-        self.assertEqual(site.id, created_site_id)
-
-        # there should still be only one new site
-        self.assertEqual(len(orm.Site.objects.all()), orig_len_sites + 1)
-
-        # the name should have changed
-        self.assertEqual(orm.Site.objects.get(id=created_site_id).name, "mysitetwo")
-
-    def test_delete(self):
-        orm = self.make_coreapi()
-        orig_len_sites = len(orm.Site.objects.all())
-        site = orm.Site(name="mysite")
-        site.save()
-        self.assertTrue(site.id > 0)
-        site.delete()
-        sites = orm.Site.objects.all()
-        self.assertEqual(len(sites), orig_len_sites)
-
-    def test_objects_all(self):
-        orm = self.make_coreapi()
-        orig_len_sites = len(orm.Site.objects.all())
-        site = orm.Site(name="mysite")
-        site.save()
-        sites = orm.Site.objects.all()
-        self.assertEqual(len(sites), orig_len_sites + 1)
-
-    def test_objects_first(self):
-        orm = self.make_coreapi()
-        site = orm.Site(name="mysite")
-        site.save()
-        site = orm.Site.objects.first()
-        self.assertNotEqual(site, None)
-
-    def test_content_type_map(self):
-        orm = self.make_coreapi()
-        self.assertTrue("Slice" in orm.content_type_map.values())
-        self.assertTrue("Site" in orm.content_type_map.values())
-        self.assertTrue("Tag" in orm.content_type_map.values())
-
-    def test_foreign_key_get(self):
-        orm = self.make_coreapi()
-        site = orm.Site(name="mysite")
-        site.save()
-        self.assertTrue(site.id > 0)
-        user = orm.User(
-            email="fake_"
-            + "".join(
-                random.choice(string.ascii_uppercase + string.digits) for _ in range(10)
-            ),
-            site_id=site.id,
-        )
-        user.save()
-        self.assertTrue(user.id > 0)
-        slice = orm.Slice(name="mysite_foo", site_id=site.id, creator_id=user.id)
-        slice.save()
-        self.assertTrue(slice.id > 0)
-        self.assertNotEqual(slice.site, None)
-        self.assertEqual(slice.site.id, site.id)
-
-    def test_foreign_key_set_with_invalidate(self):
-        orm = self.make_coreapi()
-        site = orm.Site(name="mysite")
-        site.save()
-        self.assertTrue(site.id > 0)
-        user = orm.User(
-            email="fake_"
-            + "".join(
-                random.choice(string.ascii_uppercase + string.digits) for _ in range(10)
-            ),
-            site_id=site.id,
-        )
-        user.save()
-        self.assertTrue(user.id > 0)
-        slice = orm.Slice(name="mysite_foo", site=site, creator_id=user.id)
-        slice.save()
-        slice.invalidate_cache()
-        self.assertTrue(slice.id > 0)
-        self.assertNotEqual(slice.site, None)
-        self.assertEqual(slice.site.id, site.id)
-        if not USE_FAKE_STUB:
-            self.assertTrue(slice.id in slice.site.slices_ids)
-
-    def test_foreign_key_set_without_invalidate(self):
-        orm = self.make_coreapi()
-        site = orm.Site(name="mysite")
-        site.save()
-        self.assertTrue(site.id > 0)
-        user = orm.User(
-            email="fake_"
-            + "".join(
-                random.choice(string.ascii_uppercase + string.digits) for _ in range(10)
-            ),
-            site_id=site.id,
-        )
-        user.save()
-        self.assertTrue(user.id > 0)
-        slice = orm.Slice(name="mysite_foo", site=site, creator_id=user.id)
-        slice.save()
-        self.assertTrue(slice.id > 0)
-        self.assertNotEqual(slice.site, None)
-        self.assertEqual(slice.site.id, site.id)
-        if not USE_FAKE_STUB:
-            self.assertTrue(slice.id in slice.site.slices_ids)
-            ids_from_models = [x.id for x in slice.site.slices.all()]
-            self.assertTrue(slice.id in ids_from_models)
-
-    def test_foreign_key_reset(self):
-        orm = self.make_coreapi()
-        site = orm.Site(name="mysite")
-        site.save()
-        self.assertTrue(site.id > 0)
-        user = orm.User(
-            email="fake_"
-            + "".join(
-                random.choice(string.ascii_uppercase + string.digits) for _ in range(10)
-            ),
-            site_id=site.id,
-        )
-        user.save()
-        self.assertTrue(user.id > 0)
-        slice = orm.Slice(name="mysite_foo", site=site, creator_id=user.id)
-        slice.save()
-        self.assertTrue(slice.id > 0)
-        self.assertNotEqual(slice.site, None)
-        self.assertEqual(slice.site.id, site.id)
-        if not USE_FAKE_STUB:
-            self.assertTrue(slice.id in site.slices_ids)
-            self.assertTrue(slice.id in slice.site.slices_ids)
-
-        site2 = orm.Site(name="mysite2")
-        site2.save()
-        slice.name = "mysite2_foo"
-        slice.site = site2
-        slice.save()
-        self.assertNotEqual(slice.site, None)
-        self.assertEqual(slice.site.id, site2.id)
-        if not USE_FAKE_STUB:
-            self.assertTrue(slice.id not in site.slices_ids)
-            self.assertTrue(slice.id in site2.slices_ids)
-            self.assertTrue(slice.id in slice.site.slices_ids)
-            ids_from_models1 = [x.id for x in site.slices.all()]
-            self.assertTrue(slice.id not in ids_from_models1)
-            ids_from_models2 = [x.id for x in site2.slices.all()]
-            self.assertTrue(slice.id in ids_from_models2)
-
-    def test_foreign_key_back_and_forth_even(self):
-        orm = self.make_coreapi()
-        site = orm.Site(name="mysite")
-        site.save()
-        self.assertTrue(site.id > 0)
-        user = orm.User(
-            email="fake_"
-            + "".join(
-                random.choice(string.ascii_uppercase + string.digits) for _ in range(10)
-            ),
-            site_id=site.id,
-        )
-        user.save()
-        self.assertTrue(user.id > 0)
-        slice = orm.Slice(name="mysite_foo", site=site, creator_id=user.id)
-        slice.save()
-        self.assertTrue(slice.id > 0)
-        self.assertNotEqual(slice.site, None)
-        self.assertEqual(slice.site.id, site.id)
-        if not USE_FAKE_STUB:
-            self.assertTrue(slice.id in site.slices_ids)
-            self.assertTrue(slice.id in slice.site.slices_ids)
-
-        site2 = orm.Site(name="mysite2")
-        site2.save()
-        slice.name = "mysite2_foo"
-        slice.site = site2
-        slice.site = site
-        slice.site = site2
-        slice.site = site
-        slice.save()
-        self.assertNotEqual(slice.site, None)
-        self.assertEqual(slice.site.id, site.id)
-        if not USE_FAKE_STUB:
-            self.assertTrue(slice.id not in site2.slices_ids)
-            self.assertTrue(slice.id in site.slices_ids)
-            self.assertTrue(slice.id in slice.site.slices_ids)
-
-    def test_foreign_key_back_and_forth_odd(self):
-        orm = self.make_coreapi()
-        site = orm.Site(name="mysite")
-        site.save()
-        self.assertTrue(site.id > 0)
-        user = orm.User(
-            email="fake_"
-            + "".join(
-                random.choice(string.ascii_uppercase + string.digits) for _ in range(10)
-            ),
-            site_id=site.id,
-        )
-        user.save()
-        self.assertTrue(user.id > 0)
-        slice = orm.Slice(name="mysite_foo", site=site, creator_id=user.id)
-        slice.save()
-        self.assertTrue(slice.id > 0)
-        self.assertNotEqual(slice.site, None)
-        self.assertEqual(slice.site.id, site.id)
-        if not USE_FAKE_STUB:
-            self.assertTrue(slice.id in site.slices_ids)
-            self.assertTrue(slice.id in slice.site.slices_ids)
-
-        site2 = orm.Site(name="mysite2")
-        site2.save()
-        slice.name = "mysite2_foo"
-        slice.site = site2
-        slice.site = site
-        slice.site = site2
-        slice.site = site
-        slice.site = site2
-        slice.save()
-        self.assertNotEqual(slice.site, None)
-        self.assertEqual(slice.site.id, site2.id)
-        if not USE_FAKE_STUB:
-            self.assertTrue(slice.id not in site.slices_ids)
-            self.assertTrue(slice.id in site2.slices_ids)
-            self.assertTrue(slice.id in slice.site.slices_ids)
-
-    def test_foreign_key_create_null(self):
-        orm = self.make_coreapi()
-        site = orm.Site(name="mysite")
-        site.save()
-        self.assertTrue(site.id > 0)
-        user = orm.User(
-            email="fake_"
-            + "".join(
-                random.choice(string.ascii_uppercase + string.digits) for _ in range(10)
-            ),
-            site_id=site.id,
-        )
-        user.save()
-        self.assertTrue(user.id > 0)
-        slice = orm.Slice(
-            name="mysite_foo", site=site, service=None, creator_id=user.id
-        )
-        slice.save()
-        slice.invalidate_cache()
-        self.assertTrue(slice.id > 0)
-        self.assertEqual(slice.service, None)
-
-    def test_foreign_key_set_null(self):
-        orm = self.make_coreapi()
-        site = orm.Site(name="mysite")
-        site.save()
-        self.assertTrue(site.id > 0)
-        user = orm.User(
-            email="fake_"
-            + "".join(
-                random.choice(string.ascii_uppercase + string.digits) for _ in range(10)
-            ),
-            site_id=site.id,
-        )
-        user.save()
-        self.assertTrue(user.id > 0)
-        service = orm.Service(name="myservice")
-        service.save()
-        self.assertTrue(service.id > 0)
-        # start out slice.service is non-None
-        slice = orm.Slice(
-            name="mysite_foo", site=site, service=service, creator_id=user.id
-        )
-        slice.save()
-        slice.invalidate_cache()
-        self.assertTrue(slice.id > 0)
-        self.assertNotEqual(slice.service, None)
-        self.assertEqual(slice.service.id, service.id)
-        # now set it to None
-        slice.service = None
-        slice.save()
-        slice.invalidate_cache()
-        self.assertEqual(slice.service, None)
-
-    def test_generic_foreign_key_get(self):
-        orm = self.make_coreapi()
-        service = orm.Service(name="myservice")
-        service.save()
-        site = orm.Site(name="mysite")
-        site.save()
-        self.assertTrue(site.id > 0)
-        tag = orm.Tag(
-            service=service,
-            name="mytag",
-            value="somevalue",
-            content_type=site.self_content_type_id,
-            object_id=site.id,
-        )
-        tag.save()
-        self.assertTrue(tag.id > 0)
-        self.assertNotEqual(tag.content_object, None)
-        self.assertEqual(tag.content_object.id, site.id)
-
-    def test_generic_foreign_key_get_decl(self):
-        orm = self.make_coreapi()
-        service = orm.Service(name="myservice")
-        service.save()
-        site = orm.Site(name="mysite")
-        site.save()
-        self.assertTrue(site.id > 0)
-        tag = orm.Tag(
-            service=service,
-            name="mytag",
-            value="somevalue",
-            content_type=site.self_content_type_id + "_decl",
-            object_id=site.id,
-        )
-        tag.save()
-        self.assertTrue(tag.id > 0)
-        self.assertNotEqual(tag.content_object, None)
-        self.assertEqual(tag.content_object.id, site.id)
-
-    def test_generic_foreign_key_get_bad_contenttype(self):
-        orm = self.make_coreapi()
-        service = orm.Service(name="myservice")
-        service.save()
-        site = orm.Site(name="mysite")
-        site.save()
-        self.assertTrue(site.id > 0)
-        tag = orm.Tag(
-            service=service,
-            name="mytag",
-            value="somevalue",
-            content_type="does_not_exist",
-            object_id=site.id,
-        )
-        tag.save()
-        self.assertTrue(tag.id > 0)
-        with self.assertRaises(Exception) as e:
-
-            self.assertEqual(
-                e.exception.message,
-                "Content_type does_not_exist not found in self.content_type_map",
-            )
-
-    def test_generic_foreign_key_get_bad_id(self):
-        orm = self.make_coreapi()
-        service = orm.Service(name="myservice")
-        service.save()
-        site = orm.Site(name="mysite")
-        site.save()
-        self.assertTrue(site.id > 0)
-        tag = orm.Tag(
-            service=service,
-            name="mytag",
-            value="somevalue",
-            content_type=site.self_content_type_id,
-            object_id=4567,
-        )
-        tag.save()
-        self.assertTrue(tag.id > 0)
-        with self.assertRaises(Exception) as e:
-            self.assertEqual(
-                e.exception.message, "Object 4567 of model Site was not found"
-            )
-
-    def test_generic_foreign_key_set(self):
-        orm = self.make_coreapi()
-        service = orm.Service(name="myservice")
-        service.save()
-        site = orm.Site(name="mysite")
-        site.save()
-        self.assertTrue(site.id > 0)
-        tag = orm.Tag(service=service, name="mytag", value="somevalue")
-        tag.content_object = site
-        tag.invalidate_cache()
-        self.assertEqual(tag.content_type, site.self_content_type_id)
-        self.assertEqual(tag.object_id, site.id)
-        tag.save()
-        self.assertTrue(tag.id > 0)
-        self.assertNotEqual(tag.content_object, None)
-        self.assertEqual(tag.content_object.id, site.id)
-
-    def test_leaf_model_trivial(self):
-        orm = self.make_coreapi()
-        service = orm.Service(name="myservice")
-        service.save()
-        self.assertEqual(service.leaf_model_name, "Service")
-
-    def test_leaf_model_descendant(self):
-        orm = self.make_coreapi()
-        onos_service = orm.ONOSService(name="myservice")
-        onos_service.save()
-        self.assertEqual(onos_service.model_name, "ONOSService")
-        self.assertEqual(onos_service.leaf_model_name, "ONOSService")
-
-        service = orm.Service.objects.get(id=onos_service.id)
-        self.assertEqual(service.id, onos_service.id)
-        self.assertEqual(service.model_name, "Service")
-        self.assertEqual(service.leaf_model_name, "ONOSService")
-
-        onos_service_cast = service.leaf_model
-        self.assertEqual(onos_service_cast.model_name, "ONOSService")
-        self.assertEqual(onos_service_cast.leaf_model_name, "ONOSService")
-        self.assertEqual(onos_service_cast.id, onos_service.id)
-
-    def test_field_null(self):
-        """ In a saved object, if a nullable field is left set to None, make sure the ORM returns None """
-
-        orm = self.make_coreapi()
-        tm = orm.TestModel()
-        tm.save()
-
-        tm = orm.TestModel.objects.all()[0]
-        self.assertFalse(tm._wrapped_class.HasField("intfield"))
-        self.assertEqual(tm.intfield, None)
-
-    def test_field_null_new(self):
-        """ For models that haven't been saved yet, reading the field should return the gRPC default """
-
-        orm = self.make_coreapi()
-        tm = orm.TestModel()
-
-        self.assertEqual(tm.intfield, 0)
-
-    def test_field_non_null(self):
-        """ In a saved object, if a nullable field is set to a value, then make sure the ORM returns the value """
-
-        orm = self.make_coreapi()
-        tm = orm.TestModel(intfield=3)
-        tm.save()
-
-        tm = orm.TestModel.objects.all()[0]
-        self.assertEqual(tm.intfield, 3)
-
-    def test_field_set_null(self):
-        """ Setting a field to None is not allowed """
-
-        orm = self.make_coreapi()
-        tm = orm.TestModel()
-        with self.assertRaises(Exception) as e:
-            tm.intfile = None
-        self.assertEqual(
-            e.exception.message,
-            "Setting a non-foreignkey field to None is not supported",
-        )
-
-    def test_query_iexact(self):
-        orm = self.make_coreapi()
-        with patch.object(orm.grpc_stub, "FilterTestModel", autospec=True) as filter:
-            orm.TestModel.objects.filter(name__iexact="foo")
-            self.assertEqual(filter.call_count, 1)
-            q = filter.call_args[0][0]
-
-            self.assertEqual(q.kind, q.DEFAULT)
-            self.assertEqual(len(q.elements), 1)
-            self.assertEqual(q.elements[0].operator, q.elements[0].IEXACT)
-            self.assertEqual(q.elements[0].sValue, "foo")
-
-    def test_query_equal(self):
-        orm = self.make_coreapi()
-        with patch.object(orm.grpc_stub, "FilterTestModel", autospec=True) as filter:
-            orm.TestModel.objects.filter(name="foo")
-            self.assertEqual(filter.call_count, 1)
-            q = filter.call_args[0][0]
-
-            self.assertEqual(q.kind, q.DEFAULT)
-            self.assertEqual(len(q.elements), 1)
-            self.assertEqual(q.elements[0].operator, q.elements[0].EQUAL)
-            self.assertEqual(q.elements[0].sValue, "foo")
-
-    def test_ORMWrapper_dict(self):
-        orm = self.make_coreapi()
-
-        testModel = orm.TestModel(intfield=7, stringfield="foo")
-
-        self.assertDictEqual(testModel._dict, {"intfield": 7, "stringfield": "foo"})
-
-    def test_ORMWrapper_new_diff(self):
-        orm = self.make_coreapi()
-        site = orm.Site(name="mysite")
-
-        self.assertEqual(site.is_new, True)
-        self.assertEqual(site._dict, {"name": "mysite"})
-        self.assertEqual(site.diff, {})
-        self.assertEqual(site.changed_fields, ["name"])
-        self.assertEqual(site.has_field_changed("name"), False)
-        self.assertEqual(site.has_field_changed("login_base"), False)
-
-        site.login_base = "bar"
-
-        self.assertEqual(site._dict, {"login_base": "bar", "name": "mysite"})
-        self.assertEqual(site.diff, {"login_base": (None, "bar")})
-        self.assertIn("name", site.changed_fields)
-        self.assertIn("login_base", site.changed_fields)
-        self.assertEqual(site.has_field_changed("name"), False)
-        self.assertEqual(site.has_field_changed("login_base"), True)
-        self.assertEqual(site.get_field_diff("login_base"), (None, "bar"))
-
-    def test_ORMWrapper_existing_diff(self):
-        orm = self.make_coreapi()
-        site = orm.Site(name="mysite", login_base="foo")
-        site.save()
-        site = orm.Site.objects.first()
-
-        self.assertEqual(site.is_new, False)
-        self.assertEqual(site._dict, {"id": 1, "name": "mysite", "login_base": "foo"})
-        self.assertEqual(site.diff, {})
-        self.assertEqual(site.changed_fields, [])
-        self.assertEqual(site.has_field_changed("name"), False)
-        self.assertEqual(site.has_field_changed("login_base"), False)
-
-        site.login_base = "bar"
-
-        self.assertEqual(site._dict, {"id": 1, "login_base": "bar", "name": "mysite"})
-        self.assertEqual(site.diff, {"login_base": ("foo", "bar")})
-        self.assertIn("login_base", site.changed_fields)
-        self.assertEqual(site.has_field_changed("name"), False)
-        self.assertEqual(site.has_field_changed("login_base"), True)
-
-    def test_ORMWrapper_diff_after_save(self):
-        orm = self.make_coreapi()
-        site = orm.Site(name="mysite", login_base="foo")
-        site.save()
-        site = orm.Site.objects.first()
-
-        self.assertEqual(site.diff, {})
-
-        site.login_base = "bar"
-
-        self.assertEqual(site.diff, {"login_base": ("foo", "bar")})
-
-        site.save()
-
-        self.assertEqual(site.diff, {})
-
-    def test_ORMWrapper_recompute_initial(self):
-        """ For saved models, Recompute_initial should take recompute the set of initial values, removing all items
-            from the diff set.
-        """
-
-        orm = self.make_coreapi()
-
-        testModel = orm.TestModel()
-        testModel.save()
-
-        testModel.intfield = 9
-        self.assertEqual(testModel.changed_fields, ["intfield"])
-
-        testModel.recompute_initial()
-        self.assertEqual(testModel.changed_fields, [])
-
-    def test_ORMWrapper_create_attr(self):
-        orm = self.make_coreapi()
-
-        testModel = orm.TestModel()
-        testModel.create_attr("some_new_attribute", "foo")
-        self.assertEqual(testModel.some_new_attribute, "foo")
-
-    def test_ORMWrapper_save_changed_fields(self):
-        orm = self.make_coreapi()
-
-        testModel = orm.TestModel(intfield=7, stringfield="foo")
-        testModel.save()
-
-        testModel.intfield = 9
-
-        with patch.object(
-            orm.grpc_stub, "UpdateTestModel", wraps=orm.grpc_stub.UpdateTestModel
-        ) as update:
-            testModel.save_changed_fields()
-
-            self.assertEqual(update.call_count, 1)
-            self.assertIn("metadata", update.call_args[1])
-            update_fields_arg = [
-                x[1] for x in update.call_args[1]["metadata"] if x[0] == "update_fields"
-            ]
-            self.assertEqual(update_fields_arg, ["intfield"])
-
-    def test_ORMWrapper_get_generic_foreignkeys(self):
-        """ Currently this is a placeholder that returns an empty list """
-
-        orm = self.make_coreapi()
-
-        testModel = orm.TestModel()
-        self.assertEqual(testModel.get_generic_foreignkeys(), [])
-
-    def test_ORMWrapper_gen_fkmap(self):
-        """ TestModelTwo includes a foreignkey relation to TestModel, and the fkmap should contain that relation """
-
-        orm = self.make_coreapi()
-
-        testModelTwo = orm.TestModelTwo()
-
-        self.assertDictEqual(
-            testModelTwo.gen_fkmap(),
-            {
-                "testmodel": {
-                    "kind": "fk",
-                    "modelName": "TestModel",
-                    "reverse_fieldName": "testmodeltwos",
-                    "src_fieldName": "testmodel_id",
-                }
-            },
-        )
-
-    def test_ORMWrapper_gen_reverse_fkmap(self):
-        """ TestModel includes a reverse relation back to TestModelTwo, and the reverse_fkmap should contain that
-            relation.
-        """
-
-        orm = self.make_coreapi()
-
-        testModel = orm.TestModel()
-
-        self.assertDictEqual(
-            testModel.gen_reverse_fkmap(),
-            {
-                "testmodeltwos": {
-                    "modelName": "TestModelTwo",
-                    "src_fieldName": "testmodeltwos_ids",
-                    "writeable": False,
-                }
-            },
-        )
-
-    def test_ORMWrapper_fk_resolve(self):
-        """ If we create a TestModelTwo that has a foreign key reference to a TestModel, then calling fk_resolve should
-            return that model.
-        """
-
-        orm = self.make_coreapi()
-
-        testModel = orm.TestModel()
-        testModel.save()
-
-        testModelTwo = orm.TestModelTwo(testmodel_id=testModel.id)
-
-        testModel_resolved = testModelTwo.fk_resolve("testmodel")
-        self.assertEqual(testModel_resolved.id, testModel.id)
-
-        # the cache should have been populated
-        self.assertIn(("testmodel", testModel_resolved), testModelTwo.cache.items())
-
-    def test_ORMWrapper_reverse_fk_resolve(self):
-        """ If a TestModelTwo has a relation to TestModel, then TestModel's reverse_fk should be resolvable to a list
-            of TestModelTwo objects.
-        """
-
-        orm = self.make_coreapi()
-
-        testModel = orm.TestModel()
-        testModel.save()
-
-        testModelTwo = orm.TestModelTwo(testmodel_id=testModel.id)
-        testModelTwo.save()
-
-        # fake_stub.py doesn't populate the reverse relations for us, so force what the server would have done...
-        testModel._wrapped_class.testmodeltwos_ids = [testModelTwo.id]
-
-        testModelTwos_resolved = testModel.reverse_fk_resolve("testmodeltwos")
-        self.assertEqual(testModelTwos_resolved.count(), 1)
-
-        # the reverse_cache should have been populated
-        self.assertIn(
-            ("testmodeltwos", testModelTwos_resolved), testModel.reverse_cache.items()
-        )
-
-    def test_ORMWrapper_fk_set(self):
-        """ fk_set will set the testmodel field on TesTModelTwo to point to the TestModel. """
-
-        orm = self.make_coreapi()
-
-        testModel = orm.TestModel()
-        testModel.save()
-
-        testModelTwo = orm.TestModelTwo()
-
-        testModelTwo.fk_set("testmodel", testModel)
-
-        self.assertEqual(testModelTwo.testmodel_id, testModel.id)
-
-    def test_ORMWrapper_post_save_fixups_remove(self):
-        """ Apply a post_save_fixup that removes a reverse foreign key """
-
-        orm = self.make_coreapi()
-
-        testModel = orm.TestModel()
-        testModel.save()
-
-        testModelTwo = orm.TestModelTwo(testmodel_id=testModel.id)
-
-        # fake_stub.py doesn't populate the reverse relations for us, so force what the server would have done...
-        testModel._wrapped_class.testmodeltwos_ids = [testModelTwo.id]
-
-        post_save_fixups = [
-            {
-                "src_fieldName": "testmodel",
-                "dest_id": None,  # this field appears to not be used...
-                "dest_model": testModel,
-                "remove": True,
-                "reverse_fieldName": "testmodeltwos",
-            }
-        ]
-
-        testModelTwo.post_save_fixups = post_save_fixups
-        testModelTwo.do_post_save_fixups()
-
-        self.assertEqual(testModel._wrapped_class.testmodeltwos_ids, [])
-
-    def test_ORMWrapper_post_save_fixups_add(self):
-        """ Apply a post_save_fixup that adds a reverse foreign key """
-
-        orm = self.make_coreapi()
-
-        testModel = orm.TestModel()
-        testModel.save()
-
-        testModelTwo = orm.TestModelTwo(testmodel_id=testModel.id)
-        testModelTwo.save()
-
-        # Make sure the reverse_relation is unpopulated. This should be the case, as fake_stub.py() doesn't populate
-        # the reverse relation. But let's be sure, in case someone fixes that.
-        testModel._wrapped_class.testmodeltwos_ids = []
-
-        post_save_fixups = [
-            {
-                "src_fieldName": "testmodel",
-                "dest_id": None,  # this field appears to not be used...
-                "dest_model": testModel,
-                "remove": False,
-                "reverse_fieldName": "testmodeltwos",
-            }
-        ]
-
-        testModelTwo.post_save_fixups = post_save_fixups
-        testModelTwo.do_post_save_fixups()
-
-        self.assertEqual(testModel._wrapped_class.testmodeltwos_ids, [testModelTwo.id])
-
-    def test_ORMWrapper_tologdict(self):
-        """ Tologdict contains the model name and id, used for structured logging """
-        orm = self.make_coreapi()
-
-        testModel = orm.TestModel(intfield=7, stringfile="foo")
-
-        self.assertDictEqual(
-            testModel.tologdict(), {"model_name": "TestModel", "pk": 0}
-        )
-
-    def test_ORMWrapper_ansible_tag(self):
-        """ Ansible_tag is used by old-style synchronizers. Deprecated. """
-
-        orm = self.make_coreapi()
-
-        testModel = orm.TestModel(id=7)
-
-        self.assertEqual(testModel.ansible_tag, "TestModel_7")
-
-    def test_deleted_objects_all(self):
-        orm = self.make_coreapi()
-        orig_len_sites = len(orm.Site.objects.all())
-        orig_len_deleted_sites = len(orm.Site.deleted_objects.all())
-        site = orm.Site(name="mysite")
-        site.save()
-        site.delete()
-        sites = orm.Site.objects.all()
-        self.assertEqual(len(sites), orig_len_sites)
-        deleted_sites = orm.Site.deleted_objects.all()
-        self.assertEqual(len(deleted_sites), orig_len_deleted_sites + 1)
-
-    def test_deleted_objects_filter(self):
-        orm = self.make_coreapi()
-        with patch.object(
-            orm.grpc_stub, "FilterTestModel", wraps=orm.grpc_stub.FilterTestModel
-        ) as filter:
-            foo = orm.TestModel(name="foo")
-            foo.save()
-            foo.delete()
-
-            # There should be no live objects
-            objs = orm.TestModel.objects.filter(name="foo")
-            self.assertEqual(len(objs), 0)
-
-            # There should be one deleted object
-            deleted_objs = orm.TestModel.deleted_objects.filter(name="foo")
-            self.assertEqual(len(deleted_objs), 1)
-
-            # Two calls, one for when we checked live objects, the other for when we checked deleted objects
-            self.assertEqual(filter.call_count, 2)
-            q = filter.call_args[0][0]
-
-            # Now spy on the query that was generated, to make sure it looks like we expect
-            self.assertEqual(q.kind, q.SYNCHRONIZER_DELETED_OBJECTS)
-            self.assertEqual(len(q.elements), 1)
-            self.assertEqual(q.elements[0].operator, q.elements[0].EQUAL)
-            self.assertEqual(q.elements[0].sValue, "foo")
-
-    def test_ORMQuerySet_first_nonempty(self):
-        qs = self.ORMQuerySet([1, 2, 3])
-        self.assertEqual(qs.first(), 1)
-
-    def test_ORMQuerySet_first_empty(self):
-        qs = self.ORMQuerySet([])
-        self.assertEqual(qs.first(), None)
-
-    def test_ORMQuerySet_exists_nonempty(self):
-        qs = self.ORMQuerySet([1, 2, 3])
-        self.assertEqual(qs.exists(), True)
-
-    def test_ORMQuerySet_exists_empty(self):
-        qs = self.ORMQuerySet()
-        self.assertEqual(qs.exists(), False)
-
-    def test_ORMLocalObjectManager_nonempty(self):
-        """ Test all(), first(), exists(), and count() together since they're all closely related. Use a nonempty
-            list.
-        """
-        orm = self.make_coreapi()
-
-        t = orm.TestModel()
-        t.save()
-
-        lobjs = self.ORMLocalObjectManager(t.stub, "TestModel", [t.id], False)
-        self.assertEqual(len(lobjs.all()), 1)
-        self.assertEqual(lobjs.all()[0].id, t.id)
-        self.assertEqual(lobjs.exists(), True)
-        self.assertEqual(lobjs.count(), 1)
-        self.assertEqual(lobjs.first().id, t.id)
-
-    def test_ORMLocalObjectManager_empty(self):
-        """ Test all(), first(), exists(), and count() together since they're all closely related. Use an empty
-            list.
-        """
-        orm = self.make_coreapi()
-
-        t = orm.TestModel()
-        t.save()
-
-        lobjs = self.ORMLocalObjectManager(t.stub, "TestModel", [], False)
-        self.assertEqual(len(lobjs.all()), 0)
-        self.assertEqual(lobjs.exists(), False)
-        self.assertEqual(lobjs.count(), 0)
-        self.assertEqual(lobjs.first(), None)
-
-    def test_ORMLocalObjectManager_not_writeable(self):
-        """ An ORMLocalObjectManager that is not writeable should throw exceptions on add() and remove() """
-        orm = self.make_coreapi()
-
-        t = orm.TestModel()
-        t.save()
-
-        lobjs = self.ORMLocalObjectManager(t.stub, "TestModel", [t.id], False)
-
-        with self.assertRaises(Exception) as e:
-            lobjs.add(123)
-        self.assertEqual(e.exception.message, "Only ManyToMany lists are writeable")
-
-        with self.assertRaises(Exception) as e:
-            lobjs.remove(123)
-        self.assertEqual(e.exception.message, "Only ManyToMany lists are writeable")
-
-    def test_ORMLocalObjectManager_add(self):
-        orm = self.make_coreapi()
-
-        t = orm.TestModel()
-        t.save()
-
-        lobjs = self.ORMLocalObjectManager(t.stub, "TestModel", [], True)
-        lobjs.add(t)
-        self.assertEqual(lobjs.count(), 1)
-        self.assertEqual(lobjs.first().id, t.id)
-
-    def test_ORMLocalObjectManager_remove(self):
-        orm = self.make_coreapi()
-
-        t = orm.TestModel()
-        t.save()
-
-        lobjs = self.ORMLocalObjectManager(t.stub, "TestModel", [t.id], True)
-        lobjs.remove(t)
-        self.assertEqual(lobjs.count(), 0)
-
-
-def main():
-    global USE_FAKE_STUB
-    global xos_grpc_client
-
-    # Command-line argument of -R will cause this test to use a real grpc server
-    # rather than the fake stub.
-
-    if "-R" in sys.argv:
-        USE_FAKE_STUB = False
-        sys.argv.remove("-R")
-        # Note: will leave lots of litter (users, sites, etc) behind in the database
-
-    if USE_FAKE_STUB:
-        unittest.main()
-    else:
-        # This assumes xos-client python library is installed, and a gRPC server
-        # is available.
-
-        from xosapi import xos_grpc_client
-
-        print("Using xos-client library and core server")
-
-        def test_callback():
-            try:
-                sys.argv = sys.argv[
-                    :1
-                ]  # unittest does not like xos_grpc_client's command line arguments (TODO: find a cooperative approach)
-                unittest.main()
-            except exceptions.SystemExit as e:
-                global exitStatus
-                exitStatus = e.code
-
-        xos_grpc_client.start_api_parseargs(test_callback)
-
-        sys.exit(exitStatus)
-
-
-if __name__ == "__main__":
-    main()
diff --git a/lib/xos-api/xosapi/test_wrapper.py b/lib/xos-api/xosapi/test_wrapper.py
deleted file mode 100644
index fe4f9c1..0000000
--- a/lib/xos-api/xosapi/test_wrapper.py
+++ /dev/null
@@ -1,228 +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 __future__ import print_function
-import exceptions
-import os
-import random
-import string
-import sys
-import unittest
-
-# Command-line argument of -R will cause this test to use a real grpc server
-# rather than the fake stub.
-
-# TODO: Investigate writing wrapper unit tests using mocks rather than using the ORM test framework
-
-# by default, use fake stub rather than real core
-USE_FAKE_STUB = True
-
-PARENT_DIR = os.path.join(os.path.dirname(__file__), "..")
-
-
-class TestWrappers(unittest.TestCase):
-    def setUp(self):
-        from xosconfig import Config
-
-        test_path = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
-        config = os.path.join(test_path, "test_config.yaml")
-        Config.clear()
-        Config.init(config, "synchronizer-config-schema.yaml")
-
-        if USE_FAKE_STUB:
-            sys.path.append(PARENT_DIR)
-
-    def tearDown(self):
-        if USE_FAKE_STUB:
-            sys.path.remove(PARENT_DIR)
-
-    def make_coreapi(self):
-        if USE_FAKE_STUB:
-            import xosapi.orm
-            from fake_stub import FakeStub, FakeObj, FakeProtos
-
-            xosapi.orm.import_convenience_methods()
-
-            stub = FakeStub()
-            api = xosapi.orm.ORMStub(
-                stub=stub,
-                package_name="xos",
-                protos=FakeProtos(),
-                empty=FakeObj,
-                enable_backoff=False,
-            )
-            return api
-        else:
-            return xos_grpc_client.coreapi
-
-    def test_service_get_composable_networks(self):
-        orm = self.make_coreapi()
-        deployment = orm.Deployment(name="test_deployment")
-        deployment.save()
-        controller = orm.Controller(name="test_controller", deployment_id=deployment.id)
-        controller.save()
-        site = orm.Site(name="testsite")
-        site.save()
-        user = orm.User(
-            email="fake_"
-            + "".join(
-                random.choice(string.ascii_uppercase + string.digits) for _ in range(10)
-            ),
-            site_id=site.id,
-        )
-        user.save()
-        vsg_access_template = orm.NetworkTemplate(name="vsg_access", vtn_kind="VSG")
-        vsg_access_template.save()
-        service_one = orm.Service(name="service_one")
-        service_one.save()
-        slice_one = orm.Slice(
-            name="testsite_sliceone",
-            service_id=service_one.id,
-            site_id=site.id,
-            creator_id=user.id,
-            network="noauto",
-        )
-        slice_one.save()
-        network_one = orm.Network(
-            name="testsite_sliceone_access",
-            owner_id=slice_one.id,
-            template_id=vsg_access_template.id,
-        )
-        network_one.save()
-        ns = orm.NetworkSlice(slice_id=slice_one.id, network_id=network_one.id)
-        ns.save()
-        cn_one = orm.ControllerNetwork(
-            network_id=network_one.id, controller_id=controller.id
-        )
-        cn_one.save()
-
-        if USE_FAKE_STUB:
-            # fake_Stub doesn't handle reverse foreign keys
-            service_one.slices_ids = [slice_one.id]
-            slice_one.networks_ids = [network_one.id]
-            network_one.controllernetworks_ids = [cn_one.id]
-
-        # make sure we're using a fresh copy of the object, with all its foreign keys filled in
-        service_one = orm.Service.objects.get(id=service_one.id)
-
-        cns = service_one.get_composable_networks()
-        self.assertEqual(len(cns), 1)
-        self.assertEqual(cns[0].id, network_one.id)
-
-    def test_service_get_service_instance_class_name(self):
-        orm = self.make_coreapi()
-        deployment = orm.Deployment(name="test_deployment")
-        deployment.save()
-        controller = orm.Controller(name="test_controller", deployment_id=deployment.id)
-        controller.save()
-        site = orm.Site(name="testsite")
-        site.save()
-        user = orm.User(
-            email="fake_"
-            + "".join(
-                random.choice(string.ascii_uppercase + string.digits) for _ in range(10)
-            ),
-            site_id=site.id,
-        )
-        user.save()
-        vsg_access_template = orm.NetworkTemplate(name="vsg_access", vtn_kind="VSG")
-        vsg_access_template.save()
-        service_one = orm.Service(name="service_one")
-        service_one.save()
-
-        self.assertEqual(
-            service_one.get_service_instance_class_name(), "ServiceInstance"
-        )
-
-    def test_service_get_service_instance_class(self):
-        orm = self.make_coreapi()
-        deployment = orm.Deployment(name="test_deployment")
-        deployment.save()
-        controller = orm.Controller(name="test_controller", deployment_id=deployment.id)
-        controller.save()
-        site = orm.Site(name="testsite")
-        site.save()
-        user = orm.User(
-            email="fake_"
-            + "".join(
-                random.choice(string.ascii_uppercase + string.digits) for _ in range(10)
-            ),
-            site_id=site.id,
-        )
-        user.save()
-        vsg_access_template = orm.NetworkTemplate(name="vsg_access", vtn_kind="VSG")
-        vsg_access_template.save()
-        service_one = orm.Service(name="service_one")
-        service_one.save()
-
-        self.assertEqual(
-            service_one.get_service_instance_class().model_name, "ServiceInstance"
-        )
-
-    def test_wrapper_from__class__dot_name(self):
-        """ The Service model has a wrapper, so it should be returned when make_ORMWrapper looks for a wrapper based
-            on the class name.
-        """
-        orm = self.make_coreapi()
-        obj = orm.Service()
-        self.assertEqual(obj.__class__.__name__, "ORMWrapperService")
-
-    def test_wrapper_from_class_names(self):
-        """ ONOSService._wrapped_class.class_names is "ONOSService, Service" so we should fall back to getting the
-            Service wrapper.
-        """
-        orm = self.make_coreapi()
-        obj = orm.ONOSService()
-        self.assertEqual(obj.__class__.__name__, "ORMWrapperService")
-
-
-def main():
-    global USE_FAKE_STUB
-    global xos_grpc_client
-
-    # Command-line argument of -R will cause this test to use a real grpc server
-    # rather than the fake stub.
-
-    if "-R" in sys.argv:
-        USE_FAKE_STUB = False
-        sys.argv.remove("-R")
-        # Note: will leave lots of litter (users, sites, etc) behind in the database
-
-    if USE_FAKE_STUB:
-        unittest.main()
-    else:
-        # This assumes xos-client python library is installed, and a gRPC server
-        # is available.
-
-        from xosapi import xos_grpc_client
-
-        print("Using xos-client library and core server")
-
-        def test_callback():
-            try:
-                sys.argv = sys.argv[
-                    :1
-                ]  # unittest does not like xos_grpc_client's command line arguments (TODO: find a cooperative approach)
-                unittest.main()
-            except exceptions.SystemExit as e:
-                global exitStatus
-                exitStatus = e.code
-
-        xos_grpc_client.start_api_parseargs(test_callback)
-
-        sys.exit(exitStatus)
-
-
-if __name__ == "__main__":
-    main()
diff --git a/lib/xos-api/xosapi/version.py b/lib/xos-api/xosapi/version.py
index 2c84950..524d081 100644
--- a/lib/xos-api/xosapi/version.py
+++ b/lib/xos-api/xosapi/version.py
@@ -12,5 +12,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# This file will be replaced by setup.py
-__version__ = "unknown"
+from __future__ import absolute_import
+
+import os
+
+# read the version in from VERSION file, which is installed next to this file.
+with open(os.path.join(os.path.dirname(__file__), "VERSION")) as vf:
+    __version__ = vf.read()
diff --git a/lib/xos-api/xosapi/xos_grpc_client.py b/lib/xos-api/xosapi/xos_grpc_client.py
index a80843f..fba9ce4 100644
--- a/lib/xos-api/xosapi/xos_grpc_client.py
+++ b/lib/xos-api/xosapi/xos_grpc_client.py
@@ -12,35 +12,33 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from __future__ import print_function
+from __future__ import absolute_import, print_function
+
 import argparse
 import base64
 import functools
-import grpc
-import orm
+import inspect
 import os
 import sys
 
-from twisted.internet import reactor
 from google.protobuf.empty_pb2 import Empty
-from grpc import (
-    metadata_call_credentials,
-    composite_channel_credentials,
-    ssl_channel_credentials,
-)
 
-# fix up sys.path for chameleon
-import inspect
+import grpc
+from grpc import (composite_channel_credentials, metadata_call_credentials,
+                  ssl_channel_credentials)
 
-currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
-sys.path = [currentdir] + sys.path
-
+from twisted.internet import reactor
 from xosconfig import Config
-import chameleon_client.grpc_client as chameleon_client
+
+from xosapi import orm
+import xosapi.chameleon_client.grpc_client as chameleon_client
 
 from multistructlog import create_logger
 log = create_logger(Config().get("logging"))
 
+currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
+sys.path = [currentdir] + sys.path
+
 SERVER_CA = "/usr/local/share/ca-certificates/local_certs.crt"
 
 
@@ -97,7 +95,7 @@
                 for cm in response.convenience_methods:
                     log.debug("Saving convenience method", method=cm.filename)
                     save_path = os.path.join(convenience_methods_dir, cm.filename)
-                    file(save_path, "w").write(cm.contents)
+                    open(save_path, "w").write(cm.contents)
             else:
                 log.exception(
                     "Cannot load convenience methods, restarting the synchronzier"