resilient-web-takeover-cgi-script.py.in 3.81 KB
Newer Older
1
#!${buildout:executable}
2

3
from __future__ import print_function
4
equeue_database = '${equeue:database}'
5
equeue_lockfile = '${equeue:lockfile}'
6
takeover_script = '${resiliency-takeover-script:wrapper-takeover}'
7

8
import atexit
9 10
import cgi
import cgitb
11
import datetime
12 13 14 15 16
try:
  import dbm.gnu as gdbm
except ImportError:
  import gdbm

17
import os
18
import shutil
19 20
import subprocess
import sys
21
import tempfile
22 23 24 25 26 27

if os.path.exists('resilient_software_release_information.py'):
  from resilient_software_release_information import main as resilient_main
else:
  resilient_main = lambda: {}

28 29
cgitb.enable()

30 31 32 33
def deleteTemporaryDirectory(path):
  if os.path.exists(path):
    shutil.rmtree(path)

34 35 36 37 38 39
def getLatestBackupDate():
  """
  Get the date of the latest successful backup.
  """
  # Create a copy of the db (locked by equeue process)
  temporary_directory = tempfile.mkdtemp()
40
  atexit.register(deleteTemporaryDirectory, temporary_directory)
41 42 43
  equeue_database_copy = os.path.join(temporary_directory, 'equeue.db')
  shutil.copyfile(equeue_database, equeue_database_copy)
  db = gdbm.open(equeue_database_copy)
44 45 46
  # Usually, there is only one callback (so only one key in the db), but if
  # there are several we take the "newest" one.  Indeed, sometimes the importer
  # script change name those introducing a new key inside the db.
47 48
  db_keys = db.keys()
  if not db_keys:
49 50
    result = False
  else:
51
    last_backup = db[db_keys[0]]
52
    for callback in db_keys:
53
      timestamp = float(db[callback])
54
      if timestamp > last_backup:
55
        last_backup = timestamp
Thomas Gambier's avatar
Thomas Gambier committed
56
    result = datetime.datetime.fromtimestamp(float(last_backup))
57 58 59
  db.close()
  shutil.rmtree(temporary_directory)
  return result
60

61 62 63 64 65 66 67 68
def isBackupInProgress():
  """
  Check if backup is in progress (importer script is running)
  by checking if equeue lockfile exists.
  """
  # XXX: check if file is valid
  return os.path.exists(equeue_lockfile)

69 70 71 72 73 74 75 76 77 78 79 80 81
def getInformationFromSoftwareRelease():
  result = resilient_main()
  if isinstance(result, dict):
    return result
  else:
    return {'Custom Information': 'Error, received information is malformed'}

def getSoftwareReleaseInformationFormatted():
  result_string = ""
  for key, value in getInformationFromSoftwareRelease().items():
    result_string += "<p><b>%s:</b> %s</p>" % (key, value)
  return result_string

82 83 84 85 86 87
latest_backup_date = getLatestBackupDate()
if latest_backup_date == False:
  latest_backup_message = "No backup downloaded yet, takeover should not happen now."
else:
  latest_backup_message = latest_backup_date.strftime('%Y-%m-%d %H:%M:%S')

88
print("Content-Type: text/html\n")
89 90 91

form = cgi.FieldStorage()
if "password" not in form:
92
  print("""<html>
93 94 95
<body>
  <h1>This is takeover web interface.</h1>
  <p>Calling takeover will stop and freeze the current main instance, and make this clone instance the new main instance, replacing the old one.</p>
96
  <p><font size=\"+2\"><b>Warning: submit the form only if you understand what you are doing.</b></font></p>
97
  <p>Note: the password asked here can be found within the parameters of your SlapOS instance page.</p>
98 99 100
  <hr />
  <p><b>Last valid backup:</b> %s</p>
  <p><b>Importer script(s) of backup in progress:</b> %s</p>
101
  <p><b>Backup Signature:</b> <a href='${resilient-web-takeover-cgi-script:proof-signature-url}'>${resilient-web-takeover-cgi-script:proof-signature-url}</a></b></p>
102
  %s
103 104 105 106 107
  <form action="/">
    Password: <input type="text" name="password">
    <input type="submit" value="Take over" style="background: red;">
  </form>
</body>
108
</html>""" % (latest_backup_message, isBackupInProgress(), getSoftwareReleaseInformationFormatted()))
109 110 111
  sys.exit(0)

if form['password'].value != '${:password}':
112 113
  print("<H1>Error</H1>")
  print("Password is invalid.")
114 115 116
  sys.exit(1)

# XXX hardcoded location
117
result = subprocess.check_output([takeover_script], stderr=subprocess.STDOUT)
118 119
print('Success.')
print('<pre>%s</pre>' % result)