mirror of
https://github.com/odoo/runbot.git
synced 2025-03-16 16:05:42 +07:00
172 lines
6.2 KiB
Python
172 lines
6.2 KiB
Python
![]() |
#!/usr/bin/python3
|
||
|
import argparse
|
||
|
import contextlib
|
||
|
import logging
|
||
|
import psycopg2
|
||
|
import os
|
||
|
import re
|
||
|
import shutil
|
||
|
import sys
|
||
|
|
||
|
from collections import defaultdict
|
||
|
from logging.handlers import WatchedFileHandler
|
||
|
|
||
|
LOG_FORMAT = '%(asctime)s %(levelname)s %(name)s: %(message)s'
|
||
|
logging.basicConfig(level=logging.INFO, format=LOG_FORMAT)
|
||
|
logging.getLogger('odoo.addons.runbot').setLevel(logging.DEBUG)
|
||
|
logging.addLevelName(25, "!NFO")
|
||
|
|
||
|
_logger = logging.getLogger(__name__)
|
||
|
|
||
|
DBRE = r'^(?P<build_id>\d+)-.+-[0-9a-f]{6}-?(?P<db_suffix>.*)$'
|
||
|
|
||
|
|
||
|
@contextlib.contextmanager
|
||
|
def local_pgadmin_cursor():
|
||
|
cnx = None
|
||
|
try:
|
||
|
cnx = psycopg2.connect("dbname=postgres")
|
||
|
cnx.autocommit = True # required for admin commands
|
||
|
yield cnx.cursor()
|
||
|
finally:
|
||
|
if cnx:
|
||
|
cnx.close()
|
||
|
|
||
|
|
||
|
def list_local_dbs():
|
||
|
with local_pgadmin_cursor() as local_cr:
|
||
|
local_cr.execute("""
|
||
|
SELECT datname
|
||
|
FROM pg_database
|
||
|
WHERE pg_get_userbyid(datdba) = current_user
|
||
|
""")
|
||
|
return [d[0] for d in local_cr.fetchall()]
|
||
|
|
||
|
|
||
|
def _local_pg_rename_db(dbname, new_db_name):
|
||
|
with local_pgadmin_cursor() as local_cr:
|
||
|
pid_col = 'pid' if local_cr.connection.server_version >= 90200 else 'procpid'
|
||
|
query = 'SELECT pg_terminate_backend({}) FROM pg_stat_activity WHERE datname=%s'.format(pid_col)
|
||
|
local_cr.execute(query, [dbname])
|
||
|
local_cr.execute("ALTER DATABASE \"%s\" RENAME TO \"%s\";" % (dbname, new_db_name))
|
||
|
|
||
|
|
||
|
class RunbotClient():
|
||
|
|
||
|
def __init__(self, env):
|
||
|
self.env = env
|
||
|
|
||
|
def rename_build_dirs(self, args):
|
||
|
builds_root = os.path.join(self.env['runbot.runbot']._root(), 'build')
|
||
|
builds_backup_root = os.path.join(self.env['runbot.runbot']._root(), 'build-backup')
|
||
|
if not args.dry_run:
|
||
|
try:
|
||
|
_logger.info('Backup build dir in "%s"', builds_backup_root)
|
||
|
shutil.copytree(builds_root, builds_backup_root, copy_function=os.link)
|
||
|
except FileExistsError:
|
||
|
_logger.info('Backup path "%s" already exists, skipping', builds_backup_root)
|
||
|
|
||
|
build_dirs = {}
|
||
|
leftovers = []
|
||
|
for dir_name in os.listdir(builds_root):
|
||
|
match = re.match(DBRE, dir_name)
|
||
|
if match and match['db_suffix'] == '':
|
||
|
build_dirs[match['build_id']] = dir_name
|
||
|
else:
|
||
|
leftovers.append(dir_name)
|
||
|
|
||
|
for build in self.env['runbot.build'].search([('id', 'in', list(build_dirs.keys()))]):
|
||
|
origin_dir = build_dirs[str(build.id)]
|
||
|
origin_path = os.path.join(builds_root, origin_dir)
|
||
|
if origin_dir == build.dest:
|
||
|
_logger.info('Skip moving %s, already moved', build.dest)
|
||
|
continue
|
||
|
_logger.info('Moving "%s" --> "%s"', origin_dir, build.dest)
|
||
|
if args.dry_run:
|
||
|
continue
|
||
|
dest_path = os.path.join(builds_root, build.dest)
|
||
|
os.rename(origin_path, dest_path)
|
||
|
|
||
|
for leftover in leftovers:
|
||
|
_logger.info("leftover: %s", leftover)
|
||
|
|
||
|
def rename_databases(self, args):
|
||
|
total_db = 0
|
||
|
db_names = defaultdict(dict)
|
||
|
leftovers = []
|
||
|
for local_db_name in list_local_dbs():
|
||
|
match = re.match(DBRE, local_db_name)
|
||
|
if match and match['db_suffix'] != '':
|
||
|
db_names[match['build_id']][match['db_suffix']] = local_db_name
|
||
|
else:
|
||
|
leftovers.append(local_db_name)
|
||
|
total_db += 1
|
||
|
|
||
|
nb_matching = 0
|
||
|
ids = [int(i) for i in db_names.keys()]
|
||
|
builds = self.env['runbot.build'].search([('id', 'in', ids)])
|
||
|
for build in builds:
|
||
|
for suffix in db_names[str(build.id)].keys():
|
||
|
origin_name = db_names[str(build.id)][suffix]
|
||
|
dest_name = "%s-%s" % (build.dest, suffix)
|
||
|
nb_matching += 1
|
||
|
_logger.info('Renaming database "%s" --> "%s"', origin_name, dest_name)
|
||
|
if args.dry_run:
|
||
|
continue
|
||
|
_local_pg_rename_db(origin_name, dest_name)
|
||
|
|
||
|
_logger.info("Found %s databases", total_db)
|
||
|
_logger.info("Found %s matching databases", nb_matching)
|
||
|
_logger.info("Leftovers: %s", len(leftovers))
|
||
|
_logger.info("Builds not found : %s", len(set(ids) - set(builds.ids)))
|
||
|
|
||
|
|
||
|
def run():
|
||
|
# parse args
|
||
|
parser = argparse.ArgumentParser()
|
||
|
parser.add_argument('--odoo-path', help='Odoo sources path')
|
||
|
parser.add_argument('--db_host', default='127.0.0.1')
|
||
|
parser.add_argument('--db_port', default='5432')
|
||
|
parser.add_argument('--db_user')
|
||
|
parser.add_argument('--db_password')
|
||
|
parser.add_argument('-d', '--database', default='runbot_upgrade', help='name of runbot db')
|
||
|
parser.add_argument('--logfile', default=False)
|
||
|
parser.add_argument('-n', '--dry-run', action='store_true')
|
||
|
args = parser.parse_args()
|
||
|
if args.logfile:
|
||
|
dirname = os.path.dirname(args.logfile)
|
||
|
if dirname and not os.path.isdir(dirname):
|
||
|
os.makedirs(dirname)
|
||
|
|
||
|
handler = WatchedFileHandler(args.logfile)
|
||
|
formatter = logging.Formatter(LOG_FORMAT)
|
||
|
handler.setFormatter(formatter)
|
||
|
_logger.parent.handlers.clear()
|
||
|
_logger.parent.addHandler(handler)
|
||
|
|
||
|
# configure odoo
|
||
|
sys.path.append(args.odoo_path)
|
||
|
import odoo
|
||
|
_logger.info("Starting upgrade move script using database %s", args.database)
|
||
|
odoo.tools.config['db_host'] = args.db_host
|
||
|
odoo.tools.config['db_port'] = args.db_port
|
||
|
odoo.tools.config['db_user'] = args.db_user
|
||
|
odoo.tools.config['db_password'] = args.db_password
|
||
|
addon_path = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '..'))
|
||
|
config_addons_path = odoo.tools.config['addons_path']
|
||
|
odoo.tools.config['addons_path'] = ','.join([config_addons_path, addon_path])
|
||
|
|
||
|
# create environment
|
||
|
registry = odoo.registry(args.database)
|
||
|
with odoo.api.Environment.manage():
|
||
|
with registry.cursor() as cr:
|
||
|
env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {})
|
||
|
runbot_client = RunbotClient(env)
|
||
|
runbot_client.rename_build_dirs(args)
|
||
|
runbot_client.rename_databases(args)
|
||
|
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
run()
|
||
|
_logger.info("All done")
|