[CORD-3084] Attaching a subscriber to an ONU

Change-Id: Id73c2ba3238b51615e541139b0e7ad0de30a0ba5
diff --git a/samples/olt_device.yaml b/samples/olt_device.yaml
index c8a1485..c83b65c 100644
--- a/samples/olt_device.yaml
+++ b/samples/olt_device.yaml
@@ -17,6 +17,7 @@
 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
 description: Create a simulated OLT Device in VOLTHA
@@ -36,6 +37,9 @@
         device_type: simulated_olt
         host: 172.17.0.1
         port: 50060
+        switch_datapath_id: of:0000000000000001
+        switch_port: "1"
+        outer_tpid: "0x8100"
       requirements:
         - volt_service:
             node: service#volt
@@ -51,3 +55,14 @@
         - olt_device:
             node: device#olt
             relationship: tosca.relationships.BelongsToOne
+
+    onu:
+      type: tosca.nodes.ONUDevice
+      properties:
+        serial_number: BRCM1234
+        vendor: Broadcom
+        device_type: broadcom_onu
+      requirements:
+        - pon_port:
+            node: pon_port
+            relationship: tosca.relationships.BelongsToOne
diff --git a/samples/subscriber.yaml b/samples/subscriber.yaml
index ba79a16..fe8d646 100644
--- a/samples/subscriber.yaml
+++ b/samples/subscriber.yaml
@@ -16,48 +16,13 @@
 
 tosca_definitions_version: tosca_simple_yaml_1_0
 imports:
-  - custom_types/cordsubscriberroot.yaml
-  - custom_types/ponport.yaml
-  - custom_types/voltservice.yaml
-  - custom_types/oltdevice.yaml
+  - custom_types/rcordsubscriber.yaml
 description: Create a test subscriber
 topology_template:
   node_templates:
-    service#volt:
-      type: tosca.nodes.VOLTService
-      properties:
-        name: volt
-        must-exist: true
-
-    # Setup an OLT Device
-    olt_device:
-      type: tosca.nodes.OLTDevice
-      properties:
-        name: volt-1
-        device_type: simulated_olt
-        host: 172.17.0.1
-        port: 50060
-        switch_datapath_id: of:0000000ce2314000
-        switch_port: "5"
-        outer_tpid: "0x8100"
-      requirements:
-        - volt_service:
-            node: service#volt
-            relationship: tosca.relationships.BelongsToOne
-
-    pon_port:
-      type: tosca.nodes.PONPort
-      properties:
-        name: volt-port-1
-        s_tag: 222
-      requirements:
-        - olt_device:
-            node: olt_device
-            relationship: tosca.relationships.BelongsToOne
-
     # A subscriber
     my_house:
-      type: tosca.nodes.CordSubscriberRoot
+      type: tosca.nodes.RCORDSubscriber
       properties:
         name: My House
         service_specific_id: "123"
@@ -66,8 +31,7 @@
         url_filter_enable: false
         url_filter_level: R
         c_tag: 111
-        olt_device: volt-1
-        olt_port: volt-port-1
+        onu_device: BRCM1234
         uni_port_id: 101
         mac_address: 00:AA:00:00:00:01
         ip_address: 10.8.2.1
diff --git a/samples/volt_service.yaml b/samples/volt_service.yaml
index 64569e4..c12525e 100644
--- a/samples/volt_service.yaml
+++ b/samples/volt_service.yaml
@@ -25,7 +25,9 @@
       properties:
         name: volt
         must-exist: true
-        voltha_url: 10.128.22.3:8882
-        p_onos_url: 10.128.22.3:8181
-        p_onos_user: karaf
-        p_onos_pass: karaf
+        voltha_url: 10.128.22.3
+        voltha_port: 8882
+        onos_voltha_url: 10.128.22.3
+        onos_voltha_port: 8181
+        onos_voltha_user: karaf
+        onos_voltha_pass: karaf
diff --git a/xos/synchronizer/models/convenience/voltserviceinstance.py b/xos/synchronizer/models/convenience/voltserviceinstance.py
index 48f52dc..fdae09e 100644
--- a/xos/synchronizer/models/convenience/voltserviceinstance.py
+++ b/xos/synchronizer/models/convenience/voltserviceinstance.py
@@ -17,10 +17,7 @@
 from xosapi.orm import ORMWrapper, register_convenience_wrapper
 from xosapi.convenience.serviceinstance import ORMWrapperServiceInstance
 
-from xosconfig import Config
-from multistructlog import create_logger
-
-log = create_logger(Config().get('logging'))
+import logging as log
 
 class ORMWrapperVOLTServiceInstance(ORMWrapperServiceInstance):
 
@@ -64,32 +61,25 @@
         return self.subscriber.c_tag
 
     def get_olt_device_by_subscriber(self):
+        pon_port = self.get_pon_port_by_subscriber()
+        return pon_port.olt_device
+
+    def get_pon_port_by_subscriber(self):
         si = self.stub.ServiceInstance.objects.get(id=self.id)
-
-        olt_device_name = si.get_westbound_service_instance_properties("olt_device")
-
-        olt_device = self.stub.OLTDevice.objects.get(name=olt_device_name)
-        return olt_device
-
-    def get_olt_port_by_subscriber(self):
-        si = self.stub.ServiceInstance.objects.get(id=self.id)
-
-        olt_port_name = si.get_westbound_service_instance_properties("olt_port")
-
-        olt_device = self.get_olt_device_by_subscriber()
-        olt_port = self.stub.PONPort.objects.get(name=olt_port_name, olt_device_id=olt_device.id)
-        return olt_port
+        onu_sn = si.get_westbound_service_instance_properties("onu_device")
+        onu = self.stub.ONUDevice.objects.get(serial_number=onu_sn)
+        return onu.pon_port
 
     @property
     def s_tag(self):
         try:
-            olt_port = self.get_olt_port_by_subscriber()
+            pon_port = self.get_pon_port_by_subscriber()
 
-            if olt_port:
-                return olt_port.s_tag
+            if pon_port:
+                return pon_port.s_tag
             return None
         except Exception, e:
-            log.warning('Error while reading s_tag: %s' % e.message)
+            log.exception('Error while reading s_tag: %s' % e.message)
             return None
 
     @property
@@ -100,7 +90,7 @@
                 return olt_device.switch_datapath_id
             return None
         except Exception, e:
-            log.warning('Error while reading switch_datapath_id: %s' % e.message)
+            log.exception('Error while reading switch_datapath_id: %s' % e.message)
             return None
 
     @property
@@ -111,7 +101,7 @@
                 return olt_device.switch_port
             return None
         except Exception, e:
-            log.warning('Error while reading switch_port: %s' % e.message)
+            log.exception('Error while reading switch_port: %s' % e.message)
             return None
 
     @property
@@ -122,7 +112,7 @@
                 return olt_device.outer_tpid
             return None
         except Exception, e:
-            log.warning('Error while reading outer_tpid: %s' % e.message)
+            log.exception('Error while reading outer_tpid: %s' % e.message)
             return None
 
 
diff --git a/xos/synchronizer/models/models.py b/xos/synchronizer/models/models.py
new file mode 100644
index 0000000..eeb5e74
--- /dev/null
+++ b/xos/synchronizer/models/models.py
@@ -0,0 +1,54 @@
+# 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 core.models.xosbase import *
+
+from models_decl import VOLTService_decl
+from models_decl import VOLTServiceInstance_decl
+from models_decl import OLTDevice_decl
+from models_decl import PONPort_decl
+from models_decl import ONUDevice_decl
+
+class VOLTService(VOLTService_decl):
+    class Meta:
+        proxy = True
+
+    @staticmethod
+    def has_access_device(serial_number):
+        try:
+            ONUDevice.objects.get(serial_number=serial_number)
+            return True
+        except IndexError, e:
+            return False
+
+
+class VOLTServiceInstance(VOLTServiceInstance_decl):
+    class Meta:
+        proxy = True 
+
+
+class OLTDevice(OLTDevice_decl):
+    class Meta:
+        proxy = True 
+
+
+class PONPort(PONPort_decl):
+    class Meta:
+        proxy = True 
+
+
+class ONUDevice(ONUDevice_decl):
+    class Meta:
+        proxy = True 
+
diff --git a/xos/synchronizer/models/volt.xproto b/xos/synchronizer/models/volt.xproto
index a73b8ed..355ddaf 100644
--- a/xos/synchronizer/models/volt.xproto
+++ b/xos/synchronizer/models/volt.xproto
@@ -1,5 +1,6 @@
 option name = "volt";
 option app_label = "volt";
+option legacy="True";
 
 message VOLTService (Service){
     option verbose_name = "vOLT Service";
@@ -48,21 +49,6 @@
     optional string outer_tpid = 19 [help_text = "Outer VLAN id field EtherType", null = False, db_index = False, blank = False];
 }
 
-message ONUDevice (XOSBase){
-    option verbose_name = "ONU Device";
-    option description = "Represents a physical ONU device";
-
-    required manytoone olt_device->OLTDevice:onu_devices = 1 [db_index = True, null = False, blank = False];
-    required string serial_number = 2 [max_length = 254, null = False, db_index = False, blank = False, tosca_key=True];
-    required string vendor = 3 [max_length = 254, null = False, db_index = False, blank = False];
-    required string device_id = 4 [max_length = 254, null = False, db_index = False, blank = False];
-
-    required string device_type = 5 [help_text = "Device Type", default = "asfvolt16_olt", max_length = 254, null = False, db_index = False, blank = False];
-    optional string admin_state = 6 [help_text = "admin_state", null = True, db_index = False, blank = False, feedback_state = True];
-    optional string oper_status = 7 [help_text = "oper_status", null = True, db_index = False, blank = False, feedback_state = True];
-    optional string connect_status = 8 [help_text = "connect_status", null = True, db_index = False, blank = False, feedback_state = True];
-}
-
 message PONPort (XOSBase){
     option verbose_name = "PON Port";
 
@@ -71,3 +57,18 @@
     required string port_id = 3 [help_text = "Port ID", max_length = 254, null = False, db_index = False, blank = False];
     required int32 s_tag = 4 [help_text = "S Tag", null = False, db_index = False, blank = False];
 }
+
+message ONUDevice (XOSBase){
+    option verbose_name = "ONU Device";
+    option description = "Represents a physical ONU device";
+
+    required manytoone pon_port->PONPort:onu_devices = 1 [db_index = True, null = False, blank = False];
+    required string serial_number = 2 [max_length = 254, null = False, db_index = False, blank = False, tosca_key=True, unique = True];
+    required string vendor = 3 [max_length = 254, null = False, db_index = False, blank = False];
+    required string device_type = 4 [help_text = "Device Type", default = "asfvolt16_olt", max_length = 254, null = False, db_index = False, blank = False];
+
+    optional string device_id = 5 [max_length = 254, null = True, db_index = False, blank = False, feedback_state = True];
+    optional string admin_state = 6 [help_text = "admin_state", null = True, db_index = False, blank = False, feedback_state = True];
+    optional string oper_status = 7 [help_text = "oper_status", null = True, db_index = False, blank = False, feedback_state = True];
+    optional string connect_status = 8 [help_text = "connect_status", null = True, db_index = False, blank = False, feedback_state = True];
+}
\ No newline at end of file
diff --git a/xos/synchronizer/steps/sync_volt_service_instance.py b/xos/synchronizer/steps/sync_volt_service_instance.py
index 6703d82..4d1f90f 100644
--- a/xos/synchronizer/steps/sync_volt_service_instance.py
+++ b/xos/synchronizer/steps/sync_volt_service_instance.py
@@ -20,7 +20,7 @@
 import requests
 from multistructlog import create_logger
 from requests.auth import HTTPBasicAuth
-from synchronizers.new_base.modelaccessor import VOLTService, VOLTServiceInstance, ServiceInstance, OLTDevice, model_accessor
+from synchronizers.new_base.modelaccessor import VOLTService, VOLTServiceInstance, ServiceInstance, ONUDevice, model_accessor
 from synchronizers.new_base.syncstep import SyncStep, DeferredException
 from xosconfig import Config
 
@@ -42,9 +42,11 @@
         c_tag = si.get_westbound_service_instance_properties("c_tag")
         uni_port_id = si.get_westbound_service_instance_properties("uni_port_id")
 
-        olt_device_name = si.get_westbound_service_instance_properties("olt_device")
+        onu_device_name = si.get_westbound_service_instance_properties("onu_device")
 
-        olt_device = OLTDevice.objects.get(name=olt_device_name)
+        onu_device = ONUDevice.objects.get(serial_number=onu_device_name)
+
+        olt_device = onu_device.pon_port.olt_device
 
         if not olt_device.dp_id:
             raise DeferredException("Waiting for OLTDevice %s to be synchronized" % olt_device.name)
diff --git a/xos/synchronizer/steps/test_sync_volt_service_instance.py b/xos/synchronizer/steps/test_sync_volt_service_instance.py
index cacf765..da77395 100644
--- a/xos/synchronizer/steps/test_sync_volt_service_instance.py
+++ b/xos/synchronizer/steps/test_sync_volt_service_instance.py
@@ -91,12 +91,14 @@
         si = Mock()
         si.get_westbound_service_instance_properties = mock_get_westbound_service_instance_properties
 
-        olt_device = Mock()
-        olt_device.name = "Test OLT Device"
+        onu_device = Mock()
+        onu_device.name = "BRCM1234"
+        onu_device.pon_port.olt_device.dp_id = None
+        onu_device.pon_port.olt_device.name = "Test OLT Device"
 
         self.o = o
         self.si = si
-        self.olt_device = olt_device
+        self.onu_device = onu_device
         self.volt_service = volt_service
 
     def tearDown(self):
@@ -105,13 +107,13 @@
 
     @requests_mock.Mocker()
     def test_do_not_sync(self, m):
-        self.olt_device.dp_id = None
+        self.onu_device.pon_port.olt_device.dp_id = None
 
         with patch.object(ServiceInstance.objects, "get") as service_instance_mock, \
-                patch.object(OLTDevice.objects, "get") as olt_device_mock, \
+                patch.object(ONUDevice.objects, "get") as onu_device_mock, \
                 patch.object(VOLTService.objects, "get") as olt_service_mock:
             service_instance_mock.return_value = self.si
-            olt_device_mock.return_value = self.olt_device
+            onu_device_mock.return_value = self.onu_device
             olt_service_mock.return_value = self.volt_service
 
             with self.assertRaises(DeferredException) as e:
@@ -124,13 +126,13 @@
     def test_do_sync(self, m):
         m.post("http://onos_voltha_url:4321/onos/olt/oltapp/of:dp_id/uni_port_id/c_tag", status_code=200, json={})
 
-        self.olt_device.dp_id = "of:dp_id"
+        self.onu_device.pon_port.olt_device.dp_id = "of:dp_id"
 
         with patch.object(ServiceInstance.objects, "get") as service_instance_mock, \
-                patch.object(OLTDevice.objects, "get") as olt_device_mock, \
+                patch.object(ONUDevice.objects, "get") as onu_device_mock, \
                 patch.object(VOLTService.objects, "get") as olt_service_mock:
             service_instance_mock.return_value = self.si
-            olt_device_mock.return_value = self.olt_device
+            onu_device_mock.return_value = self.onu_device
             olt_service_mock.return_value = self.volt_service
 
             self.sync_step().sync_record(self.o)
@@ -140,13 +142,13 @@
     def test_do_sync_fail(self, m):
         m.post("http://onos_voltha_url:4321/onos/olt/oltapp/of:dp_id/uni_port_id/c_tag", status_code=500, text="Mock Error")
 
-        self.olt_device.dp_id = "of:dp_id"
+        self.onu_device.pon_port.olt_device.dp_id = "of:dp_id"
 
         with patch.object(ServiceInstance.objects, "get") as service_instance_mock, \
-                patch.object(OLTDevice.objects, "get") as olt_device_mock, \
+                patch.object(ONUDevice.objects, "get") as onu_device_mock, \
                 patch.object(VOLTService.objects, "get") as olt_service_mock:
             service_instance_mock.return_value = self.si
-            olt_device_mock.return_value = self.olt_device
+            onu_device_mock.return_value = self.onu_device
             olt_service_mock.return_value = self.volt_service
 
             with self.assertRaises(Exception) as e: