blob: f7521a9b1a4517823d4719db57c1dab6eac15790 [file] [log] [blame]
Zack Williams3c5a85f2016-04-19 15:53:54 -07001from core.models import *
2from datetime import datetime
3from django.db import reset_queries
4from django.db.models import F, Q
Sapan Bhatia0235e982014-09-03 13:14:40 -04005from django.db.models.signals import post_save
Zack Williams3c5a85f2016-04-19 15:53:54 -07006from django.db.transaction import atomic
Sapan Bhatia0235e982014-09-03 13:14:40 -04007from django.dispatch import receiver
Zack Williams3c5a85f2016-04-19 15:53:54 -07008from django.utils import timezone
Scott Bakerb2254a52016-12-11 17:51:33 -08009from django.db import models as django_models
Sapan Bhatiaa9dabaa2016-01-15 11:21:20 -050010from generate.dependency_walker import *
Scott Bakerbead9d42016-06-09 21:56:19 -070011from xos.logger import Logger, logging
Zack Williams3c5a85f2016-04-19 15:53:54 -070012
13import pdb
Sapan Bhatiad49ba532015-01-23 16:09:28 +000014import time
Scott Bakerbead9d42016-06-09 21:56:19 -070015import traceback
Sapan Bhatiae2ace182015-01-16 22:17:42 +000016
Scott Baker79eaac22014-10-21 15:05:29 -070017modelPolicyEnabled = True
Sapan Bhatia538067c2015-05-09 18:10:17 +020018bad_instances=[]
Scott Baker79eaac22014-10-21 15:05:29 -070019
Scott Bakera9a1a2a2016-08-17 16:32:54 -070020model_policies = {}
21
Scott Bakerbead9d42016-06-09 21:56:19 -070022logger = Logger(level=logging.INFO)
23
Scott Baker79eaac22014-10-21 15:05:29 -070024def EnableModelPolicy(x):
25 global modelPolicyEnabled
26 modelPolicyEnabled = x
27
Sapan Bhatia538067c2015-05-09 18:10:17 +020028def 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 Bhatia475c5972014-11-05 10:32:41 -050039def update_dep(d, o):
Sapan Bhatia538067c2015-05-09 18:10:17 +020040 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 Bakerbead9d42016-06-09 21:56:19 -070049 logger.log_exc("AttributeError in update_dep")
Sapan Bhatia538067c2015-05-09 18:10:17 +020050 raise e
Scott Baker8bbc77c2015-06-22 10:56:16 -070051 except Exception,e:
Scott Bakerbead9d42016-06-09 21:56:19 -070052 logger.log_exc("Exception in update_dep")
Sapan Bhatia538067c2015-05-09 18:10:17 +020053
Sapan Bhatia475c5972014-11-05 10:32:41 -050054def delete_if_inactive(d, o):
Sapan Bhatia538067c2015-05-09 18:10:17 +020055 try:
56 d.delete()
57 print "Deleted %s (%s)"%(d,d.__class__.__name__)
58 except:
59 pass
60 return
61
Scott Bakera9a1a2a2016-08-17 16:32:54 -070062def 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 Bhatia475c5972014-11-05 10:32:41 -050074
Sapan Bhatia108f4972016-03-23 19:32:14 +010075#@atomic
Sapan Bhatiad49ba532015-01-23 16:09:28 +000076def execute_model_policy(instance, deleted):
Sapan Bhatia538067c2015-05-09 18:10:17 +020077 # Automatic dirtying
78 if (instance in bad_instances):
79 return
Sapan Bhatiae2ace182015-01-16 22:17:42 +000080
Sapan Bhatia538067c2015-05-09 18:10:17 +020081 # These are the models whose children get deleted when they are
Tony Mack3de59e32015-08-19 11:58:18 -040082 delete_policy_models = ['Slice','Instance','Network']
Sapan Bhatia538067c2015-05-09 18:10:17 +020083 sender_name = instance.__class__.__name__
84 policy_name = 'model_policy_%s'%sender_name
85 noargs = False
Sapan Bhatiad49ba532015-01-23 16:09:28 +000086
Sapan Bhatia538067c2015-05-09 18:10:17 +020087 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 Bhatiae2ace182015-01-16 22:17:42 +000092
Sapan Bhatia538067c2015-05-09 18:10:17 +020093 try:
Scott Baker1c4019a2016-08-17 21:51:19 -070094 policy_handler = model_policies.get(policy_name, None) # getattr(model_policies, policy_name, None)
Scott Bakerbead9d42016-06-09 21:56:19 -070095 logger.info("MODEL POLICY: handler %s %s" % (policy_name, policy_handler))
Sapan Bhatia538067c2015-05-09 18:10:17 +020096 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 Bakerbead9d42016-06-09 21:56:19 -0700104 logger.info("MODEL POLICY: completed handler %s %s" % (policy_name, policy_handler))
Sapan Bhatia538067c2015-05-09 18:10:17 +0200105 except:
Scott Bakerbead9d42016-06-09 21:56:19 -0700106 logger.log_exc("MODEL POLICY: Exception when running handler")
Sapan Bhatia538067c2015-05-09 18:10:17 +0200107
108 try:
Sapan Bhatia2de18622016-05-06 15:20:13 -0400109 instance.policed=timezone.now()
Sapan Bhatiad49ba532015-01-23 16:09:28 +0000110 instance.save(update_fields=['policed'])
Sapan Bhatia538067c2015-05-09 18:10:17 +0200111 except:
Scott Bakerbead9d42016-06-09 21:56:19 -0700112 logger.log_exc('MODEL POLICY: Object %r is defective'%instance)
Sapan Bhatia538067c2015-05-09 18:10:17 +0200113 bad_instances.append(instance)
Sapan Bhatiae2ace182015-01-16 22:17:42 +0000114
Sapan Bhatiace880252015-05-27 19:11:12 +0200115def noop(o,p):
116 pass
117
Scott Bakerbead9d42016-06-09 21:56:19 -0700118def 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 Williams3c5a85f2016-04-19 15:53:54 -0700130 db.close_old_connections()
Scott Bakerbead9d42016-06-09 21:56:19 -0700131 except:
132 logger.log_exc("XXX we failed to fix the failure")
133 else:
134 logger.log_exc("XXX some other error")
135
Sapan Bhatiad49ba532015-01-23 16:09:28 +0000136def run_policy():
Scott Bakera9a1a2a2016-08-17 16:32:54 -0700137 load_model_policies()
138
Sapan Bhatia538067c2015-05-09 18:10:17 +0200139 while (True):
140 start = time.time()
Scott Bakerbead9d42016-06-09 21:56:19 -0700141 try:
142 run_policy_once()
143 except:
144 logger.log_exc("MODEL_POLICY: Exception in run_policy()")
Scott Bakere611f0a2015-09-17 16:58:36 -0700145 if (time.time()-start<1):
146 time.sleep(1)
147
Scott Bakerb2254a52016-12-11 17:51:33 -0800148from core.models.plcorebase import XOSCollector
149from django.db import router
150def 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 Baker94c8a1b2017-02-17 15:33:03 -0800160 if issubclass(m.__class__, model.__class__):
161 # collector will return our parent classes; ignore them.
162 continue
Scott Bakerb2254a52016-12-11 17:51:33 -0800163 deps.append(model)
164 return deps
165
Scott Bakere611f0a2015-09-17 16:58:36 -0700166def run_policy_once():
167 from core.models import Instance,Slice,Controller,Network,User,SlicePrivilege,Site,SitePrivilege,Image,ControllerSlice,ControllerUser,ControllerSite
Sapan Bhatia5c5316c2015-11-25 16:38:02 +0100168 models = [Controller, Site, SitePrivilege, Image, ControllerSlice, ControllerSite, ControllerUser, User, Slice, Network, Instance, SlicePrivilege]
Sapan Bhatia538067c2015-05-09 18:10:17 +0200169 objects = []
170 deleted_objects = []
Sapan Bhatia475c5972014-11-05 10:32:41 -0500171
Scott Bakerbead9d42016-06-09 21:56:19 -0700172 logger.info("MODEL POLICY: run_policy_once()")
173
174 check_db_connection_okay()
175
Sapan Bhatia538067c2015-05-09 18:10:17 +0200176 for m in models:
Scott Bakerced9e4f2016-04-14 23:41:07 -0700177 res = m.objects.filter((Q(policed__lt=F('updated')) | Q(policed=None)) & Q(no_policy=False))
Sapan Bhatia538067c2015-05-09 18:10:17 +0200178 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 Bhatiace880252015-05-27 19:11:12 +0200188 # Reap non-sync'd models here
Scott Bakerb2254a52016-12-11 17:51:33 -0800189 # models_to_reap = [Slice,Network,NetworkSlice]
Sapan Bhatiace880252015-05-27 19:11:12 +0200190
Scott Bakerb2254a52016-12-11 17:51:33 -0800191 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 Bhatiace880252015-05-27 19:11:12 +0200196 dobjs = m.deleted_objects.all()
197 for d in dobjs:
Scott Bakerb2254a52016-12-11 17:51:33 -0800198 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 Bakerd18d9992016-12-22 01:25:49 -0800201 if (not getattr(d, "backend_need_reap", False)) and getattr(d, "backend_need_delete", False):
Scott Bakerb2254a52016-12-11 17:51:33 -0800202 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 Bhatiace880252015-05-27 19:11:12 +0200210 deps = walk_inv_deps(noop, d)
211 if (not deps):
Scott Bakerb2254a52016-12-11 17:51:33 -0800212 journal_object(d, "reaper.purge")
213 print 'Reaper: purging object %r'%d
Scott Baker94c8a1b2017-02-17 15:33:03 -0800214 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 Bhatia538067c2015-05-09 18:10:17 +0200220
Scott Baker8bbc77c2015-06-22 10:56:16 -0700221 try:
222 reset_queries()
223 except:
224 # this shouldn't happen, but in case it does, catch it...
Scott Bakerbead9d42016-06-09 21:56:19 -0700225 logger.log_exc("MODEL POLICY: exception in reset_queries")
226
227 logger.info("MODEL POLICY: finished run_policy_once()")