[CORD-2080] Adding 'tosca_key' support in xproto

Change-Id: Ib01c1cdcb81a51819c9e08703e6c76a4ffa09f17
diff --git a/.gitignore b/.gitignore
index 3117408..afc1bc2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@
 .noseids
 local_certs.crt
 src/tosca/tmp.yaml
+src/grpc_client/KEYS.py
 
 # Test Coverage
 .coverage
diff --git a/Makefile b/Makefile
index a6493e6..d0f6c43 100644
--- a/Makefile
+++ b/Makefile
@@ -11,7 +11,8 @@
 	nosetests -s -v --with-id --with-coverage --cover-html --cover-erase --cover-xml --cover-package="grpc_client, tosca"
 
 build:
-	docker build -t xosproject/xos-tosca --no-cache=true .
+	docker build -t xosproject/xos-tosca .
+	docker tag xosproject/xos-tosca:latest xosproject/xos-tosca:candidate
 
 start: build
 	docker run -p 9200:9200 --name xos-tosca -d xosproject/xos-tosca
@@ -27,4 +28,5 @@
 	curl -H "xos-username: xosadmin@opencord.org" -H "xos-password: rk1UYDHZXbu6KVCMkhmV" -X POST --data-binary @test/tosca/test.yaml 127.0.0.1:9102/delete
 
 tosca:
-	xosgenx --target=src/tosca/xtarget/tosca.xtarget --output=src/tosca/custom_types --write-to-file=target ../xos/xos/core/models/core.xproto
\ No newline at end of file
+	xosgenx --target=src/tosca/xtarget/tosca.xtarget --output=src/tosca/custom_types --write-to-file=target ../xos/xos/core/models/core.xproto
+	xosgenx --target=src/tosca/xtarget/tosca_keys.xtarget --output=src/grpc_client/ --write-to-file=single --dest-file=KEYS.py ../xos/xos/core/models/core.xproto
\ No newline at end of file
diff --git a/src/grpc_client/KEYS.reference.py b/src/grpc_client/KEYS.reference.py
new file mode 100644
index 0000000..fd8c776
--- /dev/null
+++ b/src/grpc_client/KEYS.reference.py
@@ -0,0 +1,75 @@
+# 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.
+
+#########################################################################
+#                                                                       #
+# This file is here for reference, the used one is generate by xos-genx #
+#                                                                       #
+#########################################################################
+
+TOSCA_KEYS = {
+    'XOSBase': [],
+    'User': ['email'],
+    'Privilege': [],
+    'AddressPool': ['name'],
+    'Controller': ['name'],
+    'ControllerImages': [],
+    'ControllerNetwork': [],
+    'ControllerRole': [],
+    'ControllerSite': [],
+    'ControllerPrivilege': [],
+    'ControllerSitePrivilege': [],
+    'ControllerSlice': [],
+    'ControllerSlicePrivilege': [],
+    'ControllerUser': [],
+    'Deployment': ['name'],
+    'DeploymentPrivilege': [],
+    'DeploymentRole': [],
+    'Diag': ['name'],
+    'Flavor': ['name'],
+    'Image': ['name'],
+    'ImageDeployments': [],
+    'Instance': ['name'],
+    'Network': ['name'],
+    'NetworkParameter': [],
+    'NetworkParameterType': ['name'],
+    'NetworkSlice': ['network', 'slice'],
+    'NetworkTemplate': ['name'],
+    'Node': ['name'],
+    'NodeLabel': ['name'],
+    'Port': [],
+    'Role': [],
+    'Service': ['name'],
+    'ServiceAttribute': ['name'],
+    'ServiceDependency': ['provider_service'],
+    'ServiceMonitoringAgentInfo': ['name'],
+    'ServicePrivilege': [],
+    'ServiceRole': [],
+    'Site': ['name'],
+    'SiteDeployment': ['site', 'deployment'],
+    'SitePrivilege': ['site', 'role'],
+    'SiteRole': [],
+    'Slice': ['name'],
+    'SlicePrivilege': [],
+    'SliceRole': [],
+    'Tag': ['name'],
+    'InterfaceType': ['name'],
+    'ServiceInterface': ['service', 'interface_type'],
+    'ServiceInstance': ['name'],
+    'ServiceInstanceLink': ['provider_service_instance'],
+    'ServiceInstanceAttribute': ['name'],
+    'TenantWithContainer': ['name'],
+    'XOS': ['name'],
+    'XOSGuiExtension': ['name'],
+}
\ No newline at end of file
diff --git a/src/grpc_client/models_accessor.py b/src/grpc_client/models_accessor.py
index b51ee90..918c04d 100644
--- a/src/grpc_client/models_accessor.py
+++ b/src/grpc_client/models_accessor.py
@@ -27,39 +27,46 @@
         Give a Model Class Name and some data, check if that exits or instantiate a new one
         """
 
-        if data.get('name'):
-            used_key = 'name'
-        else:
-            if len(data.keys()) > 0:
-                # FIXME apparently we're not matching model without a name field
-                used_key = data.keys()[0]
-            else:
-                used_key = None
+        # NOTE: we need to import this later as it's generated by main.py
+        from KEYS import TOSCA_KEYS
+
+        # get the key for this model
+        try:
+            filter_keys = TOSCA_KEYS[class_name]
+        except KeyError, e:
+            raise Exception("[XOS-TOSCA] Model %s doesn't have a tosca_key specified" % (class_name))
+
+        if len(filter_keys) == 0:
+            raise Exception("[XOS-TOSCA] Model %s doesn't have a tosca_key specified" % (class_name))
+
+        filter = {}
+        for k in filter_keys:
+            try:
+                filter[k] = data[k]
+            except KeyError, e:
+                raise Exception("[XOS-TOSCA] Model %s doesn't have a property for the specified tosca_key (%s)" % (class_name, e))
 
         key = "%s~%s" % (username, password)
         if not key in RESOURCES:
             raise Exception("[XOS-TOSCA] User '%s' does not have ready resources" % username)
         if class_name not in RESOURCES[key]:
-            raise Exception('[XOS-TOSCA] The model you are trying to create (%s: %s, class: %s) is not know by xos-core' % (used_key, data[used_key], class_name))
+            raise Exception('[XOS-TOSCA] The model you are trying to create (class: %s, properties, %s) is not know by xos-core' % (class_name, str(filter)))
 
         cls = RESOURCES[key][class_name]
-        if used_key:
-            models = cls.objects.filter(**{used_key: data[used_key]})
-        else:
-            # NOTE if we don't have a way to track the model, create a new one
-            models = []
+
+        models = cls.objects.filter(**filter)
 
         if len(models) == 1:
-            print "[XOS-Tosca] Model %s already exist, retrieving instance..." % data[used_key]
+            print "[XOS-Tosca] Model of class %s and properties %s already exist, retrieving instance..." % (class_name, str(filter))
             model = models[0]
         elif len(models) == 0:
 
             if 'must-exist' in data and data['must-exist']:
-                raise Exception("[XOS-TOSCA] Model %s:%s has property 'must-exist' but cannot be found" % (class_name, data[used_key]))
+                raise Exception("[XOS-TOSCA] Model of class %s and properties %s has property 'must-exist' but cannot be found" % (class_name, str(filter)))
 
             model = cls.objects.new()
-            print "[XOS-Tosca] Model %s is new, creating new instance..." % data[used_key] if used_key else class_name
+            print "[XOS-Tosca] Model (%s) is new, creating new instance..." % str(filter)
         else:
-            raise Exception("[XOS-Tosca] Model %s has multiple instances, I can't handle it" % data[used_key])
+            raise Exception("[XOS-Tosca] Model of class %s and properties %s has multiple instances, I can't handle it" % (class_name, str(filter)))
 
         return model
\ No newline at end of file
diff --git a/src/tosca/default.py b/src/tosca/default.py
index 6bb789c..4de0975 100644
--- a/src/tosca/default.py
+++ b/src/tosca/default.py
@@ -17,4 +17,5 @@
 import os
 
 TOSCA_DEFS_DIR = os.path.dirname(os.path.realpath(__file__)) + "/custom_types"
-TOSCA_RECIPES_DIR = os.path.dirname(os.path.realpath(__file__)) + "/"
\ No newline at end of file
+TOSCA_RECIPES_DIR = os.path.dirname(os.path.realpath(__file__)) + "/"
+TOSCA_KEYS_DIR = os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/../grpc_client")
\ No newline at end of file
diff --git a/src/tosca/generator.py b/src/tosca/generator.py
index 12e2442..6aa309c 100644
--- a/src/tosca/generator.py
+++ b/src/tosca/generator.py
@@ -15,7 +15,7 @@
 
 
 import os
-from default import TOSCA_DEFS_DIR
+from default import TOSCA_DEFS_DIR, TOSCA_KEYS_DIR
 from xosgenx.generator import XOSGenerator
 from xosapi.xos_grpc_client import Empty
 
@@ -36,7 +36,6 @@
         print "[XOS-TOSCA] Generating TOSCA"
         self._clean()
 
-
         try:
             xproto = client.utility.GetXproto(Empty())
             args = Args()
@@ -50,3 +49,17 @@
             print "[XOS-TOSCA] Failed to generate TOSCA"
             print e
 
+        try:
+            xproto = client.utility.GetXproto(Empty())
+            args = Args()
+            args.output = TOSCA_KEYS_DIR
+            args.inputs = str(xproto.xproto)
+            args.target = os.path.join(current_dir, 'xtarget/tosca_keys.xtarget')
+            args.write_to_file = 'single'
+            args.dest_file = 'KEYS.py'
+            XOSGenerator.generate(args)
+            print "[XOS-TOSCA] TOSCA Keys generated in %s" % args.output
+        except Exception as e:
+            print "[XOS-TOSCA] Failed to generate TOSCA Keys"
+            print e
+
diff --git a/src/tosca/parser.py b/src/tosca/parser.py
index acbd5f1..b30f716 100644
--- a/src/tosca/parser.py
+++ b/src/tosca/parser.py
@@ -147,6 +147,14 @@
             setattr(model, "%s_id" % class_name, related_model.id)
         return model
 
+    @staticmethod
+    def add_dependencies(data, requirements, saved_models):
+        for dep in requirements:
+            class_name = dep.keys()[0]
+            related_model = saved_models[dep[class_name]['node']]
+            data["%s_id" % class_name] = related_model.id
+        return data
+
     def __init__(self, recipe, username, password, **kwargs):
 
         self.delete = False
@@ -198,6 +206,10 @@
                             data = {}
                     # [] get model by class name
                     class_name = recipe.type.replace("tosca.nodes.", "")
+
+                    # augemnt data with relations
+                    data = self.add_dependencies(data, recipe.requirements, self.saved_model_by_name)
+
                     model = GRPCModelsAccessor.get_model_from_classname(class_name, data, self.username, self.password)
                     # [] populate model with data
                     model = self.populate_model(model, data)
diff --git a/src/tosca/xtarget/tosca_keys.xtarget b/src/tosca/xtarget/tosca_keys.xtarget
new file mode 100644
index 0000000..ad235e5
--- /dev/null
+++ b/src/tosca/xtarget/tosca_keys.xtarget
@@ -0,0 +1,5 @@
+TOSCA_KEYS = {
+{%- for m in proto.messages %}
+    '{{ m.name }}': {{ xproto_fields_to_tosca_keys(m.fields + xproto_base_fields(m, proto.message_table)) }},
+{%- endfor %}
+}
\ No newline at end of file
diff --git a/test/test_grpc_models_accessor.py b/test/test_grpc_models_accessor.py
index 3851857..729fac3 100644
--- a/test/test_grpc_models_accessor.py
+++ b/test/test_grpc_models_accessor.py
@@ -18,6 +18,7 @@
 from mock import patch, MagicMock
 from grpc_client.models_accessor import GRPCModelsAccessor
 from grpc_client.resources import RESOURCES
+from grpc_client.KEYS import TOSCA_KEYS
 
 class FakeObj:
     new = None
@@ -34,15 +35,26 @@
 
 mock_resources = {
     'username~pass': {
-        'test-model': FakeResource
+        'test-model': FakeResource,
+        'single-key': FakeResource,
+        'double-key': FakeResource
     }
 }
 
+mock_keys = {
+    'i-do-not-exists': ['name'],
+    'test-model': ['name'],
+    'empty-key': [],
+    'single-key': ['fake_key'],
+    'double-key': ['key_1', 'key_2'],
+}
+
 USERNAME = 'username'
 PASSWORD = 'pass'
 
 class GRPCModelsAccessor_Create_or_update_Test(unittest.TestCase):
 
+    @patch.dict(TOSCA_KEYS, mock_keys, clear=True)
     def test_unkown_user(self):
         """
         [GRPCModelsAccessor] get_model_from_classname: If a user does not have orm classes, raise
@@ -55,6 +67,7 @@
         self.assertEqual(e.exception.message, "[XOS-TOSCA] User 'username' does not have ready resources")
 
     @patch.dict(RESOURCES, mock_resources, clear=True)
+    @patch.dict(TOSCA_KEYS, mock_keys, clear=True)
     def test_unkown_module(self):
         """
         [GRPCModelsAccessor] get_model_from_classname: If a model is not know by the grpc api, raise
@@ -64,35 +77,92 @@
         }
         with self.assertRaises(Exception) as e:
             GRPCModelsAccessor.get_model_from_classname('i-do-not-exists', data, USERNAME, PASSWORD)
-        self.assertEqual(e.exception.message, "[XOS-TOSCA] The model you are trying to create (name: test, class: i-do-not-exists) is not know by xos-core")
+        self.assertEqual(e.exception.message, "[XOS-TOSCA] The model you are trying to create (class: i-do-not-exists, properties, {'name': 'test'}) is not know by xos-core")
+
+    def test_unkown_tosca_key(self):
+        """
+        [GRPCModelsAccessor] get_model_from_classname: If a model does not have a tosca_key, raise
+        """
+        data = {
+            "name": "test"
+        }
+        with self.assertRaises(Exception) as e:
+            GRPCModelsAccessor.get_model_from_classname('no-key', data, USERNAME, PASSWORD)
+        self.assertEqual(e.exception.message, "[XOS-TOSCA] Model no-key doesn't have a tosca_key specified")
+
+    @patch.dict(TOSCA_KEYS, mock_keys, clear=True)
+    def test_empty_tosca_key(self):
+        """
+        [GRPCModelsAccessor] get_model_from_classname: If a model does not have a tosca_key, raise
+        """
+        data = {
+            "name": "test"
+        }
+        with self.assertRaises(Exception) as e:
+            GRPCModelsAccessor.get_model_from_classname('empty-key', data, USERNAME, PASSWORD)
+        self.assertEqual(e.exception.message, "[XOS-TOSCA] Model empty-key doesn't have a tosca_key specified")
+
+    @patch.dict(TOSCA_KEYS, mock_keys, clear=True)
+    def test_tosca_key_are_defined(self):
+        """
+        [GRPCModelsAccessor] get_model_from_classname: a model should have a property for it's tosca_key
+        """
+        data = {
+            "name": "test",
+        }
+        with self.assertRaises(Exception) as e:
+            GRPCModelsAccessor.get_model_from_classname('single-key', data, USERNAME, PASSWORD)
+        self.assertEqual(e.exception.message, "[XOS-TOSCA] Model single-key doesn't have a property for the specified tosca_key ('fake_key')")
 
     @patch.object(FakeResource.objects, "filter")
     @patch.object(FakeResource.objects, "new", MagicMock(return_value=FakeModel))
+    @patch.dict(TOSCA_KEYS, mock_keys, clear=True)
+    def test_composite_key(self, mock_filter):
+        """
+        [GRPCModelsAccessor] get_model_from_classname: should use a composite key to lookup a model
+        """
+        data = {
+            "name": "test",
+            "key_1": "key1",
+            "key_2": "key2"
+        }
+        with patch.dict(RESOURCES, mock_resources, clear=True):
+            model = GRPCModelsAccessor.get_model_from_classname('double-key', data, USERNAME, PASSWORD)
+            mock_filter.assert_called_with(key_1="key1", key_2="key2")
+            self.assertEqual(model, FakeModel)
+
+    @patch.object(FakeResource.objects, "filter")
+    @patch.object(FakeResource.objects, "new", MagicMock(return_value=FakeModel))
+    @patch.dict(TOSCA_KEYS, mock_keys, clear=True)
     def test_new_model(self, mock_filter):
         """
         [GRPCModelsAccessor] get_model_from_classname: should create a new model
         """
         data = {
-            "name": "test"
+            "name": "test",
+            "fake_key": "key"
         }
         with patch.dict(RESOURCES, mock_resources, clear=True):
-            model = GRPCModelsAccessor.get_model_from_classname('test-model', data, USERNAME, PASSWORD)
-            mock_filter.assert_called_with(name="test")
+            model = GRPCModelsAccessor.get_model_from_classname('single-key', data, USERNAME, PASSWORD)
+            mock_filter.assert_called_with(fake_key="key")
             self.assertEqual(model, FakeModel)
 
     @patch.object(FakeResource.objects, "filter", MagicMock(return_value=[FakeExistingModel]))
+    @patch.dict(TOSCA_KEYS, mock_keys, clear=True)
     def test_existing_model(self):
         """
         [GRPCModelsAccessor] get_model_from_classname: should update an existing model
         """
         data = {
-            "name": "test"
+            "name": "test",
+            "fake_key": "key"
         }
         with patch.dict(RESOURCES, mock_resources, clear=True):
-            model = GRPCModelsAccessor.get_model_from_classname('test-model', data, USERNAME, PASSWORD)
+            model = GRPCModelsAccessor.get_model_from_classname('single-key', data, USERNAME, PASSWORD)
             self.assertEqual(model, FakeExistingModel)
 
     @patch.object(FakeResource.objects, "filter", MagicMock(return_value=['a', 'b']))
+    @patch.dict(TOSCA_KEYS, mock_keys, clear=True)
     def test_multiple_models(self):
         """
         [GRPCModelsAccessor] get_model_from_classname: should raise an exception if multiple instances are found
@@ -103,33 +173,7 @@
         with patch.dict(RESOURCES, mock_resources, clear=True):
             with self.assertRaises(Exception) as e:
                 GRPCModelsAccessor.get_model_from_classname('test-model', data, USERNAME, PASSWORD)
-            self.assertEqual(e.exception.message, "[XOS-Tosca] Model test has multiple instances, I can't handle it")
-
-    @patch.dict(RESOURCES, mock_resources, clear=True)
-    @patch.object(FakeResource.objects, "filter")
-    @patch.object(FakeResource.objects, "new")
-    def test_find_model_without_name_property(self, mock_new, mock_filter):
-        """
-        [GRPCModelsAccessor] get_model_from_classname: should lookup a model by the first property
-        """
-        data = {
-            'foo': 'bar',
-            'something': 'else'
-        }
-        GRPCModelsAccessor.get_model_from_classname('test-model', data, USERNAME, PASSWORD)
-        mock_filter.assert_called_with(foo="bar")
-        mock_new.assert_called()
-
-    @patch.dict(RESOURCES, mock_resources, clear=True)
-    @patch.object(FakeResource.objects, "new")
-    def test_model_without_properties(self, mock_new):
-        """
-        [GRPCModelsAccessor] get_model_from_classname: should create a new model if not properties are specified
-        """
-        data = {
-        }
-        GRPCModelsAccessor.get_model_from_classname('test-model', data, USERNAME, PASSWORD)
-        mock_new.assert_called()
+            self.assertEqual(e.exception.message, "[XOS-Tosca] Model of class test-model and properties {'name': 'test'} has multiple instances, I can't handle it")
 
 if __name__ == '__main__':
     unittest.main()
\ No newline at end of file
diff --git a/test/test_tosca_parser_e2e.py b/test/test_tosca_parser_e2e.py
index a4c8e29..87f3cf1 100644
--- a/test/test_tosca_parser_e2e.py
+++ b/test/test_tosca_parser_e2e.py
@@ -220,4 +220,4 @@
         with self.assertRaises(Exception) as e:
             parser.execute()
 
-        self.assertEqual(e.exception.message.message, "[XOS-TOSCA] Model Site:Open Networking Lab has property 'must-exist' but cannot be found")
+        self.assertEqual(e.exception.message.message, "[XOS-TOSCA] Model of class Site and properties {'name': 'Open Networking Lab'} has property 'must-exist' but cannot be found")
diff --git a/test/tosca/link.yaml b/test/tosca/link.yaml
new file mode 100644
index 0000000..f2c000f
--- /dev/null
+++ b/test/tosca/link.yaml
@@ -0,0 +1,160 @@
+
+# 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.
+
+
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Persist xos-sample-gui-extension
+
+imports:
+  - custom_types/slice.yaml
+  - custom_types/network.yaml
+  - custom_types/networkslice.yaml
+  - custom_types/networktemplate.yaml
+  - custom_types/site.yaml
+  - custom_types/image.yaml
+  - custom_types/service.yaml
+  - custom_types/serviceinstance.yaml
+  - custom_types/serviceinstancelink.yaml
+
+topology_template:
+  node_templates:
+
+    service#mcord:
+      type: tosca.nodes.Service
+      properties:
+        name: mcord
+
+    test1:
+      type: tosca.nodes.ServiceInstance
+      properties:
+        name: test1
+
+    test2:
+      type: tosca.nodes.ServiceInstance
+      properties:
+        name: test2
+
+    link1:
+      type: tosca.nodes.ServiceInstanceLink
+      requirements:
+        - provider_service_instance:
+            node: test1
+            relationship: tosca.relationships.BelongsToOne
+        - subscriber_service_instance:
+            node: test2
+            relationship: tosca.relationships.BelongsToOne
+
+    link2:
+      type: tosca.nodes.ServiceInstanceLink
+      requirements:
+        - subscriber_service:
+            node: service#mcord
+            relationship: tosca.relationships.BelongsToOne
+        - provider_service_instance:
+            node: test2
+            relationship: tosca.relationships.BelongsToOne
+
+    # Site
+    mysite:
+      type: tosca.nodes.Site
+      properties:
+        must-exist: true
+        name: mysite
+
+    # Images
+    image#trusty-server-multi-nic:
+      type: tosca.nodes.Image
+      properties:
+        must-exist: true
+        name: trusty-server-multi-nic
+
+    # slices
+    slice#slice1:
+      type: tosca.nodes.Slice
+      properties:
+        name: mysite_slice1
+      requirements:
+        - site:
+            node: mysite
+            relationship: tosca.relationships.BelongsToOne
+        - default_image:
+            node: image#trusty-server-multi-nic
+            relationship: tosca.relationships.BelongsToOne
+
+    slice#slice2:
+      type: tosca.nodes.Slice
+      properties:
+        name: mysite_slice2
+      requirements:
+        - site:
+            node: mysite
+            relationship: tosca.relationships.BelongsToOne
+        - default_image:
+            node: image#trusty-server-multi-nic
+            relationship: tosca.relationships.BelongsToOne
+
+    # networks
+
+    shared_template:
+      type: tosca.nodes.NetworkTemplate
+      properties:
+        must-exist: true
+        name: shared_template
+
+    network#network1:
+      type: tosca.nodes.Network
+      properties:
+        name: network1
+      requirements:
+        - template:
+            node: shared_template
+            relationship: tosca.relationships.BelongsToOne
+        - owner:
+            node: slice#slice1
+            relationship: tosca.relationships.BelongsToOne
+
+    network#network2:
+      type: tosca.nodes.Network
+      properties:
+        name: network2
+      requirements:
+        - template:
+            node: shared_template
+            relationship: tosca.relationships.BelongsToOne
+        - owner:
+            node: slice#slice2
+            relationship: tosca.relationships.BelongsToOne
+
+    networkslice#slice1_network2:
+      type: tosca.nodes.NetworkSlice
+      requirements:
+        - network:
+            node: network#network2
+            relationship: tosca.relationships.BelongsToOne
+        - slice:
+            node: slice#slice1
+            relationship: tosca.relationships.BelongsToOne
+
+    networkslice#slice2_network1:
+      type: tosca.nodes.NetworkSlice
+      requirements:
+        - network:
+            node: network#network1
+            relationship: tosca.relationships.BelongsToOne
+        - slice:
+            node: slice#slice2
+            relationship: tosca.relationships.BelongsToOne
+