735 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			735 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
from __future__ import print_function
 | 
						|
 | 
						|
import os
 | 
						|
import shutil
 | 
						|
import shlex
 | 
						|
import sys
 | 
						|
import subprocess
 | 
						|
import tempfile
 | 
						|
from seiscomp import config, kernel, system
 | 
						|
 | 
						|
# Python version depended string conversion
 | 
						|
if sys.version_info[0] < 3:
 | 
						|
    py3bstr = str
 | 
						|
    py3ustr = str
 | 
						|
 | 
						|
else:
 | 
						|
    py3bstr = lambda s: s.encode("utf-8")
 | 
						|
    py3ustr = lambda s: s.decode("utf-8", "replace")
 | 
						|
 | 
						|
 | 
						|
class DBParams:
 | 
						|
    def __init__(self):
 | 
						|
        self.db = None
 | 
						|
        self.rwuser = None
 | 
						|
        self.rwpwd = None
 | 
						|
        self.rouser = None
 | 
						|
        self.ropwd = None
 | 
						|
        self.rohost = None
 | 
						|
        self.rwhost = None
 | 
						|
        self.drop = False
 | 
						|
        self.create = False
 | 
						|
 | 
						|
 | 
						|
def check_output(cmd):
 | 
						|
    proc = subprocess.Popen(
 | 
						|
        cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True
 | 
						|
    )
 | 
						|
    out = proc.communicate()
 | 
						|
    return [py3ustr(out[0]), py3ustr(out[1]), proc.returncode]
 | 
						|
 | 
						|
 | 
						|
def addEntry(cfg, param, item):
 | 
						|
    # Adds an item to a parameter list
 | 
						|
    try:
 | 
						|
        items = cfg.getStrings(param)
 | 
						|
        if len(items) == 1 and items[0] == "":
 | 
						|
            items.clear()
 | 
						|
    except ValueError:
 | 
						|
        items = config.VectorStr()
 | 
						|
 | 
						|
    if item not in items:
 | 
						|
        items.push_back(item)
 | 
						|
    cfg.setStrings(param, items)
 | 
						|
 | 
						|
 | 
						|
def removeEntry(cfg, param, item):
 | 
						|
    # Removes an items from a parameter list
 | 
						|
    try:
 | 
						|
        items = cfg.getStrings(param)
 | 
						|
        it = items.begin()
 | 
						|
        while it != items.end():
 | 
						|
            if it.value() == item:
 | 
						|
                items.erase(it)
 | 
						|
                cfg.setStrings(param, items)
 | 
						|
                break
 | 
						|
            it.next()
 | 
						|
    except ValueError:
 | 
						|
        # No parameter set, nothing to do
 | 
						|
        pass
 | 
						|
 | 
						|
 | 
						|
# The kernel module which starts scmaster if enabled
 | 
						|
class Module(kernel.CoreModule):
 | 
						|
    def __init__(self, env):
 | 
						|
        kernel.CoreModule.__init__(self, env, env.moduleName(__file__))
 | 
						|
        # High priority
 | 
						|
        self.order = -1
 | 
						|
 | 
						|
        # Default values
 | 
						|
        self.messaging = True
 | 
						|
        self.messagingBind = None
 | 
						|
 | 
						|
        try:
 | 
						|
            self.messaging = self.env.getBool("messaging.enable")
 | 
						|
        except ValueError:
 | 
						|
            pass
 | 
						|
        try:
 | 
						|
            self.messagingBind = self.env.getString("messaging.bind")
 | 
						|
        except ValueError:
 | 
						|
            pass
 | 
						|
 | 
						|
    # Add master port
 | 
						|
    def _get_start_params(self):
 | 
						|
        if self.messagingBind:
 | 
						|
            return (
 | 
						|
                kernel.Module._get_start_params(self)
 | 
						|
                + " --bind %s" % self.messagingBind
 | 
						|
            )
 | 
						|
 | 
						|
        return kernel.Module._get_start_params(self)
 | 
						|
 | 
						|
    def start(self):
 | 
						|
        if not self.messaging:
 | 
						|
            print(
 | 
						|
                "[kernel] {} is disabled by config".format(self.name), file=sys.stderr
 | 
						|
            )
 | 
						|
            return 1
 | 
						|
 | 
						|
        appConfig = system.Environment.Instance().appConfigFileName(self.name)
 | 
						|
        localConfig = system.Environment.Instance().configFileName(self.name)
 | 
						|
        lockFile = os.path.join(self.env.SEISCOMP_ROOT, self.env.lockFile(self.name))
 | 
						|
        try:
 | 
						|
            needRestart = False
 | 
						|
            started = os.path.getmtime(lockFile)
 | 
						|
            try:
 | 
						|
                needRestart = started < os.path.getmtime(appConfig)
 | 
						|
            except Exception:
 | 
						|
                pass
 | 
						|
            try:
 | 
						|
                needRestart = started < os.path.getmtime(localConfig)
 | 
						|
            except Exception:
 | 
						|
                pass
 | 
						|
 | 
						|
            if needRestart:
 | 
						|
                self.stop()
 | 
						|
        except Exception:
 | 
						|
            pass
 | 
						|
 | 
						|
        return kernel.CoreModule.start(self)
 | 
						|
 | 
						|
    def check(self):
 | 
						|
        if not self.messaging:
 | 
						|
            print(
 | 
						|
                "[kernel] {} is disabled by config".format(self.name), file=sys.stderr
 | 
						|
            )
 | 
						|
            return 0
 | 
						|
 | 
						|
        return kernel.CoreModule.check(self)
 | 
						|
 | 
						|
    def status(self, shouldRun):
 | 
						|
        if not self.messaging:
 | 
						|
            shouldRun = False
 | 
						|
        return kernel.CoreModule.status(self, shouldRun)
 | 
						|
 | 
						|
    def readDBParams(self, params, setup_config):
 | 
						|
        try:
 | 
						|
            params.db = setup_config.getString(
 | 
						|
                self.name + ".database.enable.backend.db"
 | 
						|
            )
 | 
						|
        except ValueError as err:
 | 
						|
            print(err)
 | 
						|
            print("  - database name not set, ignoring setup", file=sys.stderr)
 | 
						|
            return False
 | 
						|
 | 
						|
        try:
 | 
						|
            params.rwhost = setup_config.getString(
 | 
						|
                self.name + ".database.enable.backend.rwhost"
 | 
						|
            )
 | 
						|
        except ValueError:
 | 
						|
            print("  - database host (rw) not set, ignoring setup", file=sys.stderr)
 | 
						|
            return False
 | 
						|
 | 
						|
        try:
 | 
						|
            params.rwuser = setup_config.getString(
 | 
						|
                self.name + ".database.enable.backend.rwuser"
 | 
						|
            )
 | 
						|
        except ValueError:
 | 
						|
            print("  - database user (rw) not set, ignoring setup", file=sys.stderr)
 | 
						|
            return False
 | 
						|
 | 
						|
        try:
 | 
						|
            params.rwpwd = setup_config.getString(
 | 
						|
                self.name + ".database.enable.backend.rwpwd"
 | 
						|
            )
 | 
						|
        except ValueError:
 | 
						|
            print("  - database password (rw) not set, ignoring setup", file=sys.stderr)
 | 
						|
            return False
 | 
						|
 | 
						|
        try:
 | 
						|
            params.rohost = setup_config.getString(
 | 
						|
                self.name + ".database.enable.backend.rohost"
 | 
						|
            )
 | 
						|
        except ValueError:
 | 
						|
            print("  - database host (ro) not set, ignoring setup", file=sys.stderr)
 | 
						|
            return False
 | 
						|
 | 
						|
        try:
 | 
						|
            params.rouser = setup_config.getString(
 | 
						|
                self.name + ".database.enable.backend.rouser"
 | 
						|
            )
 | 
						|
        except ValueError:
 | 
						|
            print("  - database user (ro) not set, ignoring setup", file=sys.stderr)
 | 
						|
            return False
 | 
						|
 | 
						|
        try:
 | 
						|
            params.ropwd = setup_config.getString(
 | 
						|
                self.name + ".database.enable.backend.ropwd"
 | 
						|
            )
 | 
						|
        except ValueError:
 | 
						|
            print("  - database password (ro) not set, ignoring setup", file=sys.stderr)
 | 
						|
            return False
 | 
						|
 | 
						|
        try:
 | 
						|
            params.create = setup_config.getBool(
 | 
						|
                self.name + ".database.enable.backend.create"
 | 
						|
            )
 | 
						|
        except ValueError:
 | 
						|
            params.create = False
 | 
						|
 | 
						|
        try:
 | 
						|
            params.drop = setup_config.getBool(
 | 
						|
                self.name + ".database.enable.backend.create.drop"
 | 
						|
            )
 | 
						|
        except ValueError:
 | 
						|
            params.drop = False
 | 
						|
 | 
						|
        return True
 | 
						|
 | 
						|
    def setup(self, setup_config):
 | 
						|
        schemapath = os.path.join(self.env.SEISCOMP_ROOT, "share", "db")
 | 
						|
 | 
						|
        cfg = config.Config()
 | 
						|
        system.Environment.Instance().initConfig(cfg, self.name)
 | 
						|
 | 
						|
        try:
 | 
						|
            dbenable = setup_config.getBool(self.name + ".database.enable")
 | 
						|
        except ValueError:
 | 
						|
            print("  - database.enable not set, ignoring setup", file=sys.stderr)
 | 
						|
            return 0
 | 
						|
 | 
						|
        dbBackend = None
 | 
						|
 | 
						|
        if not dbenable:
 | 
						|
            removeEntry(cfg, "queues.production.plugins", "dbstore")
 | 
						|
            removeEntry(cfg, "queues.production.processors.messages", "dbstore")
 | 
						|
            cfg.remove("queues.production.processors.messages.dbstore.driver")
 | 
						|
            cfg.remove("queues.production.processors.messages.dbstore.read")
 | 
						|
            cfg.remove("queues.production.processors.messages.dbstore.write")
 | 
						|
        else:
 | 
						|
            try:
 | 
						|
                dbBackend = setup_config.getString(
 | 
						|
                    self.name + ".database.enable.backend"
 | 
						|
                )
 | 
						|
            except ValueError:
 | 
						|
                print("  - database backend not set, ignoring setup", file=sys.stderr)
 | 
						|
                return 1
 | 
						|
 | 
						|
            if dbBackend == "mysql/mariadb":
 | 
						|
                dbBackend = "mysql"
 | 
						|
                try:
 | 
						|
                    rootpwd = setup_config.getString(
 | 
						|
                        self.name + ".database.enable.backend.create.rootpw"
 | 
						|
                    )
 | 
						|
                except ValueError:
 | 
						|
                    rootpwd = ""
 | 
						|
 | 
						|
                try:
 | 
						|
                    runAsSuperUser = setup_config.getBool(
 | 
						|
                        self.name + ".database.enable.backend.create.runAsSuperUser"
 | 
						|
                    )
 | 
						|
                except ValueError:
 | 
						|
                    runAsSuperUser = False
 | 
						|
 | 
						|
                try:
 | 
						|
                    characterSet = setup_config.getString(
 | 
						|
                        self.name + ".database.enable.backend.create.characterset"
 | 
						|
                    )
 | 
						|
                except ValueError:
 | 
						|
                    characterSet = None
 | 
						|
 | 
						|
                params = DBParams()
 | 
						|
                if not self.readDBParams(params, setup_config):
 | 
						|
                    return 1
 | 
						|
 | 
						|
                cfg.setString(
 | 
						|
                    "queues.production.processors.messages.dbstore.read",
 | 
						|
                    "{}:{}@{}/{}".format(
 | 
						|
                        params.rouser, params.ropwd, params.rohost, params.db
 | 
						|
                    ),
 | 
						|
                )
 | 
						|
                cfg.setString(
 | 
						|
                    "queues.production.processors.messages.dbstore.write",
 | 
						|
                    "{}:{}@{}/{}".format(
 | 
						|
                        params.rwuser, params.rwpwd, params.rwhost, params.db
 | 
						|
                    ),
 | 
						|
                )
 | 
						|
 | 
						|
                if params.create:
 | 
						|
                    dbScript = os.path.join(schemapath, "mysql_setup.py")
 | 
						|
                    options = [
 | 
						|
                        params.db,
 | 
						|
                        params.rwuser,
 | 
						|
                        params.rwpwd,
 | 
						|
                        params.rouser,
 | 
						|
                        params.ropwd,
 | 
						|
                        params.rwhost,
 | 
						|
                        rootpwd,
 | 
						|
                        str(params.drop),
 | 
						|
                        schemapath,
 | 
						|
                    ]
 | 
						|
                    if characterSet is not None:
 | 
						|
                        options.append(characterSet)
 | 
						|
 | 
						|
                    binary = os.path.join(schemapath, "pkexec_wrapper.sh")
 | 
						|
                    print(
 | 
						|
                        "+ Running MySQL database setup script {}".format(dbScript),
 | 
						|
                        file=sys.stderr,
 | 
						|
                    )
 | 
						|
                    if runAsSuperUser:
 | 
						|
                        cmd = "{} seiscomp-python {} {}".format(
 | 
						|
                            binary, dbScript, " ".join(shlex.quote(o) for o in options)
 | 
						|
                        )
 | 
						|
                    else:
 | 
						|
                        cmd = "{} {}".format(dbScript, " ".join(shlex.quote(o) for o in options))
 | 
						|
 | 
						|
                    p = subprocess.Popen(cmd, shell=True)
 | 
						|
                    ret = p.wait()
 | 
						|
                    if ret != 0:
 | 
						|
                        print("  - Failed to setup database", file=sys.stderr)
 | 
						|
                        return 1
 | 
						|
 | 
						|
            elif dbBackend == "postgresql":
 | 
						|
                dbBackend = "postgresql"
 | 
						|
 | 
						|
                params = DBParams()
 | 
						|
                if not self.readDBParams(params, setup_config):
 | 
						|
                    return 1
 | 
						|
 | 
						|
                cfg.setString(
 | 
						|
                    "queues.production.processors.messages.dbstore.read",
 | 
						|
                    "{}:{}@{}/{}".format(
 | 
						|
                        params.rouser, params.ropwd, params.rohost, params.db
 | 
						|
                    ),
 | 
						|
                )
 | 
						|
                cfg.setString(
 | 
						|
                    "queues.production.processors.messages.dbstore.write",
 | 
						|
                    "{}:{}@{}/{}".format(
 | 
						|
                        params.rwuser, params.rwpwd, params.rwhost, params.db
 | 
						|
                    ),
 | 
						|
                )
 | 
						|
 | 
						|
                if params.create:
 | 
						|
                    try:
 | 
						|
                        tmpPath = tempfile.mkdtemp()
 | 
						|
                        os.chmod(tmpPath, 0o755)
 | 
						|
                        tmpPath = os.path.join(tmpPath, "setup")
 | 
						|
                        try:
 | 
						|
                            shutil.copytree(schemapath, tmpPath)
 | 
						|
                            filename = os.path.join(
 | 
						|
                                self.env.SEISCOMP_ROOT, "bin", "seiscomp-python"
 | 
						|
                            )
 | 
						|
                            shutil.copy(filename, tmpPath)
 | 
						|
                        except Exception as err:
 | 
						|
                            print(err)
 | 
						|
                            return 1
 | 
						|
 | 
						|
                        dbScript = os.path.join(tmpPath, "postgres_setup.py")
 | 
						|
                        options = [
 | 
						|
                            params.db,
 | 
						|
                            params.rwuser,
 | 
						|
                            params.rwpwd,
 | 
						|
                            params.rouser,
 | 
						|
                            params.ropwd,
 | 
						|
                            params.rwhost,
 | 
						|
                            str(params.drop),
 | 
						|
                            tmpPath,
 | 
						|
                        ]
 | 
						|
 | 
						|
                        binary = os.path.join(schemapath, "pkexec_wrapper.sh")
 | 
						|
                        print(
 | 
						|
                            "+ Running PostgreSQL database setup script {}".format(
 | 
						|
                                dbScript
 | 
						|
                            ),
 | 
						|
                            file=sys.stderr,
 | 
						|
                        )
 | 
						|
                        cmd = '{} su postgres -c "{}/seiscomp-python {} {}"'.format(
 | 
						|
                            binary, tmpPath, dbScript, " ".join(shlex.quote(o) for o in options)
 | 
						|
                        )
 | 
						|
 | 
						|
                        p = subprocess.Popen(cmd, shell=True)
 | 
						|
                        ret = p.wait()
 | 
						|
                        if ret != 0:
 | 
						|
                            print("  - Failed to setup database", file=sys.stderr)
 | 
						|
                            return 1
 | 
						|
                    finally:
 | 
						|
                        try:
 | 
						|
                            shutil.rmtree(tmpPath)
 | 
						|
                        except OSError:
 | 
						|
                            pass
 | 
						|
 | 
						|
            elif dbBackend == "sqlite3":
 | 
						|
                dbBackend = "sqlite3"
 | 
						|
                dbScript = os.path.join(schemapath, "sqlite3_setup.py")
 | 
						|
 | 
						|
                try:
 | 
						|
                    create = setup_config.getBool(
 | 
						|
                        self.name + ".database.enable.backend.create"
 | 
						|
                    )
 | 
						|
                except BaseException:
 | 
						|
                    create = False
 | 
						|
 | 
						|
                try:
 | 
						|
                    filename = setup_config.getString(
 | 
						|
                        self.name + ".database.enable.backend.filename"
 | 
						|
                    )
 | 
						|
                    filename = system.Environment.Instance().absolutePath(filename)
 | 
						|
                except BaseException:
 | 
						|
                    filename = os.path.join(
 | 
						|
                        self.env.SEISCOMP_ROOT, "var", "lib", "seiscomp.db"
 | 
						|
                    )
 | 
						|
 | 
						|
                if not filename:
 | 
						|
                    print("  - location not set, ignoring setup", file=sys.stderr)
 | 
						|
                    return 1
 | 
						|
 | 
						|
                try:
 | 
						|
                    override = setup_config.getBool(
 | 
						|
                        self.name + ".database.enable.backend.create.override"
 | 
						|
                    )
 | 
						|
                except BaseException:
 | 
						|
                    override = False
 | 
						|
 | 
						|
                options = [filename, schemapath]
 | 
						|
 | 
						|
                if create:
 | 
						|
                    print(
 | 
						|
                        "+ Running SQLite3 database setup script {}".format(dbScript),
 | 
						|
                        file=sys.stderr,
 | 
						|
                    )
 | 
						|
                    cmd = "seiscomp-python {} {} {}".format(
 | 
						|
                        dbScript, " ".join(shlex.quote(o) for o in options), override
 | 
						|
                    )
 | 
						|
                    p = subprocess.Popen(cmd, shell=True)
 | 
						|
                    ret = p.wait()
 | 
						|
                    if ret != 0:
 | 
						|
                        print("  - Failed to setup database", file=sys.stderr)
 | 
						|
                        return 1
 | 
						|
 | 
						|
                cfg.setString(
 | 
						|
                    "queues.production.processors.messages.dbstore.read", filename
 | 
						|
                )
 | 
						|
                cfg.setString(
 | 
						|
                    "queues.production.processors.messages.dbstore.write", filename
 | 
						|
                )
 | 
						|
 | 
						|
            # Configure db backend for scmaster
 | 
						|
            cfg.setString("core.plugins", "db" + dbBackend)
 | 
						|
            cfg.setString(
 | 
						|
                "queues.production.processors.messages.dbstore.driver", dbBackend
 | 
						|
            )
 | 
						|
 | 
						|
            addEntry(cfg, "queues.production.plugins", "dbstore")
 | 
						|
            addEntry(cfg, "queues.production.processors.messages", "dbstore")
 | 
						|
 | 
						|
        cfg.writeConfig(
 | 
						|
            system.Environment.Instance().configFileLocation(
 | 
						|
                self.name, system.Environment.CS_CONFIG_APP
 | 
						|
            )
 | 
						|
        )
 | 
						|
 | 
						|
        # Now we need to insert the corresponding plugin to etc/global.cfg
 | 
						|
        # that all connected local clients can handle the database backend
 | 
						|
        if dbBackend:
 | 
						|
            cfgfile = os.path.join(self.env.SEISCOMP_ROOT, "etc", "global.cfg")
 | 
						|
            cfg = config.Config()
 | 
						|
            cfg.readConfig(cfgfile)
 | 
						|
            cfg.setString("core.plugins", "db" + dbBackend)
 | 
						|
            cfg.writeConfig()
 | 
						|
 | 
						|
        return 0
 | 
						|
 | 
						|
    def updateConfig(self):
 | 
						|
        cfg = config.Config()
 | 
						|
        system.Environment.Instance().initConfig(cfg, self.name)
 | 
						|
 | 
						|
        try:
 | 
						|
            queues = cfg.getStrings("queues")
 | 
						|
        except ValueError:
 | 
						|
            queues = []
 | 
						|
 | 
						|
        # iterate through all queues and check DB schema version if message
 | 
						|
        # processor dbstore is present
 | 
						|
        for queue in queues:
 | 
						|
            print("INFO: Checking queue '{}'".format(queue), file=sys.stderr)
 | 
						|
            try:
 | 
						|
                msgProcs = cfg.getStrings("queues.{}.processors.messages".format(queue))
 | 
						|
                if "dbstore" in msgProcs and not self.checkDBStore(cfg, queue):
 | 
						|
                    return 1
 | 
						|
            except ValueError:
 | 
						|
                print("  * ignoring - no database backend configured", file=sys.stderr)
 | 
						|
 | 
						|
        return 0
 | 
						|
 | 
						|
    def checkDBStore(self, cfg, queue):
 | 
						|
        prefix = "queues.{}.processors.messages.dbstore".format(queue)
 | 
						|
 | 
						|
        print("  * checking DB schema version", file=sys.stderr)
 | 
						|
 | 
						|
        try:
 | 
						|
            backend = cfg.getString("{}.driver".format(prefix))
 | 
						|
        except ValueError:
 | 
						|
            print(
 | 
						|
                "WARNING: dbstore message processor activated but no "
 | 
						|
                "database backend configured",
 | 
						|
                file=sys.stderr,
 | 
						|
            )
 | 
						|
            return True
 | 
						|
 | 
						|
        if backend not in ("mysql", "postgresql"):
 | 
						|
            print(
 | 
						|
                "WARNING: Only MySQL and PostgreSQL migrations are "
 | 
						|
                "supported right now. Please check and upgrade the "
 | 
						|
                "database schema version yourselves.",
 | 
						|
                file=sys.stderr,
 | 
						|
            )
 | 
						|
            return True
 | 
						|
 | 
						|
        print("  * check database write access ... ", end="", file=sys.stderr)
 | 
						|
 | 
						|
        # 1. Parse connection
 | 
						|
        try:
 | 
						|
            params = cfg.getString("{}.write".format(prefix))
 | 
						|
        except ValueError:
 | 
						|
            print("failed", file=sys.stderr)
 | 
						|
            print(
 | 
						|
                "WARNING: dbstore message processor activated but no "
 | 
						|
                "write connection configured",
 | 
						|
                file=sys.stderr,
 | 
						|
            )
 | 
						|
            return True
 | 
						|
 | 
						|
        user = "sysop"
 | 
						|
        pwd = "sysop"
 | 
						|
        host = "localhost"
 | 
						|
        db = "seiscomp"
 | 
						|
        port = None
 | 
						|
 | 
						|
        tmp = params.split("@")
 | 
						|
        if len(tmp) > 1:
 | 
						|
            params = tmp[1]
 | 
						|
 | 
						|
            tmp = tmp[0].split(":")
 | 
						|
            if len(tmp) == 1:
 | 
						|
                user = tmp[0]
 | 
						|
                pwd = None
 | 
						|
            elif len(tmp) == 2:
 | 
						|
                user = tmp[0]
 | 
						|
                pwd = tmp[1]
 | 
						|
            else:
 | 
						|
                print("failed", file=sys.stderr)
 | 
						|
                print(
 | 
						|
                    "WARNING: Invalid scmaster.cfg:{}.write, cannot check "
 | 
						|
                    "schema version".format(prefix),
 | 
						|
                    file=sys.stderr,
 | 
						|
                )
 | 
						|
                return True
 | 
						|
 | 
						|
        tmp = params.split("/")
 | 
						|
        if len(tmp) > 1:
 | 
						|
            tmpHost = tmp[0]
 | 
						|
            db = tmp[1]
 | 
						|
        else:
 | 
						|
            tmpHost = tmp[0]
 | 
						|
 | 
						|
        # get host name and port
 | 
						|
        tmp = tmpHost.split(":")
 | 
						|
        host = tmp[0]
 | 
						|
        if len(tmp) == 2:
 | 
						|
            try:
 | 
						|
                port = int(tmp[1])
 | 
						|
            except ValueError:
 | 
						|
                print("ERROR: Invalid port number {}".format(tmp[1]), file=sys.stderr)
 | 
						|
                return True
 | 
						|
 | 
						|
        db = db.split("?")[0]
 | 
						|
 | 
						|
        # 2. Try to login
 | 
						|
        if backend == "mysql":
 | 
						|
            cmd = 'mysql -u "%s" -h "%s" -D"%s" --skip-column-names' % (user, host, db)
 | 
						|
            if port:
 | 
						|
                cmd += " -P %d" % (port)
 | 
						|
            if pwd:
 | 
						|
                cmd += ' -p"%s"' % pwd.replace("$", "\\$")
 | 
						|
            cmd += " -e \"SELECT value from Meta where name='Schema-Version'\""
 | 
						|
        else:
 | 
						|
            if pwd:
 | 
						|
                os.environ["PGPASSWORD"] = pwd
 | 
						|
            cmd = 'psql -U "%s" -h "%s" -t "%s"' % (user, host, db)
 | 
						|
            if port:
 | 
						|
                cmd += " -p %d" % (port)
 | 
						|
            cmd += " -c \"SELECT value from Meta where name='Schema-Version'\""
 | 
						|
 | 
						|
        out = check_output(cmd)
 | 
						|
        if out[2] != 0:
 | 
						|
            print("failed", file=sys.stderr)
 | 
						|
            print("WARNING: {} returned with error:".format(backend), file=sys.stderr)
 | 
						|
            print(out[1].strip(), file=sys.stderr)
 | 
						|
            return False
 | 
						|
 | 
						|
        print("passed", file=sys.stderr)
 | 
						|
 | 
						|
        version = out[0].strip()
 | 
						|
        print("  * database schema version is {}".format(version), file=sys.stderr)
 | 
						|
 | 
						|
        try:
 | 
						|
            vmaj, vmin = [int(t) for t in version.split(".")]
 | 
						|
            vrev = 0
 | 
						|
        except ValueError:
 | 
						|
            try:
 | 
						|
                vmaj, vmin, vrev = [int(t) for t in version.split(".")]
 | 
						|
            except ValueError:
 | 
						|
                print(
 | 
						|
                    "WARNING: wrong version format: expected MAJOR.MINOR[.REV]",
 | 
						|
                    file=sys.stderr,
 | 
						|
                )
 | 
						|
                return True
 | 
						|
 | 
						|
        strictVersionMatch = True
 | 
						|
        try:
 | 
						|
            strictVersionMatch = cfg.getBool("{}.strictVersionMatch".format(prefix))
 | 
						|
        except ValueError:
 | 
						|
            pass
 | 
						|
 | 
						|
        if not strictVersionMatch:
 | 
						|
            print("  * database version check is disabled", file=sys.stderr)
 | 
						|
            return True
 | 
						|
 | 
						|
        migrations = os.path.join(
 | 
						|
            self.env.SEISCOMP_ROOT, "share", "db", "migrations", backend
 | 
						|
        )
 | 
						|
        migration_paths = {}
 | 
						|
 | 
						|
        vcurrmaj = 0
 | 
						|
        vcurrmin = 0
 | 
						|
        vcurrrev = 0
 | 
						|
 | 
						|
        for f in os.listdir(migrations):
 | 
						|
            if os.path.isfile(os.path.join(migrations, f)):
 | 
						|
                name, ext = os.path.splitext(f)
 | 
						|
                if ext != ".sql":
 | 
						|
                    continue
 | 
						|
                try:
 | 
						|
                    vfrom, vto = name.split("_to_")
 | 
						|
                except ValueError:
 | 
						|
                    continue
 | 
						|
 | 
						|
                try:
 | 
						|
                    vfrommaj, vfrommin, vfromrev = [int(t) for t in vfrom.split("_")]
 | 
						|
                except ValueError as e:
 | 
						|
                    try:
 | 
						|
                        vfrommaj, vfrommin = [int(t) for t in vfrom.split("_")]
 | 
						|
                        vfromrev = 0
 | 
						|
                    except ValueError:
 | 
						|
                        continue
 | 
						|
 | 
						|
                try:
 | 
						|
                    vtomaj, vtomin, vtorev = [int(t) for t in vto.split("_")]
 | 
						|
                except ValueError:
 | 
						|
                    try:
 | 
						|
                        vtomaj, vtomin = [int(t) for t in vto.split("_")]
 | 
						|
                        vtorev = 0
 | 
						|
                    except ValueError:
 | 
						|
                        continue
 | 
						|
 | 
						|
                migration_paths[(vfrommaj, vfrommin, vfromrev)] = (
 | 
						|
                    vtomaj,
 | 
						|
                    vtomin,
 | 
						|
                    vtorev,
 | 
						|
                )
 | 
						|
 | 
						|
                if (vtomaj > vcurrmaj) or (
 | 
						|
                    (vtomaj == vcurrmaj)
 | 
						|
                    and (
 | 
						|
                        (vtomin > vcurrmin)
 | 
						|
                        or ((vtomin == vcurrmin) and (vtorev > vcurrrev))
 | 
						|
                    )
 | 
						|
                ):
 | 
						|
                    vcurrmaj = vtomaj
 | 
						|
                    vcurrmin = vtomin
 | 
						|
                    vcurrrev = vtorev
 | 
						|
 | 
						|
        print(
 | 
						|
            "  * last migration version is %d.%d.%d" % (vcurrmaj, vcurrmin, vcurrrev),
 | 
						|
            file=sys.stderr,
 | 
						|
        )
 | 
						|
 | 
						|
        if vcurrmaj == vmaj and vcurrmin == vmin and vcurrrev == vrev:
 | 
						|
            print("  * schema up-to-date", file=sys.stderr)
 | 
						|
            return True
 | 
						|
 | 
						|
        if (vmaj, vmin, vrev) not in migration_paths:
 | 
						|
            print("  * no migrations found", file=sys.stderr)
 | 
						|
            return True
 | 
						|
 | 
						|
        print(
 | 
						|
            "  * migration to the current version is required. Apply the " "following",
 | 
						|
            file=sys.stderr,
 | 
						|
        )
 | 
						|
        print(
 | 
						|
            "    database migration scripts in exactly the given order:",
 | 
						|
            file=sys.stderr,
 | 
						|
        )
 | 
						|
        print("    * seiscomp stop", file=sys.stderr)
 | 
						|
 | 
						|
        def fn(maj, min, rev):
 | 
						|
            return "%d_%d_%d" % (maj, min, rev) if rev else "%d_%d" % (maj, min)
 | 
						|
 | 
						|
        while (vmaj, vmin, vrev) in migration_paths:
 | 
						|
            (vtomaj, vtomin, vtorev) = migration_paths[(vmaj, vmin, vrev)]
 | 
						|
            fname = "%s_to_%s.sql" % (fn(vmaj, vmin, vrev), fn(vtomaj, vtomin, vtorev))
 | 
						|
            if backend == "mysql":
 | 
						|
                print(
 | 
						|
                    "    * mysql -u {} -h {} -p {} < {}".format(
 | 
						|
                        user, host, db, os.path.join(migrations, fname)
 | 
						|
                    ),
 | 
						|
                    file=sys.stderr,
 | 
						|
                )
 | 
						|
            elif backend == "postgresql":
 | 
						|
                print(
 | 
						|
                    "    * psql -U {} -h {} -d {} -W -f {}".format(
 | 
						|
                        user, host, db, os.path.join(migrations, fname)
 | 
						|
                    ),
 | 
						|
                    file=sys.stderr,
 | 
						|
                )
 | 
						|
            else:
 | 
						|
                print(
 | 
						|
                    "    * {}".format(os.path.join(migrations, fname)), file=sys.stderr
 | 
						|
                )
 | 
						|
 | 
						|
            (vmaj, vmin, vrev) = (vtomaj, vtomin, vtorev)
 | 
						|
 | 
						|
        print("    * seiscomp start", file=sys.stderr)
 | 
						|
 | 
						|
        return False
 |