cron_task.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. #!/usr/bin/env python2
  2. import signal
  3. import traceback
  4. from sys import stderr
  5. from datetime import datetime, timedelta
  6. from flask.ext.mail import Message
  7. from flask import url_for
  8. import itsdangerous
  9. from ffdnispdb.crawler import TextValidator
  10. from ffdnispdb.models import ISP
  11. from ffdnispdb import create_app, db, mail, utils
  12. app=create_app({
  13. 'SERVER_NAME': 'db.ffdn.org',
  14. })
  15. MAX_RUNTIME=15*60
  16. class Timeout(Exception):
  17. pass
  18. class ScriptTimeout(Exception):
  19. """
  20. Script exceeded its allowed run time
  21. """
  22. strike=1
  23. last_isp=-1
  24. script_begin=datetime.now()
  25. def timeout_handler(signum, frame):
  26. global last_isp, strike
  27. if script_begin < datetime.now()-timedelta(seconds=MAX_RUNTIME):
  28. raise ScriptTimeout
  29. if last_isp == isp.id:
  30. strike += 1
  31. if strike > 2:
  32. signal.alarm(6)
  33. raise Timeout
  34. else:
  35. last_isp = isp.id
  36. strike = 1
  37. signal.alarm(6)
  38. signal.signal(signal.SIGALRM, timeout_handler)
  39. signal.alarm(6)
  40. def gen_reactivate_key(isp):
  41. s=itsdangerous.URLSafeSerializer(app.secret_key,
  42. salt='reactivate')
  43. return s.dumps([
  44. isp.id,
  45. str(isp.last_update_attempt)
  46. ])
  47. def send_warning_email(isp, debug_msg):
  48. msg=Message(u"Problem while updating your ISP's data", sender=app.config['EMAIL_SENDER'])
  49. msg.body = """
  50. Hello,
  51. You are receiving this message because your are listed as technical contact for "%s" on the FFDN ISP database.
  52. Our automatic update script could not access or process your ISP's data located at %s.
  53. Automatic updates of your ISP were disabled until you fix the problem.
  54. Here is some debug output to help you locate the issue:
  55. %s
  56. ---
  57. When the issue is resolved, please click on the link below to reactivate automatic updates on your ISP:
  58. %s?key=%s
  59. Thanks,
  60. The FFDN ISP Database team
  61. https://db.ffdn.org
  62. """.strip()%(isp.complete_name, isp.json_url, debug_msg.strip(),
  63. url_for('ispdb.reactivate_isp', projectid=isp.id), gen_reactivate_key(isp))
  64. msg.add_recipient(isp.tech_email)
  65. print u' Sending notification email to %s'%(isp.tech_email)
  66. mail.send(msg)
  67. app.app_context().push()
  68. try:
  69. for isp in ISP.query.filter(ISP.is_disabled == False,
  70. ISP.json_url != None,
  71. ISP.next_update < utils.utcnow(),
  72. ISP.update_error_strike < 3)\
  73. .order_by(ISP.last_update_success):
  74. try:
  75. print u'%s: Attempting to update %s'%(datetime.now(), isp)
  76. print u' last successful update=%s'%(utils.tosystemtz(isp.last_update_success))
  77. print u' last update attempt=%s'%(utils.tosystemtz(isp.last_update_attempt))
  78. print u' next update was scheduled %s ago'%(utils.utcnow()-isp.next_update)
  79. print u' strike=%d'%(isp.update_error_strike)
  80. isp.last_update_attempt=utils.utcnow()
  81. db.session.add(isp)
  82. db.session.commit()
  83. validator=TextValidator()
  84. log = ''
  85. exc, exc_trace = None, None
  86. try:
  87. for l in validator(isp.json_url, isp.cache_info or {}):
  88. log += l
  89. except Exception as e:
  90. exc = e
  91. exc_trace = traceback.format_exc()
  92. if not validator.success: # handle error
  93. isp.update_error_strike += 1
  94. # reset cache info (to force refetch next time)
  95. isp.cache_info = {}
  96. isp.next_update = utils.utcnow()+timedelta(seconds=validator.jdict_max_age)
  97. db.session.add(isp)
  98. db.session.commit()
  99. print u'%s: Error while updating:'%(datetime.now())
  100. if isp.update_error_strike >= 3:
  101. print u' three strikes, you\'re out'
  102. send_warning_email(isp, log)
  103. print log.rstrip().encode('utf-8')+'\n'
  104. if exc:
  105. print u'Unexpected exception in the validator: %r' % exc
  106. print exc_trace
  107. continue
  108. if validator.modified:
  109. isp.json = validator.jdict
  110. isp.cache_info = validator.cache_info
  111. isp.last_update_success = isp.last_update_attempt
  112. isp.update_error_strike = 0
  113. isp.next_update = utils.utcnow()+timedelta(seconds=validator.jdict_max_age)
  114. db.session.add(isp)
  115. db.session.commit()
  116. print u'%s: Update successful !'%(datetime.now())
  117. print u' next update is scheduled for %s\n'%(isp.next_update)
  118. except Timeout:
  119. print u'%s: Timeout while updating:'%(datetime.now())
  120. isp=ISP.query.get(isp.id)
  121. isp.update_error_strike += 1
  122. db.session.add(isp)
  123. db.session.commit()
  124. if isp.update_error_strike >= 3:
  125. send_warning_email(isp, 'Your ISP took more then 18 seconds to process. '
  126. 'Having problems with your webserver ?')
  127. print u' three strikes, you\'re out'
  128. print traceback.format_exc()
  129. except ScriptTimeout:
  130. pass
  131. except Timeout:
  132. pass