Avoid saving of related objects if nothing has changed

Change-Id: I01f291dd66e0e1a37622c6af8667955e3f4fdf74
diff --git a/xos/synchronizer/model_policies/model_policy_att_workflow_driver_serviceinstance.py b/xos/synchronizer/model_policies/model_policy_att_workflow_driver_serviceinstance.py
index e410561..a664dcd 100644
--- a/xos/synchronizer/model_policies/model_policy_att_workflow_driver_serviceinstance.py
+++ b/xos/synchronizer/model_policies/model_policy_att_workflow_driver_serviceinstance.py
@@ -67,11 +67,13 @@
             self.update_onu(si.serial_number, "DISABLED")
 
     def update_onu(self, serial_number, admin_state):
-        # TODO if the status hasn't changed don't save it again
-        self.logger.debug("MODEL_POLICY: setting ONUDevice [%s] admin_state to %s" % (serial_number, admin_state))
-        onu = ONUDevice.objects.get(serial_number=serial_number)
-        onu.admin_state = admin_state
-        onu.save(always_update_timestamp=True)
+        onu = [onu for onu in ONUDevice.objects.all() if onu.serial_number.lower() == serial_number.lower()][0]
+        if onu.admin_state == admin_state:
+            self.logger.debug("MODEL_POLICY: ONUDevice [%s] already has admin_state to %s" % (serial_number, admin_state))
+        else:
+            self.logger.debug("MODEL_POLICY: setting ONUDevice [%s] admin_state to %s" % (serial_number, admin_state))
+            onu.admin_state = admin_state
+            onu.save(always_update_timestamp=True)
 
     def get_subscriber(self, serial_number):
         try:
@@ -82,7 +84,7 @@
             return None
 
     def update_subscriber(self, subscriber, si):
-        # TODO if the status hasn't changed don't save it again
+        cur_status = subscriber.status
         if si.authentication_state == "AWAITING":
             subscriber.status = "awaiting-auth"
             si.status_message += " - Awaiting Authentication"
@@ -98,9 +100,13 @@
         elif si.authentication_state == "DENIED":
             subscriber.status = "auth-failed"
             si.status_message += " - Authentication denied"
-        self.logger.debug("MODEL_POLICY: handling subscriber", onu_device=subscriber.onu_device, authentication_state=si.authentication_state, subscriber_status=subscriber.status)
 
-        subscriber.save(always_update_timestamp=True)
+        if cur_status == subscriber.status:
+            self.logger.debug("MODEL_POLICY: subscriber status has not changed", onu_device=subscriber.onu_device,
+                              authentication_state=si.authentication_state, subscriber_status=subscriber.status)
+        else:
+            self.logger.debug("MODEL_POLICY: updating subscriber", onu_device=subscriber.onu_device, authentication_state=si.authentication_state, subscriber_status=subscriber.status)
+            subscriber.save(always_update_timestamp=True)
 
     def handle_delete(self, si):
         pass
diff --git a/xos/synchronizer/model_policies/test_model_policy_att_workflow_driver_serviceinstance.py b/xos/synchronizer/model_policies/test_model_policy_att_workflow_driver_serviceinstance.py
index d7ba4a2..8e15202 100644
--- a/xos/synchronizer/model_policies/test_model_policy_att_workflow_driver_serviceinstance.py
+++ b/xos/synchronizer/model_policies/test_model_policy_att_workflow_driver_serviceinstance.py
@@ -73,6 +73,24 @@
     def tearDown(self):
         sys.path = self.sys_path_save
 
+    def test_update_onu(self):
+
+        onu = ONUDevice(
+            serial_number="BRCM1234",
+            admin_state="ENABLED"
+        )
+        with patch.object(ONUDevice.objects, "get_items") as get_onu, \
+            patch.object(onu, "save") as onu_save:
+            get_onu.return_value = [onu]
+
+            self.policy.update_onu("brcm1234", "ENABLED")
+            onu_save.assert_not_called()
+
+            self.policy.update_onu("brcm1234", "DISABLED")
+            self.assertEqual(onu.admin_state, "DISABLED")
+            onu_save.assert_called_with(always_update_timestamp=True)
+
+
     def test_enable_onu(self):
         from helpers import AttHelpers
         with patch.object(AttHelpers, "validate_onu") as validate_onu, \
@@ -157,6 +175,7 @@
             self.assertIn("Awaiting Authentication", self.si.status_message)
             sub_save.assert_called()
             sub_save.reset_mock()
+            sub.status = None
 
             self.si.authentication_state = "REQUESTED"
             self.policy.update_subscriber(sub, self.si)
@@ -164,6 +183,7 @@
             self.assertIn("Authentication requested", self.si.status_message)
             sub_save.assert_called()
             sub_save.reset_mock()
+            sub.status = None
 
             self.si.authentication_state = "STARTED"
             self.policy.update_subscriber(sub, self.si)
@@ -171,6 +191,7 @@
             self.assertIn("Authentication started", self.si.status_message)
             sub_save.assert_called()
             sub_save.reset_mock()
+            sub.status = None
 
             self.si.authentication_state = "APPROVED"
             self.policy.update_subscriber(sub, self.si)
@@ -178,6 +199,7 @@
             self.assertIn("Authentication succeded", self.si.status_message)
             sub_save.assert_called()
             sub_save.reset_mock()
+            sub.status = None
 
             self.si.authentication_state = "DENIED"
             self.policy.update_subscriber(sub, self.si)
@@ -185,6 +207,39 @@
             self.assertIn("Authentication denied", self.si.status_message)
             sub_save.assert_called()
             sub_save.reset_mock()
+            sub.status = None
+
+    def test_update_subscriber_not(self):
+        sub = RCORDSubscriber(
+            onu_device="BRCM1234"
+        )
+
+        with patch.object(sub, "save") as sub_save:
+            sub.status = "awaiting-auth"
+            self.si.authentication_state = "AWAITING"
+            self.policy.update_subscriber(sub, self.si)
+            sub_save.assert_not_called()
+
+            sub.status = "awaiting-auth"
+            self.si.authentication_state = "REQUESTED"
+            self.policy.update_subscriber(sub, self.si)
+            sub_save.assert_not_called()
+
+            sub.status = "awaiting-auth"
+            self.si.authentication_state = "STARTED"
+            self.policy.update_subscriber(sub, self.si)
+            sub_save.assert_not_called()
+
+            sub.status = "enabled"
+            self.si.authentication_state = "APPROVED"
+            self.policy.update_subscriber(sub, self.si)
+            sub_save.assert_not_called()
+
+            sub.status = "auth-failed"
+            self.si.authentication_state = "DENIED"
+            self.policy.update_subscriber(sub, self.si)
+            sub_save.assert_not_called()
+
 
     def test_handle_update_subscriber(self):
         self.si.onu_state = "DISABLED"