CORD-1762 add xosbase.backend_need_delete_policy and serviceinstance.link_delete_count fields

Change-Id: Ia9cdeb8751e5cc789e95591b22a546e19762c8c4
(cherry picked from commit dbb36277ebad858a6870974be0393a209bfb4f9d)
diff --git a/xos/core/models/attic/serviceinstancelink.model b/xos/core/models/attic/serviceinstancelink_model.py
similarity index 63%
rename from xos/core/models/attic/serviceinstancelink.model
rename to xos/core/models/attic/serviceinstancelink_model.py
index 88ca4b4..0d33dcb 100644
--- a/xos/core/models/attic/serviceinstancelink.model
+++ b/xos/core/models/attic/serviceinstancelink_model.py
@@ -22,3 +22,12 @@
 
     super(ServiceInstanceLink, self).save(*args, **kwargs)
 
+def delete(self, *args, **kwargs):
+    provider_service_instance = self.provider_service_instance
+    super(ServiceInstanceLink, self).delete(*args, **kwargs)
+
+    # This should be handled by a model_policy, but we don't currently have a
+    # model policy for core objects, so handle it during the save method.
+    if provider_service_instance and (not provider_service_instance.deleted):
+        provider_service_instance.link_deleted_count += 1
+        provider_service_instance.save(always_update_timestamp=True, update_fields=["updated", "link_deleted_count"])
diff --git a/xos/core/models/core.xproto b/xos/core/models/core.xproto
index 919674c..58b2ed2 100644
--- a/xos/core/models/core.xproto
+++ b/xos/core/models/core.xproto
@@ -20,6 +20,7 @@
      optional string policy_status = 16 [default = "Policy in process", max_length = 1024];
      optional int32 policy_code = 16 [default = 0];
      required string leaf_model_name = 17 [null = False, max_length = 1024, help_text = "The most specialized model in this chain of inheritance, often defined by a service developer"];
+     required bool backend_need_delete_policy = 18 [default = False, help_text = "True if delete model_policy must be run before object can be reaped"];
 }
 
 // The calling user represents the user being accessed, or is a site admin.
@@ -553,6 +554,7 @@
      required manytoone owner->Service:service_instances = 2 [db_index = True, null = False, blank = False];
      optional string service_specific_id = 3 [db_index = False, max_length = 30, null = True, content_type = "stripped", blank = True];
      optional string service_specific_attribute = 10 [db_index = False, null = True, blank = True, varchar = True];
+     optional uint32 link_deleted_count = 11 [default = 0, help_text = "Incremented each time a provided_link is deleted from this ServiceInstance"];
 }
 
 message ServiceInstanceLink (XOSBase) {
diff --git a/xos/coreapi/reaper.py b/xos/coreapi/reaper.py
index 3d6def0..25fb226 100644
--- a/xos/coreapi/reaper.py
+++ b/xos/coreapi/reaper.py
@@ -133,6 +133,10 @@
                         log.info("skipping because it has need_delete set", object = d)
                         continue
 
+                    if (not getattr(d, "backend_need_reap", False)) and getattr(d, "backend_need_delete_policy", False):
+                        log.info("skipping because it has need_delete_policy set", object = d)
+                        continue
+
                     cascade_set = self.get_cascade_set(d)
                     if cascade_set:
                         self.journal_object(d, "reaper.cascade_set", msg=",".join([str(m) for m in cascade_set]))