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)
+