CORD-1736 fix get_composable_networks not returning networks

Change-Id: I4c9d8d44a9eae8ab04a49c2549e8bc1742a5923b
diff --git a/xos/xos_client/xosapi/convenience/service.py b/xos/xos_client/xosapi/convenience/service.py
index 380eb59..28cec65 100644
--- a/xos/xos_client/xosapi/convenience/service.py
+++ b/xos/xos_client/xosapi/convenience/service.py
@@ -31,7 +31,7 @@
         nets = []
         for slice in self.slices.all():
             for net in slice.networks.all():
-                if (net.template.vtn_kind not in SUPPORTED_VTN_SERVCOMP_KINDS) or (net.owner != slice):
+                if (net.template.vtn_kind not in SUPPORTED_VTN_SERVCOMP_KINDS) or (net.owner.id != slice.id):
                     continue
 
                 if not net.controllernetworks.exists():
diff --git a/xos/xos_client/xosapi/fake_stub.py b/xos/xos_client/xosapi/fake_stub.py
index 7e57f5e..cc112e8 100644
--- a/xos/xos_client/xosapi/fake_stub.py
+++ b/xos/xos_client/xosapi/fake_stub.py
@@ -116,6 +116,27 @@
 
         return fbn
 
+class Controller(FakeObj):
+    FIELDS = ( {"name": "id", "default": 0},
+               {"name": "name", "default": ""},
+               {"name": "deployment_id", "default": 0, "fk_model": "Deployment"}
+             )
+
+    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": ""},
+             )
+
+    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": ""},
@@ -130,7 +151,10 @@
     FIELDS = ( {"name": "id", "default": 0},
                {"name": "name", "default": ""},
                {"name": "site_id", "default": 0, "fk_model": "Site"},
+               {"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"} )
 
     def __init__(self, **kwargs):
@@ -141,7 +165,7 @@
 class Site(FakeObj):
     FIELDS = ( {"name": "id", "default": 0},
                {"name": "name", "default": ""},
-               {"name": "slice_ids", "default": 0, "fk_reverse": "Slice"},
+               {"name": "slice_ids", "default": [], "fk_reverse": "Slice"},
                {"name": "leaf_model_name", "default": "Site"})
 
     def __init__(self, **kwargs):
@@ -152,6 +176,7 @@
 class Service(FakeObj):
     FIELDS = ( {"name": "id", "default": 0},
                {"name": "name", "default": ""},
+               {"name": "slices_ids", "default": [], "fk_reverse": "Slice"},
                {"name": "leaf_model_name", "default": "Service"})
 
     def __init__(self, **kwargs):
@@ -171,6 +196,52 @@
 
     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"})
+
+    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"})
+
+    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"})
+
+    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": "ControllerNetwork"})
+
+    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},
@@ -178,7 +249,6 @@
                {"name": "value", "default": ""},
                {"name": "content_type", "default": None},
                {"name": "object_id", "default": None},
-               {"name": "slice_ids", "default": 0, "fk_reverse": "Slice"},
                {"name": "leaf_model_name", "default": "Tag"})
 
     def __init__(self, **kwargs):
@@ -197,7 +267,7 @@
     def __init__(self):
         self.id_counter = 1
         self.objs = {}
-        for name in ["Slice", "Site", "Tag", "Service", "ONOSService", "User"]:
+        for name in ["Controller", "Deployment", "Slice", "Site", "Tag", "Service", "ONOSService", "User", "Network", "NetworkTemplate", "ControllerNetwork", "NetworkSlice"]:
             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))
@@ -247,7 +317,7 @@
 class FakeSymDb(object):
     def __init__(self):
         self._classes = {}
-        for name in ["Slice", "Site", "ID", "Tag", "Service", "ONOSService", "User"]:
+        for name in ["Controller", "Deployment", "Slice", "Site", "ID", "Tag", "Service", "ONOSService", "User", "Network", "NetworkTemplate", "ControllerNetwork", "NetworkSlice"]:
             self._classes["xos.%s" % name] = globals()[name]
 
 
diff --git a/xos/xos_client/xosapi/wrapper_test.py b/xos/xos_client/xosapi/wrapper_test.py
new file mode 100644
index 0000000..0b7703a
--- /dev/null
+++ b/xos/xos_client/xosapi/wrapper_test.py
@@ -0,0 +1,110 @@
+
+# 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 exceptions
+import random
+import shutil
+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
+
+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
+else:
+    USE_FAKE_STUB = True
+
+class TestWrappers(unittest.TestCase):
+    def make_coreapi(self):
+        if USE_FAKE_STUB:
+            stub = FakeStub()
+            api = xosapi.orm.ORMStub(stub=stub, package_name = "xos", sym_db = FakeSymDb(), 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)
+
+if USE_FAKE_STUB:
+    sys.path.append("..")
+
+    import xosapi.orm
+    from fake_stub import FakeStub, FakeSymDb, FakeObj
+
+    print "Using Fake Stub"
+
+    unittest.main()
+else:
+    # This assumes xos-client python library is installed, and a gRPC server
+    # is available.
+
+    from twisted.internet import reactor
+    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, e:
+            global exitStatus
+            exitStatus = e.code
+
+    xos_grpc_client.start_api_parseargs(test_callback)
+
+    sys.exit(exitStatus)
+