blob: 5374e014c9ab6a24644accd73254ffd7b1e9bd4d [file] [log] [blame]
# Copyright 2017-present Open Networking Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import print_function
from multistructlog import create_logger
from xosconfig import Config
from core.models import Privilege, imp
from django.db import reset_queries
from django.db.models import F, Q
from django.utils import timezone
from generate.dependency_walker import walk_deps, walk_inv_deps
import os
import time
import traceback
modelPolicyEnabled = True
bad_instances = []
model_policies = {}
Config.init()
log = create_logger(Config().get("logging"))
def EnableModelPolicy(x):
global modelPolicyEnabled
modelPolicyEnabled = x
def update_wp(d, o):
try:
save_fields = []
if d.write_protect != o.write_protect:
d.write_protect = o.write_protect
save_fields.append("write_protect")
if save_fields:
d.save(update_fields=save_fields)
except AttributeError as e:
raise e
def update_dep(d, o):
try:
print("Trying to update %s" % d)
save_fields = []
if d.updated < o.updated:
save_fields = ["updated"]
if save_fields:
d.save(update_fields=save_fields)
except AttributeError as e:
log.exception("AttributeError in update_dep", e=e)
raise e
except Exception as e:
log.exception("Exception in update_dep", e=e)
def delete_if_inactive(d, o):
try:
d.delete()
print("Deleted %s (%s)" % (d, d.__class__.__name__))
except BaseException:
pass
return
def load_model_policies(policies_dir=None):
global model_policies
if policies_dir is None:
policies_dir = Config().observer_model_policies_dir
for fn in os.listdir(policies_dir):
pathname = os.path.join(policies_dir, fn)
if (
os.path.isfile(pathname)
and fn.startswith("model_policy_")
and fn.endswith(".py")
and (fn != "__init__.py")
):
model_policies[fn[:-3]] = imp.load_source(fn[:-3], pathname)
log.debug(
"Loaded model polices %s from %s"
% (",".join(model_policies.keys()), policies_dir)
)
# @atomic
def execute_model_policy(instance, deleted):
# Automatic dirtying
if instance in bad_instances:
return
# These are the models whose children get deleted when they are
delete_policy_models = ["Slice", "Instance", "Network"]
sender_name = instance.__class__.__name__
policy_name = "model_policy_%s" % sender_name
if not deleted:
walk_inv_deps(update_dep, instance)
walk_deps(update_wp, instance)
elif sender_name in delete_policy_models:
walk_inv_deps(delete_if_inactive, instance)
try:
policy_handler = model_policies.get(
policy_name, None
) # getattr(model_policies, policy_name, None)
log.debug(
"MODEL POLICY: handler %s %s",
policy_name=policy_name,
policy_handler=policy_handler,
)
if policy_handler is not None:
if deleted:
try:
policy_handler.handle_delete(instance)
except AttributeError:
pass
else:
policy_handler.handle(instance)
log.debug(
"MODEL POLICY: completed handler",
policy_name=policy_name,
policy_handler=policy_handler,
)
except Exception as e:
log.exception("MODEL POLICY: Exception when running handler", e=e)
try:
instance.policed = timezone.now()
instance.save(update_fields=["policed"])
except BaseException:
log.exception("MODEL POLICY: Object is defective", object=instance)
bad_instances.append(instance)
def noop(o, p):
pass
def check_db_connection_okay():
# django implodes if the database connection is closed by docker-compose
from django import db
try:
db.connection.cursor()
except Exception as e:
if "connection already closed" in traceback.format_exc():
log.error("XXX connection already closed")
try:
# if db.connection:
# db.connection.close()
db.close_old_connections()
except Exception as f:
log.exception("XXX we failed to fix the failure", e=f)
else:
log.exception("XXX some other error", e=e)
def run_policy():
load_model_policies()
while True:
start = time.time()
try:
run_policy_once()
except Exception as e:
log.exception("MODEL_POLICY: Exception in run_policy()", e)
if time.time() - start < 1:
time.sleep(1)
def run_policy_once():
from core.models import (
Instance,
Slice,
Controller,
Network,
User,
SlicePrivilege,
Site,
SitePrivilege,
Image,
ControllerSlice,
ControllerUser,
ControllerSite,
)
models = [
Controller,
Site,
SitePrivilege,
Image,
ControllerSlice,
ControllerSite,
ControllerUser,
User,
Slice,
Network,
Instance,
SlicePrivilege,
Privilege,
]
objects = []
deleted_objects = []
check_db_connection_okay()
for m in models:
res = m.objects.filter(
(Q(policed__lt=F("updated")) | Q(policed=None)) & Q(no_policy=False)
)
objects.extend(res)
res = m.deleted_objects.filter(Q(policed__lt=F("updated")) | Q(policed=None))
deleted_objects.extend(res)
for o in objects:
execute_model_policy(o, o.deleted)
for o in deleted_objects:
execute_model_policy(o, True)
try:
reset_queries()
except Exception as e:
# this shouldn't happen, but in case it does, catch it...
log.exception("MODEL POLICY: exception in reset_queries", e=e)