Prevent synchronizer to push OLTs to VOLTHA if they have feedback state
Moved the s_tag in the PONPort Model

Change-Id: I8e6b07ff925f0df7772a66425e9ddaa944cfe9fb
diff --git a/samples/olt_device.yaml b/samples/olt_device.yaml
index e5e898c..c8a1485 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/ponport.yaml
   - custom_types/voltservice.yaml
 description: Create a simulated OLT Device in VOLTHA
 topology_template:
@@ -39,3 +40,14 @@
         - volt_service:
             node: service#volt
             relationship: tosca.relationships.BelongsToOne
+
+    pon_port:
+      type: tosca.nodes.PONPort
+      properties:
+        name: test_olt_port_1
+        port_id: ff00ff
+        s_tag: 111
+      requirements:
+        - olt_device:
+            node: device#olt
+            relationship: tosca.relationships.BelongsToOne
diff --git a/xos/synchronizer/models/volt.xproto b/xos/synchronizer/models/volt.xproto
index a58b696..b66bddc 100644
--- a/xos/synchronizer/models/volt.xproto
+++ b/xos/synchronizer/models/volt.xproto
@@ -39,7 +39,6 @@
     optional string of_id = 13 [help_text = "openflow id", null = True, db_index = False, blank = False, feedback_state = True];
 
     required string uplink = 14 [default="129", help_text = "uplink port", null = False, db_index = False, blank = False];
-    required string vlan = 15 [default="3", help_text = "same as S-Tag", null = True, db_index = False, blank = False];
     required string driver = 16 [default="pmc-olt", help_text = "Olt driver", null = True, db_index = False, blank = False];
 }
 
@@ -55,7 +54,7 @@
     option verbose_name = "PON Port";
 
     required string name = 1 [db_index = True, null = False, blank = False];
-    required manytoone volt_device->OLTDevice:ports = 2 [db_index = True, null = False, blank = False];
-    required string port_id = 3 [help_text = "Port ID (Feedback State)", max_length = 254, null = False, db_index = False, blank = False];
+    required manytoone olt_device->OLTDevice:ports = 2 [db_index = True, null = False, blank = False];
+    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];
 }
diff --git a/xos/synchronizer/steps/sync_olt_device.py b/xos/synchronizer/steps/sync_olt_device.py
index 591f7f9..74da3b0 100644
--- a/xos/synchronizer/steps/sync_olt_device.py
+++ b/xos/synchronizer/steps/sync_olt_device.py
@@ -72,56 +72,66 @@
     def sync_record(self, o):
         log.info("sync'ing device", object=str(o), **o.tologdict())
 
-        voltha_url = self.get_voltha_info(o)['url']
+        # If the device has feedback_state is already present in voltha
+        if not o.device_id and not o.admin_state and not o.oper_status and not o.of_id:
+            log.info("Pushing device to VOLTHA", object=str(o), **o.tologdict())
+            voltha_url = self.get_voltha_info(o)['url']
 
-        data = {
-            "type": o.device_type,
-            "host_and_port": "%s:%s" % (o.host, o.port)
-        }
+            data = {
+                "type": o.device_type,
+                "host_and_port": "%s:%s" % (o.host, o.port)
+            }
 
-        if o.device_type == 'simulated_olt':
-            # simulated devices won't accept host and port, for testing only
-            data.pop('host_and_port')
-            data['mac_address'] = "00:0c:e2:31:40:00"
+            if o.device_type == 'simulated_olt':
+                # simulated devices won't accept host and port, for testing only
+                data.pop('host_and_port')
+                data['mac_address'] = "00:0c:e2:31:40:00"
 
-        log.info("pushing olt to voltha", data=data)
+            log.info("pushing olt to voltha", data=data)
 
-        r = requests.post(voltha_url + "/api/v1/devices", json=data)
+            r = requests.post(voltha_url + "/api/v1/devices", json=data)
 
-        if r.status_code != 200:
-            raise Exception("Failed to add device: %s" % r.text)
+            if r.status_code != 200:
+                raise Exception("Failed to add device: %s" % r.text)
 
-        log.info("add device response", text=r.text)
+            log.info("add device response", text=r.text)
 
-        res = r.json()
+            res = r.json()
 
-        print log.info("add device json res", res=res)
+            log.info("add device json res", res=res)
 
-        if not res['id']:
-            raise Exception('VOLTHA Device Id is empty, this probably means that the device is already provisioned in VOLTHA')
-        else:
-            o.device_id = res['id'];
+            if not res['id']:
+                raise Exception('VOLTHA Device Id is empty, this probably means that the device is already provisioned in VOLTHA')
+            else:
+                o.device_id = res['id'];
 
-        # enable device
+            # enable device
 
-        r = requests.post(voltha_url + "/api/v1/devices/" + o.device_id + "/enable")
+            r = requests.post(voltha_url + "/api/v1/devices/" + o.device_id + "/enable")
 
-        if r.status_code != 200:
-            raise Exception("Failed to enable device: %s" % r.text)
+            if r.status_code != 200:
+                raise Exception("Failed to enable device: %s" % r.text)
 
-        # read state
-        r = requests.get(voltha_url + "/api/v1/devices/" + o.device_id).json()
-        while r['oper_status'] == "ACTIVATING":
-            log.info("Waiting for device %s (%s) to activate" % (o.name, o.device_id))
-            sleep(5)
+            # read state
             r = requests.get(voltha_url + "/api/v1/devices/" + o.device_id).json()
+            while r['oper_status'] == "ACTIVATING":
+                log.info("Waiting for device %s (%s) to activate" % (o.name, o.device_id))
+                sleep(5)
+                r = requests.get(voltha_url + "/api/v1/devices/" + o.device_id).json()
 
-        o.admin_state = r['admin_state']
-        o.oper_status = r['oper_status']
+            o.admin_state = r['admin_state']
+            o.oper_status = r['oper_status']
 
-        # find of_id of device
-        o.of_id = self.get_of_id_from_device(o)
-        o.save()
+            # find of_id of device
+            o.of_id = self.get_of_id_from_device(o)
+            o.save()
+        else:
+            log.info("Device already exists in VOLTHA", object=str(o), **o.tologdict())
+
+
+        # NOTE do we need to move this synchronization in a PON_PORT specific step?
+        # for now we assume that each OLT has only one Port
+        vlan = o.ports.all()[0].s_tag
 
         # add device info to P-ONOS
         data = {
@@ -132,7 +142,7 @@
               },
               "accessDevice": {
                 "uplink": o.uplink,
-                "vlan": o.vlan
+                "vlan": vlan
               }
             }
           }
diff --git a/xos/synchronizer/steps/test_sync_olt_device.py b/xos/synchronizer/steps/test_sync_olt_device.py
index cac697d..cd4906f 100644
--- a/xos/synchronizer/steps/test_sync_olt_device.py
+++ b/xos/synchronizer/steps/test_sync_olt_device.py
@@ -40,6 +40,17 @@
     raise Exception("Unable to find service=%s xproto=%s" % (service_name, xproto_name))
 # END generate model from xproto
 
+def match_onos_req(req):
+    r = req.json()['devices']
+    if not r['abc']:
+        return False
+    else:
+        if not r['abc']['basic']['driver'] == 'pmc-olt':
+            return False
+        if not r['abc']['accessDevice']['vlan'] == "s_tag" or not r['abc']['accessDevice']['uplink'] == "129":
+            return False
+    return True
+
 class TestSyncOLTDevice(unittest.TestCase):
 
     def setUp(self):
@@ -61,6 +72,10 @@
         from sync_olt_device import SyncOLTDevice
         self.sync_step = SyncOLTDevice
 
+        pon_port = Mock()
+        pon_port.port_id = "00ff00"
+        pon_port.s_tag = "s_tag"
+
         # create a mock service instance
         o = Mock()
         o.volt_service.voltha_url = "voltha_url"
@@ -74,13 +89,20 @@
         o.host = "172.17.0.1"
         o.port = "50060"
         o.uplink = "129"
-        o.vlan = "3"
         o.driver = "pmc-olt"
 
+        # feedback state
+        o.device_id = None
+        o.admin_state = None
+        o.oper_status = None
+        o.of_id = None
+
         o.tologdict.return_value = {'name': "Mock VOLTServiceInstance"}
 
         o.save.return_value = "Saved"
 
+        o.ports.all.return_value = [pon_port]
+
         self.o = o
 
     def tearDown(self):
@@ -175,17 +197,6 @@
         }
         m.get("http://voltha_url/api/v1/logical_devices", status_code=200, json=logical_devices)
 
-        def match_onos_req(req):
-            r = req.json()['devices']
-            if not r['abc']:
-                return False
-            else:
-                if not r['abc']['basic']['driver'] == 'pmc-olt':
-                    return False
-                if not r['abc']['accessDevice']['vlan'] == "3" or not r['abc']['accessDevice']['uplink'] == "129":
-                    return False
-            return True
-
         m.post("http://p_onos_url/onos/v1/network/configuration/", status_code=200, additional_matcher=match_onos_req, json={})
 
         self.sync_step().sync_record(self.o)
@@ -195,6 +206,21 @@
         self.o.save.assert_called_once()
 
     @requests_mock.Mocker()
+    def test_sync_record_already_existing_in_voltha(self, m):
+
+        # mock device feedback state
+        self.o.device_id = "123"
+        self.o.admin_state = "ACTIVE"
+        self.o.oper_status = "ENABLED"
+        self.o.of_id = "abc"
+
+        m.post("http://p_onos_url/onos/v1/network/configuration/", status_code=200, additional_matcher=match_onos_req, json={})
+
+        self.sync_step().sync_record(self.o)
+        self.o.save.assert_not_called()
+
+
+    @requests_mock.Mocker()
     def test_delete_record(self, m):
         self.o.of_id = "abc"
         self.o.device_id = "123"