SEBA-353 Pull step should be resilient to missing PonPorts;
Delete OLTDevice if Voltha unreachable
Change-Id: I806eaa9f3bdd281ef3184ec5d6e76177199d5cad
diff --git a/VERSION b/VERSION
index 63a1a1c..8dbb0f2 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.1.9
+2.1.10
diff --git a/xos/synchronizer/pull_steps/pull_onus.py b/xos/synchronizer/pull_steps/pull_onus.py
index d4c2d10..18f047c 100644
--- a/xos/synchronizer/pull_steps/pull_onus.py
+++ b/xos/synchronizer/pull_steps/pull_onus.py
@@ -82,7 +82,7 @@
log.debug("Skipping pull on ONUDevice %s as enacted < updated" % model.serial_number, serial_number=model.serial_number, id=model.id, enacted=model.enacted, updated=model.updated)
# if we are not updating the device we still need to pull ports
self.fetch_onu_ports(model)
- return
+ continue
except IndexError:
model = ONUDevice()
@@ -90,6 +90,18 @@
log.debug("ONUDevice is new, creating it", serial_number=onu["serial_number"])
+ try:
+ olt = OLTDevice.objects.get(device_id=onu["parent_id"])
+ except IndexError:
+ log.warning("Unable to find olt for ONUDevice", serial_number=onu["serial_number"], olt_device_id=onu["parent_id"])
+ continue
+
+ try:
+ pon_port = PONPort.objects.get(port_no=onu["parent_port_no"], olt_device_id=olt.id)
+ except IndexError:
+ log.warning("Unable to find pon_port for ONUDevice", serial_number=onu["serial_number"], olt_device_id=onu["parent_id"], port_no=onu["parent_port_no"])
+ continue
+
# Adding feedback state to the device
model.vendor = onu["vendor"]
model.device_type = onu["type"]
@@ -100,9 +112,6 @@
model.connect_status = onu["connect_status"]
model.xos_managed = False
- olt = OLTDevice.objects.get(device_id=onu["parent_id"])
- pon_port = PONPort.objects.get(port_no=onu["parent_port_no"], olt_device_id=olt.id)
-
model.pon_port = pon_port
model.pon_port_id = pon_port.id
diff --git a/xos/synchronizer/pull_steps/test_pull_onus.py b/xos/synchronizer/pull_steps/test_pull_onus.py
index a1fb763..6fc5816 100644
--- a/xos/synchronizer/pull_steps/test_pull_onus.py
+++ b/xos/synchronizer/pull_steps/test_pull_onus.py
@@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import functools
import unittest
from mock import patch, call, Mock, PropertyMock
import requests_mock
@@ -85,10 +86,18 @@
self.olt = Mock()
self.olt.id = 1
+ # second mock OLTDevice
+ self.olt2 = Mock()
+ self.olt2.id = 2
+
# mock pon port
self.pon_port = Mock()
self.pon_port.id = 1
+ # mock pon port
+ self.pon_port2 = Mock()
+ self.pon_port2.id = 2
+
# mock voltha responses
self.devices = {
"items": [
@@ -109,6 +118,39 @@
]
}
+ self.two_devices = {
+ "items": [
+ {
+ "id": "0001130158f01b2d",
+ "type": "broadcom_onu",
+ "vendor": "Broadcom",
+ "serial_number": "BRCM22222222",
+ "vendor_id": "BRCM",
+ "adapter": "broadcom_onu",
+ "vlan": 0,
+ "admin_state": "ENABLED",
+ "oper_status": "ACTIVE",
+ "connect_status": "REACHABLE",
+ "parent_id": "00010fc93996afea",
+ "parent_port_no": 1
+ },
+ {
+ "id": "0001130158f01b2e",
+ "type": "broadcom_onu",
+ "vendor": "Broadcom",
+ "serial_number": "BRCM22222223",
+ "vendor_id": "BRCM",
+ "adapter": "broadcom_onu",
+ "vlan": 0,
+ "admin_state": "ENABLED",
+ "oper_status": "ACTIVE",
+ "connect_status": "REACHABLE",
+ "parent_id": "00010fc93996afeb",
+ "parent_port_no": 1
+ }
+ ],
+ }
+
# TODO add ports
self.ports = {
"items": []
@@ -148,6 +190,88 @@
self.assertEqual(mock_save.call_count, 1)
+ @requests_mock.Mocker()
+ def test_pull_bad_pon(self, m):
+
+ def olt_side_effect(device_id):
+ # fail the first onu device
+ if device_id=="00010fc93996afea":
+ return self.olt
+ else:
+ return self.olt2
+
+ def pon_port_side_effect(mock_pon_port, port_no, olt_device_id):
+ # fail the first onu device
+ if olt_device_id==1:
+ raise IndexError()
+ return self.pon_port2
+
+ with patch.object(VOLTService.objects, "all") as olt_service_mock, \
+ patch.object(OLTDevice.objects, "get") as mock_olt_device, \
+ patch.object(PONPort.objects, "get") as mock_pon_port, \
+ patch.object(ONUDevice, "save", autospec=True) as mock_save:
+ olt_service_mock.return_value = [self.volt_service]
+ mock_pon_port.side_effect = functools.partial(pon_port_side_effect, self.pon_port)
+ mock_olt_device.side_effect = olt_side_effect
+
+ m.get("http://voltha_url:1234/api/v1/devices", status_code=200, json=self.two_devices)
+ m.get("http://voltha_url:1234/api/v1/devices/0001130158f01b2d/ports", status_code=200, json=self.ports)
+ m.get("http://voltha_url:1234/api/v1/devices/0001130158f01b2e/ports", status_code=200, json=self.ports)
+
+ self.sync_step().pull_records()
+
+ self.assertEqual(mock_save.call_count, 1)
+ saved_onu = mock_save.call_args[0][0]
+
+ # we should get the second onu in self.two_onus
+
+ self.assertEqual(saved_onu.admin_state, "ENABLED")
+ self.assertEqual(saved_onu.oper_status, "ACTIVE")
+ self.assertEqual(saved_onu.connect_status, "REACHABLE")
+ self.assertEqual(saved_onu.device_type, "broadcom_onu")
+ self.assertEqual(saved_onu.vendor, "Broadcom")
+ self.assertEqual(saved_onu.device_id, "0001130158f01b2e")
+
+ self.assertEqual(mock_save.call_count, 1)
+
+ @requests_mock.Mocker()
+ def test_pull_bad_olt(self, m):
+
+ def olt_side_effect(device_id):
+ # fail the first onu device
+ if device_id=="00010fc93996afea":
+ raise IndexError()
+ else:
+ return self.olt2
+
+ with patch.object(VOLTService.objects, "all") as olt_service_mock, \
+ patch.object(OLTDevice.objects, "get") as mock_olt_device, \
+ patch.object(PONPort.objects, "get") as mock_pon_port, \
+ patch.object(ONUDevice, "save", autospec=True) as mock_save:
+ olt_service_mock.return_value = [self.volt_service]
+ mock_pon_port.return_value = self.pon_port2
+ mock_olt_device.side_effect = olt_side_effect
+
+ m.get("http://voltha_url:1234/api/v1/devices", status_code=200, json=self.two_devices)
+ m.get("http://voltha_url:1234/api/v1/devices/0001130158f01b2d/ports", status_code=200, json=self.ports)
+ m.get("http://voltha_url:1234/api/v1/devices/0001130158f01b2e/ports", status_code=200, json=self.ports)
+
+ self.sync_step().pull_records()
+
+ self.assertEqual(mock_save.call_count, 1)
+ saved_onu = mock_save.call_args[0][0]
+
+ # we should get the second onu in self.two_onus
+
+ self.assertEqual(saved_onu.admin_state, "ENABLED")
+ self.assertEqual(saved_onu.oper_status, "ACTIVE")
+ self.assertEqual(saved_onu.connect_status, "REACHABLE")
+ self.assertEqual(saved_onu.device_type, "broadcom_onu")
+ self.assertEqual(saved_onu.vendor, "Broadcom")
+ self.assertEqual(saved_onu.device_id, "0001130158f01b2e")
+
+ self.assertEqual(mock_save.call_count, 1)
+
if __name__ == "__main__":
unittest.main()
\ No newline at end of file
diff --git a/xos/synchronizer/steps/sync_olt_device.py b/xos/synchronizer/steps/sync_olt_device.py
index 4646dbd..40f884e 100644
--- a/xos/synchronizer/steps/sync_olt_device.py
+++ b/xos/synchronizer/steps/sync_olt_device.py
@@ -185,22 +185,25 @@
log.warning("OLTDevice %s has no device_id, it was never saved in VOLTHA" % model.name)
return
else:
- # Disable the OLT device
- request = requests.post("%s:%d/api/v1/devices/%s/disable" % (voltha['url'], voltha['port'], model.device_id))
+ try:
+ # Disable the OLT device
+ request = requests.post("%s:%d/api/v1/devices/%s/disable" % (voltha['url'], voltha['port'], model.device_id))
- if request.status_code != 200:
- log.error("Failed to disable OLT device in VOLTHA: %s - %s" % (model.name, model.device_id), rest_response=request.text, rest_status_code=request.status_code)
- raise Exception("Failed to disable OLT device in VOLTHA")
+ if request.status_code != 200:
+ log.error("Failed to disable OLT device in VOLTHA: %s - %s" % (model.name, model.device_id), rest_response=request.text, rest_status_code=request.status_code)
+ raise Exception("Failed to disable OLT device in VOLTHA")
- # NOTE [teo] wait some time after the disable to let VOLTHA doing its things
- i = 0
- for i in list(reversed(range(10))):
- sleep(1)
- log.info("Deleting the OLT in %s seconds" % i)
+ # NOTE [teo] wait some time after the disable to let VOLTHA doing its things
+ i = 0
+ for i in list(reversed(range(10))):
+ sleep(1)
+ log.info("Deleting the OLT in %s seconds" % i)
- # Delete the OLT device
- request = requests.delete("%s:%d/api/v1/devices/%s/delete" % (voltha['url'], voltha['port'], model.device_id))
+ # Delete the OLT device
+ request = requests.delete("%s:%d/api/v1/devices/%s/delete" % (voltha['url'], voltha['port'], model.device_id))
- if request.status_code != 200:
- log.error("Failed to delete OLT device from VOLTHA: %s - %s" % (model.name, model.device_id), rest_response=request.text, rest_status_code=request.status_code)
- raise Exception("Failed to delete OLT device from VOLTHA")
+ if request.status_code != 200:
+ log.error("Failed to delete OLT device from VOLTHA: %s - %s" % (model.name, model.device_id), rest_response=request.text, rest_status_code=request.status_code)
+ raise Exception("Failed to delete OLT device from VOLTHA")
+ except requests.ConnectionError:
+ log.warning("ConnectionError when contacting Voltha in OLT delete step", name=model.name, device_id=model.device_id)
diff --git a/xos/synchronizer/steps/test_sync_olt_device.py b/xos/synchronizer/steps/test_sync_olt_device.py
index 18ed99b..38da1a1 100644
--- a/xos/synchronizer/steps/test_sync_olt_device.py
+++ b/xos/synchronizer/steps/test_sync_olt_device.py
@@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from requests import ConnectionError
import unittest
import functools
from mock import patch, call, Mock, PropertyMock
@@ -339,6 +340,18 @@
self.assertEqual(m.call_count, 2)
+ @patch('requests.post')
+ def test_delete_record_connectionerror(self, m):
+ self.o.of_id = "0001000ce2314000"
+ self.o.device_id = "123"
+
+ m.side_effect = ConnectionError()
+
+ self.sync_step().delete_record(self.o)
+
+ # No exception thrown, as ConnectionError will be caught
+
+
@requests_mock.Mocker()
def test_delete_unsynced_record(self, m):