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
|