Zack Williams | 3c5a85f | 2016-04-19 15:53:54 -0700 | [diff] [blame] | 1 | from core.models import * |
| 2 | from datetime import datetime |
| 3 | from django.db import reset_queries |
| 4 | from django.db.models import F, Q |
Sapan Bhatia | 0235e98 | 2014-09-03 13:14:40 -0400 | [diff] [blame] | 5 | from django.db.models.signals import post_save |
Zack Williams | 3c5a85f | 2016-04-19 15:53:54 -0700 | [diff] [blame] | 6 | from django.db.transaction import atomic |
Sapan Bhatia | 0235e98 | 2014-09-03 13:14:40 -0400 | [diff] [blame] | 7 | from django.dispatch import receiver |
Zack Williams | 3c5a85f | 2016-04-19 15:53:54 -0700 | [diff] [blame] | 8 | from django.utils import timezone |
Scott Baker | b2254a5 | 2016-12-11 17:51:33 -0800 | [diff] [blame] | 9 | from django.db import models as django_models |
Sapan Bhatia | a9dabaa | 2016-01-15 11:21:20 -0500 | [diff] [blame] | 10 | from generate.dependency_walker import * |
Scott Baker | bead9d4 | 2016-06-09 21:56:19 -0700 | [diff] [blame] | 11 | from xos.logger import Logger, logging |
Zack Williams | 3c5a85f | 2016-04-19 15:53:54 -0700 | [diff] [blame] | 12 | |
| 13 | import pdb |
Sapan Bhatia | d49ba53 | 2015-01-23 16:09:28 +0000 | [diff] [blame] | 14 | import time |
Scott Baker | bead9d4 | 2016-06-09 21:56:19 -0700 | [diff] [blame] | 15 | import traceback |
Sapan Bhatia | e2ace18 | 2015-01-16 22:17:42 +0000 | [diff] [blame] | 16 | |
Scott Baker | 79eaac2 | 2014-10-21 15:05:29 -0700 | [diff] [blame] | 17 | modelPolicyEnabled = True |
Sapan Bhatia | 538067c | 2015-05-09 18:10:17 +0200 | [diff] [blame] | 18 | bad_instances=[] |
Scott Baker | 79eaac2 | 2014-10-21 15:05:29 -0700 | [diff] [blame] | 19 | |
Scott Baker | a9a1a2a | 2016-08-17 16:32:54 -0700 | [diff] [blame] | 20 | model_policies = {} |
| 21 | |
Scott Baker | bead9d4 | 2016-06-09 21:56:19 -0700 | [diff] [blame] | 22 | logger = Logger(level=logging.INFO) |
| 23 | |
Scott Baker | 79eaac2 | 2014-10-21 15:05:29 -0700 | [diff] [blame] | 24 | def EnableModelPolicy(x): |
| 25 | global modelPolicyEnabled |
| 26 | modelPolicyEnabled = x |
| 27 | |
Sapan Bhatia | 538067c | 2015-05-09 18:10:17 +0200 | [diff] [blame] | 28 | def update_wp(d, o): |
| 29 | try: |
| 30 | save_fields = [] |
| 31 | if (d.write_protect != o.write_protect): |
| 32 | d.write_protect = o.write_protect |
| 33 | save_fields.append('write_protect') |
| 34 | if (save_fields): |
| 35 | d.save(update_fields=save_fields) |
| 36 | except AttributeError,e: |
| 37 | raise e |
| 38 | |
Sapan Bhatia | 475c597 | 2014-11-05 10:32:41 -0500 | [diff] [blame] | 39 | def update_dep(d, o): |
Sapan Bhatia | 538067c | 2015-05-09 18:10:17 +0200 | [diff] [blame] | 40 | try: |
| 41 | print 'Trying to update %s'%d |
| 42 | save_fields = [] |
| 43 | if (d.updated < o.updated): |
| 44 | save_fields = ['updated'] |
| 45 | |
| 46 | if (save_fields): |
| 47 | d.save(update_fields=save_fields) |
| 48 | except AttributeError,e: |
Scott Baker | bead9d4 | 2016-06-09 21:56:19 -0700 | [diff] [blame] | 49 | logger.log_exc("AttributeError in update_dep") |
Sapan Bhatia | 538067c | 2015-05-09 18:10:17 +0200 | [diff] [blame] | 50 | raise e |
Scott Baker | 8bbc77c | 2015-06-22 10:56:16 -0700 | [diff] [blame] | 51 | except Exception,e: |
Scott Baker | bead9d4 | 2016-06-09 21:56:19 -0700 | [diff] [blame] | 52 | logger.log_exc("Exception in update_dep") |
Sapan Bhatia | 538067c | 2015-05-09 18:10:17 +0200 | [diff] [blame] | 53 | |
Sapan Bhatia | 475c597 | 2014-11-05 10:32:41 -0500 | [diff] [blame] | 54 | def delete_if_inactive(d, o): |
Sapan Bhatia | 538067c | 2015-05-09 18:10:17 +0200 | [diff] [blame] | 55 | try: |
| 56 | d.delete() |
| 57 | print "Deleted %s (%s)"%(d,d.__class__.__name__) |
| 58 | except: |
| 59 | pass |
| 60 | return |
| 61 | |
Scott Baker | a9a1a2a | 2016-08-17 16:32:54 -0700 | [diff] [blame] | 62 | def load_model_policies(policies_dir=None): |
| 63 | global model_policies |
| 64 | |
| 65 | if policies_dir is None: |
| 66 | policies_dir = Config().observer_model_policies_dir |
| 67 | |
| 68 | for fn in os.listdir(policies_dir): |
| 69 | pathname = os.path.join(policies_dir,fn) |
| 70 | if os.path.isfile(pathname) and fn.startswith("model_policy_") and fn.endswith(".py") and (fn!="__init__.py"): |
| 71 | model_policies[fn[:-3]] = imp.load_source(fn[:-3],pathname) |
| 72 | |
| 73 | logger.info("Loaded model polices %s from %s" % (",".join(model_policies.keys()), policies_dir)) |
Sapan Bhatia | 475c597 | 2014-11-05 10:32:41 -0500 | [diff] [blame] | 74 | |
Sapan Bhatia | 108f497 | 2016-03-23 19:32:14 +0100 | [diff] [blame] | 75 | #@atomic |
Sapan Bhatia | d49ba53 | 2015-01-23 16:09:28 +0000 | [diff] [blame] | 76 | def execute_model_policy(instance, deleted): |
Sapan Bhatia | 538067c | 2015-05-09 18:10:17 +0200 | [diff] [blame] | 77 | # Automatic dirtying |
| 78 | if (instance in bad_instances): |
| 79 | return |
Sapan Bhatia | e2ace18 | 2015-01-16 22:17:42 +0000 | [diff] [blame] | 80 | |
Sapan Bhatia | 538067c | 2015-05-09 18:10:17 +0200 | [diff] [blame] | 81 | # These are the models whose children get deleted when they are |
Tony Mack | 3de59e3 | 2015-08-19 11:58:18 -0400 | [diff] [blame] | 82 | delete_policy_models = ['Slice','Instance','Network'] |
Sapan Bhatia | 538067c | 2015-05-09 18:10:17 +0200 | [diff] [blame] | 83 | sender_name = instance.__class__.__name__ |
| 84 | policy_name = 'model_policy_%s'%sender_name |
| 85 | noargs = False |
Sapan Bhatia | d49ba53 | 2015-01-23 16:09:28 +0000 | [diff] [blame] | 86 | |
Sapan Bhatia | 538067c | 2015-05-09 18:10:17 +0200 | [diff] [blame] | 87 | if (not deleted): |
| 88 | walk_inv_deps(update_dep, instance) |
| 89 | walk_deps(update_wp, instance) |
| 90 | elif (sender_name in delete_policy_models): |
| 91 | walk_inv_deps(delete_if_inactive, instance) |
Sapan Bhatia | e2ace18 | 2015-01-16 22:17:42 +0000 | [diff] [blame] | 92 | |
Sapan Bhatia | 538067c | 2015-05-09 18:10:17 +0200 | [diff] [blame] | 93 | try: |
Scott Baker | 1c4019a | 2016-08-17 21:51:19 -0700 | [diff] [blame] | 94 | policy_handler = model_policies.get(policy_name, None) # getattr(model_policies, policy_name, None) |
Scott Baker | bead9d4 | 2016-06-09 21:56:19 -0700 | [diff] [blame] | 95 | logger.info("MODEL POLICY: handler %s %s" % (policy_name, policy_handler)) |
Sapan Bhatia | 538067c | 2015-05-09 18:10:17 +0200 | [diff] [blame] | 96 | if policy_handler is not None: |
| 97 | if (deleted): |
| 98 | try: |
| 99 | policy_handler.handle_delete(instance) |
| 100 | except AttributeError: |
| 101 | pass |
| 102 | else: |
| 103 | policy_handler.handle(instance) |
Scott Baker | bead9d4 | 2016-06-09 21:56:19 -0700 | [diff] [blame] | 104 | logger.info("MODEL POLICY: completed handler %s %s" % (policy_name, policy_handler)) |
Sapan Bhatia | 538067c | 2015-05-09 18:10:17 +0200 | [diff] [blame] | 105 | except: |
Scott Baker | bead9d4 | 2016-06-09 21:56:19 -0700 | [diff] [blame] | 106 | logger.log_exc("MODEL POLICY: Exception when running handler") |
Sapan Bhatia | 538067c | 2015-05-09 18:10:17 +0200 | [diff] [blame] | 107 | |
| 108 | try: |
Sapan Bhatia | 2de1862 | 2016-05-06 15:20:13 -0400 | [diff] [blame] | 109 | instance.policed=timezone.now() |
Sapan Bhatia | d49ba53 | 2015-01-23 16:09:28 +0000 | [diff] [blame] | 110 | instance.save(update_fields=['policed']) |
Sapan Bhatia | 538067c | 2015-05-09 18:10:17 +0200 | [diff] [blame] | 111 | except: |
Scott Baker | bead9d4 | 2016-06-09 21:56:19 -0700 | [diff] [blame] | 112 | logger.log_exc('MODEL POLICY: Object %r is defective'%instance) |
Sapan Bhatia | 538067c | 2015-05-09 18:10:17 +0200 | [diff] [blame] | 113 | bad_instances.append(instance) |
Sapan Bhatia | e2ace18 | 2015-01-16 22:17:42 +0000 | [diff] [blame] | 114 | |
Sapan Bhatia | ce88025 | 2015-05-27 19:11:12 +0200 | [diff] [blame] | 115 | def noop(o,p): |
| 116 | pass |
| 117 | |
Scott Baker | bead9d4 | 2016-06-09 21:56:19 -0700 | [diff] [blame] | 118 | def check_db_connection_okay(): |
| 119 | # django implodes if the database connection is closed by docker-compose |
| 120 | from django import db |
| 121 | try: |
| 122 | db.connection.cursor() |
| 123 | #diag = Diag.objects.filter(name="foo").first() |
| 124 | except Exception, e: |
| 125 | if "connection already closed" in traceback.format_exc(): |
| 126 | logger.error("XXX connection already closed") |
| 127 | try: |
| 128 | # if db.connection: |
| 129 | # db.connection.close() |
Zack Williams | 3c5a85f | 2016-04-19 15:53:54 -0700 | [diff] [blame] | 130 | db.close_old_connections() |
Scott Baker | bead9d4 | 2016-06-09 21:56:19 -0700 | [diff] [blame] | 131 | except: |
| 132 | logger.log_exc("XXX we failed to fix the failure") |
| 133 | else: |
| 134 | logger.log_exc("XXX some other error") |
| 135 | |
Sapan Bhatia | d49ba53 | 2015-01-23 16:09:28 +0000 | [diff] [blame] | 136 | def run_policy(): |
Scott Baker | a9a1a2a | 2016-08-17 16:32:54 -0700 | [diff] [blame] | 137 | load_model_policies() |
| 138 | |
Sapan Bhatia | 538067c | 2015-05-09 18:10:17 +0200 | [diff] [blame] | 139 | while (True): |
| 140 | start = time.time() |
Scott Baker | bead9d4 | 2016-06-09 21:56:19 -0700 | [diff] [blame] | 141 | try: |
| 142 | run_policy_once() |
| 143 | except: |
| 144 | logger.log_exc("MODEL_POLICY: Exception in run_policy()") |
Scott Baker | e611f0a | 2015-09-17 16:58:36 -0700 | [diff] [blame] | 145 | if (time.time()-start<1): |
| 146 | time.sleep(1) |
| 147 | |
Scott Baker | b2254a5 | 2016-12-11 17:51:33 -0800 | [diff] [blame] | 148 | from core.models.plcorebase import XOSCollector |
| 149 | from django.db import router |
| 150 | def has_deleted_dependencies(m): |
| 151 | # Check to see if 'm' would cascade to any objects that have the 'deleted' |
| 152 | # field set in them. |
| 153 | collector = XOSCollector(using=router.db_for_write(m.__class__, instance=m)) |
| 154 | collector.collect([m])
|
| 155 | deps=[]
|
| 156 | for (k, models) in collector.data.items():
|
| 157 | for model in models: |
| 158 | if model==m: |
| 159 | continue |
Scott Baker | 94c8a1b | 2017-02-17 15:33:03 -0800 | [diff] [blame] | 160 | if issubclass(m.__class__, model.__class__): |
| 161 | # collector will return our parent classes; ignore them. |
| 162 | continue |
Scott Baker | b2254a5 | 2016-12-11 17:51:33 -0800 | [diff] [blame] | 163 | deps.append(model) |
| 164 | return deps |
| 165 | |
Scott Baker | e611f0a | 2015-09-17 16:58:36 -0700 | [diff] [blame] | 166 | def run_policy_once(): |
| 167 | from core.models import Instance,Slice,Controller,Network,User,SlicePrivilege,Site,SitePrivilege,Image,ControllerSlice,ControllerUser,ControllerSite |
Sapan Bhatia | 5c5316c | 2015-11-25 16:38:02 +0100 | [diff] [blame] | 168 | models = [Controller, Site, SitePrivilege, Image, ControllerSlice, ControllerSite, ControllerUser, User, Slice, Network, Instance, SlicePrivilege] |
Sapan Bhatia | 538067c | 2015-05-09 18:10:17 +0200 | [diff] [blame] | 169 | objects = [] |
| 170 | deleted_objects = [] |
Sapan Bhatia | 475c597 | 2014-11-05 10:32:41 -0500 | [diff] [blame] | 171 | |
Scott Baker | bead9d4 | 2016-06-09 21:56:19 -0700 | [diff] [blame] | 172 | logger.info("MODEL POLICY: run_policy_once()") |
| 173 | |
| 174 | check_db_connection_okay() |
| 175 | |
Sapan Bhatia | 538067c | 2015-05-09 18:10:17 +0200 | [diff] [blame] | 176 | for m in models: |
Scott Baker | ced9e4f | 2016-04-14 23:41:07 -0700 | [diff] [blame] | 177 | res = m.objects.filter((Q(policed__lt=F('updated')) | Q(policed=None)) & Q(no_policy=False)) |
Sapan Bhatia | 538067c | 2015-05-09 18:10:17 +0200 | [diff] [blame] | 178 | objects.extend(res) |
| 179 | res = m.deleted_objects.filter(Q(policed__lt=F('updated')) | Q(policed=None)) |
| 180 | deleted_objects.extend(res) |
| 181 | |
| 182 | for o in objects: |
| 183 | execute_model_policy(o, o.deleted) |
| 184 | |
| 185 | for o in deleted_objects: |
| 186 | execute_model_policy(o, True) |
| 187 | |
Sapan Bhatia | ce88025 | 2015-05-27 19:11:12 +0200 | [diff] [blame] | 188 | # Reap non-sync'd models here |
Scott Baker | b2254a5 | 2016-12-11 17:51:33 -0800 | [diff] [blame] | 189 | # models_to_reap = [Slice,Network,NetworkSlice] |
Sapan Bhatia | ce88025 | 2015-05-27 19:11:12 +0200 | [diff] [blame] | 190 | |
Scott Baker | b2254a5 | 2016-12-11 17:51:33 -0800 | [diff] [blame] | 191 | models_to_reap = django_models.get_models(include_auto_created=False) |
| 192 | for m in models_to_reap: |
| 193 | if not hasattr(m, "deleted_objects"): |
| 194 | continue |
| 195 | |
Sapan Bhatia | ce88025 | 2015-05-27 19:11:12 +0200 | [diff] [blame] | 196 | dobjs = m.deleted_objects.all() |
| 197 | for d in dobjs: |
Scott Baker | b2254a5 | 2016-12-11 17:51:33 -0800 | [diff] [blame] | 198 | if hasattr(d,"_meta") and hasattr(d._meta,"proxy") and d._meta.proxy: |
| 199 | # skip proxy objects; we'll get the base instead |
| 200 | continue |
Scott Baker | d18d999 | 2016-12-22 01:25:49 -0800 | [diff] [blame] | 201 | if (not getattr(d, "backend_need_reap", False)) and getattr(d, "backend_need_delete", False): |
Scott Baker | b2254a5 | 2016-12-11 17:51:33 -0800 | [diff] [blame] | 202 | journal_object(d, "reaper.need_delete") |
| 203 | print "Reaper: skipping %r because it has need_delete set" % d |
| 204 | continue |
| 205 | deleted_deps = has_deleted_dependencies(d) |
| 206 | if deleted_deps: |
| 207 | journal_object(d, "reaper.has_deleted_dependencies", msg=",".join([str(m) for m in deleted_deps])) |
| 208 | print 'Reaper: cannot purge object %r because it has deleted dependencies: %s' % (d, ",".join([str(m) for m in deleted_deps])) |
| 209 | continue |
Sapan Bhatia | ce88025 | 2015-05-27 19:11:12 +0200 | [diff] [blame] | 210 | deps = walk_inv_deps(noop, d) |
| 211 | if (not deps): |
Scott Baker | b2254a5 | 2016-12-11 17:51:33 -0800 | [diff] [blame] | 212 | journal_object(d, "reaper.purge") |
| 213 | print 'Reaper: purging object %r'%d |
Scott Baker | 94c8a1b | 2017-02-17 15:33:03 -0800 | [diff] [blame] | 214 | try: |
| 215 | d.delete(purge=True) |
| 216 | except: |
| 217 | journal_object(d, "reaper.purge.exception") |
| 218 | print 'Reaper: exception purging object %r'%d |
| 219 | traceback.print_exc() |
Sapan Bhatia | 538067c | 2015-05-09 18:10:17 +0200 | [diff] [blame] | 220 | |
Scott Baker | 8bbc77c | 2015-06-22 10:56:16 -0700 | [diff] [blame] | 221 | try: |
| 222 | reset_queries() |
| 223 | except: |
| 224 | # this shouldn't happen, but in case it does, catch it... |
Scott Baker | bead9d4 | 2016-06-09 21:56:19 -0700 | [diff] [blame] | 225 | logger.log_exc("MODEL POLICY: exception in reset_queries") |
| 226 | |
| 227 | logger.info("MODEL POLICY: finished run_policy_once()") |