#!/opt/slapos/parts/python2.7/bin/python2.7 import os import sys import subprocess import time import getopt import sqlite3 import ssl import urllib2 from xml.dom import minidom import json import hashlib def fmt_date(): return time.strftime("%Y%m%d") # get all of the installed software types by checking the SR urls # return a list, and run routine on all of them def discover_software(): conn = sqlite3.connect("/opt/slapos/slapproxy.db") cur = conn.cursor() qry = cur.execute("SELECT DISTINCT url FROM software14") return [row[0] for row in qry if row[0]] def get_connection_information(software_release): conn = sqlite3.connect("/opt/slapos/slapproxy.db") cur = conn.cursor() if 'software/apache-frontend' not in software_release: partition_reference = 'instance-of-%' elif 'software/apache-frontend' in software_release: partition_reference = 'apache-frontend-1' else: raise ValueError qry = cur.execute("SELECT connection_xml FROM partition14 WHERE connection_xml IS NOT NULL AND software_release=? AND partition_reference LIKE ?", (software_release, partition_reference) ) xml = None for row in qry: xml = str(row[0]) break if xml is None: return (None, None) instance = minidom.parseString(xml) if partition_reference.startswith('instance-of-'): try: el = instance.getElementsByTagName('parameter')[0] value = el.childNodes[0].nodeValue except: return "error" # remove leading and trailing '"' character # from serialized partition information inside SQlite DB if value.startswith('"') and value.endswith('"'): value = value[1:-1].replace('\\"', '"') if not value.startswith("{"): value = "\"" + value + "\"" json_text = json.loads(value) if 'family-admin' in json_text: return (json_text['family-admin'], json_text['inituser-password']) elif 'insecure' in json_text: return (json_text, None) else: return (None, None) elif partition_reference == 'apache-frontend-1': ip = None for el in instance.getElementsByTagName('parameter'): if el.getAttribute('id') == 'private-ipv4': if len(el.childNodes) > 0: ip = str(el.childNodes[0].nodeValue) or None break return (ip, None) else: raise ValueError def check_tables(): conn = sqlite3.connect("/opt/slapos/slapproxy.db") cur = conn.cursor() qry = cur.execute("SELECT CASE WHEN tbl_name = 'partition14' THEN 1 ELSE 0 END FROM sqlite_master WHERE tbl_name = 'partition14' AND type = 'table'") if qry is None: print "tables aren't ready yet, your build may have failed, check logs in /opt/slapos/log/" sys.exit(0) def get_build_status(software_release): sr_hash = hashlib.md5(software_release).hexdigest() try: os.stat(os.path.join('/opt/slapgrid', sr_hash, '.completed')) except OSError: try: f = open("/opt/slapos/log/slapos-node-software-" + fmt_date() + ".log") except Exception: try: f = open("/opt/slapos/log/slapos-node-software.log") except Exception: return False lines = f.readlines() if "ERROR" in lines[-3]: return "error" return False return True # Check if the last two lines show the software finished building. # If an error came just before this, we'll report failure. # Otherwise it passed and we can move on. # We want to open today's log, as it is most up to date def status(): software_release_list = discover_software() software_release_list_status = [get_build_status(software_release) for software_release in software_release_list] if len(software_release_list) == 2 and \ software_release_list_status == [True, True]: build = True else: build = False if build: erp5_sr = [q for q in software_release_list if 'software/apache-frontend' not in q][0] frn_sr = [q for q in software_release_list if 'software/apache-frontend' in q][0] zope_ip, pw = get_connection_information(erp5_sr) try: # by default try new apporach (domain based)... hostname = open('/tmp/playbook-frontend-custom-domain').read().strip() except Exception: try: # ...and fall back to old one (IP based) hostname = open('/tmp/playbook-public-ipv4').read().strip() except Exception: frontend = None else: if len(hostname) == 0: frontend = zope_ip else: frontend = 'https://' + hostname frontend_ip, _ = get_connection_information(frn_sr) connected = False if frontend is not None: # as frontend is already up, check if all is ready to serve requests # before providing the access url to the user ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE try: r1 = urllib2.urlopen(frontend, context=ctx) if 'Zope Management Interface' in r1.read(): connected = True else: print 'URL %s ready, but does not reply with Zope' % frontend except urllib2.URLError, e: print "Zope not available yet: %s" % e if zope_ip is None or frontend is None or frontend_ip is None or not connected: print "Build successful, please wait for instantiation" sys.exit(2) print ("Build successful, connect to:\n" " " + frontend) if pw is not None: print (" with\n" " username: zope password: " + pw) elif not build: if 'error' in [software_release_list_status]: print "An error occurred while building, check /opt/slapos/log/slapos-node-software-" + \ fmt_date() + ".log for details" sys.exit(2) else: print "Your software is still building, be patient it can take a while" sys.exit(2) ipv6 = None # check if the services are actually running (run slapos node and parse output) if pw is None: zope_ip = "https://" + zope_ip[zope_ip.index("@")+1:] original_zope_ip = zope_ip if "[" in zope_ip and "]" in zope_ip: ipv6 = zope_ip[zope_ip.index("[")+1:zope_ip.index("]")] with open("/etc/hosts", "ra+") as f: if " erp5-instance" not in f.read(): f.write("\n%s erp5-instance\n" % ipv6) zope_ip = zope_ip.replace("[" + ipv6 + "]", "erp5-instance") ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE try: r1 = urllib2.urlopen(zope_ip, context=ctx) except urllib2.URLError, e: print "At least one of your services isn't running! Check with slapos node" print "restart a service with slapos node restart slappart:service" print "" print "DEBUG information: %s" % e sys.exit(2) if r1.getcode() != 200: print "At least one of your services isn't running! Check with slapos node" print "restart a service with slapos node restart slappart:service" sys.exit(2) if ipv6: print "" print "The URL above may require extra configuration if you want to access it" print "from another machine. You can install an apache locally and include the" print "the follow rewrite rule (http version):" print """ RewriteRule ^/(.*) %s/VirtualHostBase/http/%%{HTTP_HOST}/VirtualHostRoot/$1 [L,P] or (https version): RewriteRule ^/(.*) %s/VirtualHostBase/https/%%{HTTP_HOST}/VirtualHostRoot/$1 [L,P] """ % (original_zope_ip, original_zope_ip) def info(software_release): if get_build_status(software_release): print get_connection_information(software_release) else: print "Information unavailable at this time, run " + sys.argv[0] + " -s for details" def usage(): print ("Get the status and information of your ERP5 build\n" "Usage:") print (" --help (-h): Print this message and exit\n" " --status (-s): Print the status of your ERP5 build\n" " --info (-i): Print the technical information about partition tables\n" " --dump (-d): Dump the entire database (alias for slapos proxy show)\n") def dump(): subprocess.call(["slapos", "proxy", "show", "-u", "/opt/slapos/slapproxy.db"]) def main(argv): # parse command line options try: opts, args = getopt.getopt(argv, "sihd", ["status", "info", "help", "dump"]) except getopt.error, msg: usage() sys.exit(2) if len(opts) == 0: usage() sys.exit(2) # process arguments for opt, arg in opts: if opt in ("-h", "--help"): usage() sys.exit() elif opt in ("-s", "--status"): check_tables() status() elif opt in ("-i", "--info"): check_tables() for sr in discover_software(): info(sr) elif opt in ("-d", "--dump"): dump() if __name__ == "__main__": main(sys.argv[1:])