3 from datetime import datetime
4 from planetstack.config import Config
5 from util.logger import Logger, logging
6 from observer.steps import *
7 from django.db.models import F, Q
9 logger = Logger(level=logging.INFO)
11 class FailedDependency(Exception):
15 """ A PlanetStack Sync step.
18 psmodel Model name the step synchronizes
19 dependencies list of names of models that must be synchronized first if the current model depends on them
24 sync_config_dir = Config().sync_config_dir
26 sync_config_dir = '/etc/planetstack/sync'
27 prop_config_path = '/'.join(sync_config_dir,self.name,prop)
28 return open(prop_config_path).read().rstrip()
30 def __init__(self, **args):
31 """Initialize a sync step
33 name -- Name of the step
34 provides -- PlanetStack models sync'd by this step
37 self.driver = args.get('driver')
38 self.error_map = args.get('error_map')
41 self.soft_deadline = int(self.get_prop('soft_deadline_seconds'))
43 self.soft_deadline = 5 # 5 seconds
47 def fetch_pending(self, deletion=False):
48 # This is the most common implementation of fetch_pending
49 # Steps should override it if they have their own logic
50 # for figuring out what objects are outstanding.
51 main_obj = self.provides[0]
53 objs = main_obj.objects.filter(Q(enacted__lt=F('updated')) | Q(enacted=None))
55 objs = main_obj.deleted_objects.all()
58 #return Sliver.objects.filter(ip=None)
60 def check_dependencies(self, obj, failed):
61 for dep in self.dependencies:
62 peer_name = dep[0].lower() + dep[1:] # django names are camelCased with the first letter lower
64 peer_object = getattr(obj, peer_name)
68 if (peer_object and peer_object.pk==failed.pk and type(peer_object)==type(failed)):
69 if (obj.backend_status!=peer_object.backend_status):
70 obj.backend_status = peer_object.backend_status
71 obj.save(update_fields=['backend_status'])
72 raise FailedDependency("Failed dependency for %s:%s peer %s:%s failed %s:%s" % (obj.__class__.__name__, str(obj.pk), peer_object.__class__.__name__, str(peer_object.pk), failed.__class__.__name__, str(failed.pk)))
74 def call(self, failed=[], deletion=False):
75 pending = self.fetch_pending(deletion)
79 self.check_dependencies(o,f) # Raises exception if failed
85 o.enacted = datetime.now() # Is this the same timezone? XXX
86 o.backend_status = "OK"
87 o.save(update_fields=['enacted'])
89 logger.log_exc("sync step failed!")
92 o.backend_status = self.error_map.map(str_e)
94 o.backend_status = str_e
97 # DatabaseError: value too long for type character varying(140)
100 o.save(update_fields=['backend_status'])
102 print "Could not update backend status field!"
109 def __call__(self, **args):
110 return self.call(**args)