SEBA-108 call acquire_service_instance on fc service

Change-Id: I1cc26b8a25eefbf6b9f49f17326b0f23608e80d6
diff --git a/Dockerfile.synchronizer b/Dockerfile.synchronizer
index 270ba1a..aea7822 100644
--- a/Dockerfile.synchronizer
+++ b/Dockerfile.synchronizer
@@ -16,7 +16,7 @@
 
 # xosproject/volt-synchronizer
 
-FROM xosproject/xos-synchronizer-base:2.1.4
+FROM xosproject/xos-synchronizer-base:2.1.8
 
 COPY xos/synchronizer /opt/xos/synchronizers/volt
 COPY VERSION /opt/xos/synchronizers/volt/
diff --git a/VERSION b/VERSION
index 50ffc5a..7ec1d6d 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.0.3
+2.1.0
diff --git a/samples/olt_device_host_and_port.yaml b/samples/olt_device_host_and_port.yaml
index db74731..c0ec47c 100644
--- a/samples/olt_device_host_and_port.yaml
+++ b/samples/olt_device_host_and_port.yaml
@@ -38,6 +38,8 @@
         switch_datapath_id: of:0000000000000001
         switch_port: "1"
         outer_tpid: "0x8100"
+        dp_id: of:0000000ce2314000
+        uplink: "129"
       requirements:
         - volt_service:
             node: service#volt
diff --git a/samples/pon_port.yaml b/samples/pon_port.yaml
index 684238e..2dfe376 100644
--- a/samples/pon_port.yaml
+++ b/samples/pon_port.yaml
@@ -36,7 +36,6 @@
       properties:
         name: test_pon_port_1
         port_no: 2
-        s_tag: 222
       requirements:
         - olt_device:
             node: device#olt
diff --git a/samples/second_pon_port.yaml b/samples/second_pon_port.yaml
new file mode 100644
index 0000000..91882e0
--- /dev/null
+++ b/samples/second_pon_port.yaml
@@ -0,0 +1,62 @@
+# 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.
+
+# curl -H "xos-username: admin@opencord.org" -H "xos-password: letmein" -X POST --data-binary @pon_port.yaml http://192.168.99.100:30007/run
+
+tosca_definitions_version: tosca_simple_yaml_1_0
+imports:
+  - custom_types/oltdevice.yaml
+  - custom_types/onudevice.yaml
+  - custom_types/ponport.yaml
+  - custom_types/voltservice.yaml
+  - custom_types/uniport.yaml
+description: Create a simulated OLT Device in VOLTHA
+topology_template:
+  node_templates:
+
+    device#olt:
+      type: tosca.nodes.OLTDevice
+      properties:
+        name: test_olt
+        must-exist: true
+
+    pon_port2:
+      type: tosca.nodes.PONPort
+      properties:
+        name: test_pon_port_2
+        port_no: 3
+      requirements:
+        - olt_device:
+            node: device#olt
+            relationship: tosca.relationships.BelongsToOne
+
+    device#onu2:
+      type: tosca.nodes.ONUDevice
+      properties:
+        serial_number: BRCM1235
+        vendor: Broadcom
+      requirements:
+        - pon_port:
+            node: pon_port2
+            relationship: tosca.relationships.BelongsToOne
+    
+    uni_port2:
+      type: tosca.nodes.UNIPort
+      properties:
+        name: test_uni_port_2
+        port_no: 3
+      requirements:
+        - onu_device:
+            node: device#onu2
+            relationship: tosca.relationships.BelongsToOne
diff --git a/samples/second_subscriber.yaml b/samples/second_subscriber.yaml
new file mode 100644
index 0000000..063e49e
--- /dev/null
+++ b/samples/second_subscriber.yaml
@@ -0,0 +1,32 @@
+# 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.
+
+# curl -H "xos-username: admin@opencord.org" -H "xos-password: letmein" -X POST --data-binary @subscriber.yaml http://192.168.99.100:30007/run
+
+tosca_definitions_version: tosca_simple_yaml_1_0
+imports:
+  - custom_types/rcordsubscriber.yaml
+description: Create a test subscriber
+topology_template:
+  node_templates:
+    # A subscriber
+    my_house2:
+      type: tosca.nodes.RCORDSubscriber
+      properties:
+        name: My Second House
+        c_tag: 112
+        s_tag: 222 
+        onu_device: BRCM1234 
+        ip_address: "1.2.3.5"
+        mac_address: "11:22:33:44:55:77"
diff --git a/samples/subscriber.yaml b/samples/subscriber.yaml
index 6f06781..ebd3a1b 100644
--- a/samples/subscriber.yaml
+++ b/samples/subscriber.yaml
@@ -28,3 +28,5 @@
         c_tag: 111
         s_tag: 222 
         onu_device: BRCM1234 
+        ip_address: "1.2.3.4"
+        mac_address: "11:22:33:44:55:66"
diff --git a/xos/synchronizer/model_policies/model_policy_voltserviceinstance.py b/xos/synchronizer/model_policies/model_policy_voltserviceinstance.py
index 227efa9..1321955 100644
--- a/xos/synchronizer/model_policies/model_policy_voltserviceinstance.py
+++ b/xos/synchronizer/model_policies/model_policy_voltserviceinstance.py
@@ -39,34 +39,17 @@
         pass
 
     def create_eastbound_instance(self, si):
-
-        chain = si.subscribed_links.all()
-
-        # Already has a chain
-        if len(chain) > 0 and not si.is_new:
-            self.logger.debug("MODEL_POLICY: VOLTServiceInstance %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 = si.owner.subscribed_dependencies.all()
-
         for link in links:
             # SEBA-216 prevent any attempt to create an ONOSServiceInstance
             if "onos" in link.provider_service.name.lower():
                 continue
 
-            si_class = link.provider_service.get_service_instance_class_name()
-            self.logger.info("MODEL_POLICY: VOLTServiceInstance %s creating %s" % (si, si_class))
+            provider_service = link.provider_service.leaf_model
 
-            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()
+            valid_provider_service_instance = provider_service.validate_links(si)
+            if not valid_provider_service_instance:
+                provider_service.acquire_service_instance(si)
 
     def associate_onu_device(self, si):
 
diff --git a/xos/synchronizer/model_policies/test_model_policy_voltserviceinstance.py b/xos/synchronizer/model_policies/test_model_policy_voltserviceinstance.py
index e6fb171..f129de5 100644
--- a/xos/synchronizer/model_policies/test_model_policy_voltserviceinstance.py
+++ b/xos/synchronizer/model_policies/test_model_policy_voltserviceinstance.py
@@ -88,9 +88,14 @@
         with patch.object(ServiceInstanceLink, "save", autospec=True) as save_link, \
             patch.object(VSGServiceInstance, "save", autospec=True) as save_vsg:
 
+            subscriber_si = Mock()
+
             link = Mock()
             link.provider_service.get_service_instance_class_name.return_value = "VSGServiceInstance"
             link.provider_service.name = "FabricCrossconnect"
+            link.provider_service.validate_links = Mock(return_value=[])
+            link.provider_service.acquire_service_instance = Mock(return_value=subscriber_si)
+            link.provider_service.leaf_model = link.provider_service
 
             si = Mock()
             si.subscribed_links.all.return_value = []
@@ -98,17 +103,30 @@
 
             self.policy.create_eastbound_instance(si)
 
-            # Should have created a vsg
+            link.provider_service.validate_links.assert_called_with(si)
+            link.provider_service.acquire_service_instance.assert_called_with(si)
 
-            self.assertEqual(save_vsg.call_count, 1)
-            vsg = save_vsg.call_args[0][0]
+    def test_create_vsg_already_exists(self):
+        with patch.object(ServiceInstanceLink, "save", autospec=True) as save_link, \
+            patch.object(VSGServiceInstance, "save", autospec=True) as save_vsg:
 
-            # Should have created a link from OLT to vsg
+            subscriber_si = Mock()
 
-            self.assertEqual(save_link.call_count, 1)
-            link = save_link.call_args[0][0]
-            self.assertEqual(link.provider_service_instance, vsg)
-            self.assertEqual(link.subscriber_service_instance, si)
+            link = Mock()
+            link.provider_service.get_service_instance_class_name.return_value = "VSGServiceInstance"
+            link.provider_service.name = "FabricCrossconnect"
+            link.provider_service.validate_links = Mock(return_value=subscriber_si)
+            link.provider_service.acquire_service_instance = Mock()
+            link.provider_service.leaf_model = link.provider_service
+
+            si = Mock()
+            si.subscribed_links.all.return_value = []
+            si.owner.subscribed_dependencies.all.return_value = [link]
+
+            self.policy.create_eastbound_instance(si)
+
+            link.provider_service.validate_links.assert_called_with(si)
+            link.provider_service.acquire_service_instance.assert_not_called()
 
     def test_associate_onu(self):
         with patch.object(ServiceInstance.objects, "get") as get_si, \