cron_task.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  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('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=''.join(validator(isp.json_url, isp.cache_info or {}))
  85. if not validator.success: # handle error
  86. isp.update_error_strike += 1
  87. # reset cache info (to force refetch next time)
  88. isp.cache_info = {}
  89. isp.next_update = utils.utcnow()+timedelta(seconds=validator.jdict_max_age)
  90. db.session.add(isp)
  91. db.session.commit()
  92. print u'%s: Error while updating:'%(datetime.now())
  93. if isp.update_error_strike >= 3:
  94. print u' three strikes, you\'re out'
  95. send_warning_email(isp, log)
  96. print log.rstrip()+'\n'
  97. continue
  98. if validator.modified:
  99. isp.json = validator.jdict
  100. isp.cache_info = validator.cache_info
  101. isp.last_update_success = isp.last_update_attempt
  102. isp.update_error_strike = 0
  103. isp.next_update = utils.utcnow()+timedelta(seconds=validator.jdict_max_age)
  104. db.session.add(isp)
  105. db.session.commit()
  106. print u'%s: Update successful !'%(datetime.now())
  107. print u' next update is scheduled for %s\n'%(isp.next_update)
  108. except Timeout:
  109. print u'%s: Timeout while updating:'%(datetime.now())
  110. isp=ISP.query.get(isp.id)
  111. isp.update_error_strike += 1
  112. db.session.add(isp)
  113. db.session.commit()
  114. if isp.update_error_strike >= 3:
  115. send_warning_email(isp, 'Your ISP took more then 18 seconds to process. '
  116. 'Having problems with your webserver ?')
  117. print u' three strikes, you\'re out'
  118. print traceback.format_exc()
  119. except ScriptTimeout:
  120. pass
  121. except Timeout:
  122. pass