mirror of
https://github.com/bringout/oca-ocb-core.git
synced 2026-04-22 16:32:00 +02:00
Initial commit: Core packages
This commit is contained in:
commit
12c29a983b
9512 changed files with 8379910 additions and 0 deletions
20
odoo-bringout-oca-ocb-base/odoo/service/__init__.py
Normal file
20
odoo-bringout-oca-ocb-base/odoo/service/__init__.py
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import common
|
||||
from . import db
|
||||
from . import model
|
||||
from . import server
|
||||
|
||||
# deprecated since 15.3
|
||||
from . import wsgi_server
|
||||
|
||||
#.apidoc title: RPC Services
|
||||
|
||||
""" Classes of this module implement the network protocols that the
|
||||
OpenERP server uses to communicate with remote clients.
|
||||
|
||||
Some classes are mostly utilities, whose API need not be visible to
|
||||
the average user/developer. Study them only if you are about to
|
||||
implement an extension to the network protocols, or need to debug some
|
||||
low-level behavior of the wire.
|
||||
"""
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
58
odoo-bringout-oca-ocb-base/odoo/service/common.py
Normal file
58
odoo-bringout-oca-ocb-base/odoo/service/common.py
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
|
||||
import odoo.release
|
||||
import odoo.tools
|
||||
from odoo.exceptions import AccessDenied
|
||||
from odoo.tools.translate import _
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
RPC_VERSION_1 = {
|
||||
'server_version': odoo.release.version,
|
||||
'server_version_info': odoo.release.version_info,
|
||||
'server_serie': odoo.release.serie,
|
||||
'protocol_version': 1,
|
||||
}
|
||||
|
||||
def exp_login(db, login, password):
|
||||
return exp_authenticate(db, login, password, None)
|
||||
|
||||
def exp_authenticate(db, login, password, user_agent_env):
|
||||
if not user_agent_env:
|
||||
user_agent_env = {}
|
||||
res_users = odoo.registry(db)['res.users']
|
||||
try:
|
||||
return res_users.authenticate(db, login, password, {**user_agent_env, 'interactive': False})
|
||||
except AccessDenied:
|
||||
return False
|
||||
|
||||
def exp_version():
|
||||
return RPC_VERSION_1
|
||||
|
||||
def exp_about(extended=False):
|
||||
"""Return information about the OpenERP Server.
|
||||
|
||||
@param extended: if True then return version info
|
||||
@return string if extended is False else tuple
|
||||
"""
|
||||
|
||||
info = _('See http://openerp.com')
|
||||
|
||||
if extended:
|
||||
return info, odoo.release.version
|
||||
return info
|
||||
|
||||
def exp_set_loglevel(loglevel, logger=None):
|
||||
# TODO Previously, the level was set on the now deprecated
|
||||
# `odoo.netsvc.Logger` class.
|
||||
return True
|
||||
|
||||
def dispatch(method, params):
|
||||
g = globals()
|
||||
exp_method_name = 'exp_' + method
|
||||
if exp_method_name in g:
|
||||
return g[exp_method_name](*params)
|
||||
else:
|
||||
raise Exception("Method not found: %s" % method)
|
||||
510
odoo-bringout-oca-ocb-base/odoo/service/db.py
Normal file
510
odoo-bringout-oca-ocb-base/odoo/service/db.py
Normal file
|
|
@ -0,0 +1,510 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import base64
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import threading
|
||||
import traceback
|
||||
from xml.etree import ElementTree as ET
|
||||
import zipfile
|
||||
|
||||
from datetime import datetime
|
||||
from psycopg2 import sql
|
||||
from pytz import country_timezones
|
||||
from functools import wraps
|
||||
from contextlib import closing
|
||||
from decorator import decorator
|
||||
|
||||
import psycopg2
|
||||
|
||||
import odoo
|
||||
from odoo import SUPERUSER_ID
|
||||
from odoo.exceptions import AccessDenied
|
||||
import odoo.release
|
||||
import odoo.sql_db
|
||||
import odoo.tools
|
||||
from odoo.sql_db import db_connect
|
||||
from odoo.release import version_info
|
||||
from odoo.tools import find_pg_tool, exec_pg_environ
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class DatabaseExists(Warning):
|
||||
pass
|
||||
|
||||
|
||||
def check_db_management_enabled(method):
|
||||
def if_db_mgt_enabled(method, self, *args, **kwargs):
|
||||
if not odoo.tools.config['list_db']:
|
||||
_logger.error('Database management functions blocked, admin disabled database listing')
|
||||
raise AccessDenied()
|
||||
return method(self, *args, **kwargs)
|
||||
return decorator(if_db_mgt_enabled, method)
|
||||
|
||||
#----------------------------------------------------------
|
||||
# Master password required
|
||||
#----------------------------------------------------------
|
||||
|
||||
def check_super(passwd):
|
||||
if passwd and odoo.tools.config.verify_admin_password(passwd):
|
||||
return True
|
||||
raise odoo.exceptions.AccessDenied()
|
||||
|
||||
# This should be moved to odoo.modules.db, along side initialize().
|
||||
def _initialize_db(id, db_name, demo, lang, user_password, login='admin', country_code=None, phone=None):
|
||||
try:
|
||||
db = odoo.sql_db.db_connect(db_name)
|
||||
with closing(db.cursor()) as cr:
|
||||
# TODO this should be removed as it is done by Registry.new().
|
||||
odoo.modules.db.initialize(cr)
|
||||
odoo.tools.config['load_language'] = lang
|
||||
cr.commit()
|
||||
|
||||
registry = odoo.modules.registry.Registry.new(db_name, demo, None, update_module=True)
|
||||
|
||||
with closing(registry.cursor()) as cr:
|
||||
env = odoo.api.Environment(cr, SUPERUSER_ID, {})
|
||||
|
||||
if lang:
|
||||
modules = env['ir.module.module'].search([('state', '=', 'installed')])
|
||||
modules._update_translations(lang)
|
||||
|
||||
if country_code:
|
||||
country = env['res.country'].search([('code', 'ilike', country_code)])[0]
|
||||
env['res.company'].browse(1).write({'country_id': country_code and country.id, 'currency_id': country_code and country.currency_id.id})
|
||||
if len(country_timezones.get(country_code, [])) == 1:
|
||||
users = env['res.users'].search([])
|
||||
users.write({'tz': country_timezones[country_code][0]})
|
||||
if phone:
|
||||
env['res.company'].browse(1).write({'phone': phone})
|
||||
if '@' in login:
|
||||
env['res.company'].browse(1).write({'email': login})
|
||||
|
||||
# update admin's password and lang and login
|
||||
values = {'password': user_password, 'lang': lang}
|
||||
if login:
|
||||
values['login'] = login
|
||||
emails = odoo.tools.email_split(login)
|
||||
if emails:
|
||||
values['email'] = emails[0]
|
||||
env.ref('base.user_admin').write(values)
|
||||
|
||||
cr.execute('SELECT login, password FROM res_users ORDER BY login')
|
||||
cr.commit()
|
||||
except Exception as e:
|
||||
_logger.exception('CREATE DATABASE failed:')
|
||||
|
||||
|
||||
def _check_faketime_mode(db_name):
|
||||
if os.getenv('ODOO_FAKETIME_TEST_MODE') and db_name in odoo.tools.config['db_name'].split(','):
|
||||
try:
|
||||
db = odoo.sql_db.db_connect(db_name)
|
||||
with db.cursor() as cursor:
|
||||
cursor.execute("SELECT (pg_catalog.now() AT TIME ZONE 'UTC');")
|
||||
server_now = cursor.fetchone()[0]
|
||||
time_offset = (datetime.now() - server_now).total_seconds()
|
||||
|
||||
cursor.execute("""
|
||||
CREATE OR REPLACE FUNCTION public.now()
|
||||
RETURNS timestamp with time zone AS $$
|
||||
SELECT pg_catalog.now() + %s * interval '1 second';
|
||||
$$ LANGUAGE sql;
|
||||
""", (int(time_offset), ))
|
||||
cursor.execute("SELECT (now() AT TIME ZONE 'UTC');")
|
||||
new_now = cursor.fetchone()[0]
|
||||
_logger.info("Faketime mode, new cursor now is %s", new_now)
|
||||
cursor.commit()
|
||||
except psycopg2.Error as e:
|
||||
_logger.warning("Unable to set fakedtimed NOW() : %s", e)
|
||||
|
||||
|
||||
def _create_empty_database(name):
|
||||
db = odoo.sql_db.db_connect('postgres')
|
||||
with closing(db.cursor()) as cr:
|
||||
chosen_template = odoo.tools.config['db_template']
|
||||
cr.execute("SELECT datname FROM pg_database WHERE datname = %s",
|
||||
(name,), log_exceptions=False)
|
||||
if cr.fetchall():
|
||||
_check_faketime_mode(name)
|
||||
raise DatabaseExists("database %r already exists!" % (name,))
|
||||
else:
|
||||
# database-altering operations cannot be executed inside a transaction
|
||||
cr.rollback()
|
||||
cr._cnx.autocommit = True
|
||||
|
||||
# 'C' collate is only safe with template0, but provides more useful indexes
|
||||
collate = sql.SQL("LC_COLLATE 'C'" if chosen_template == 'template0' else "")
|
||||
cr.execute(
|
||||
sql.SQL("CREATE DATABASE {} ENCODING 'unicode' {} TEMPLATE {}").format(
|
||||
sql.Identifier(name), collate, sql.Identifier(chosen_template)
|
||||
))
|
||||
|
||||
# TODO: add --extension=trigram,unaccent
|
||||
try:
|
||||
db = odoo.sql_db.db_connect(name)
|
||||
with db.cursor() as cr:
|
||||
cr.execute("CREATE EXTENSION IF NOT EXISTS pg_trgm")
|
||||
if odoo.tools.config['unaccent']:
|
||||
cr.execute("CREATE EXTENSION IF NOT EXISTS unaccent")
|
||||
# From PostgreSQL's point of view, making 'unaccent' immutable is incorrect
|
||||
# because it depends on external data - see
|
||||
# https://www.postgresql.org/message-id/flat/201012021544.oB2FiTn1041521@wwwmaster.postgresql.org#201012021544.oB2FiTn1041521@wwwmaster.postgresql.org
|
||||
# But in the case of Odoo, we consider that those data don't
|
||||
# change in the lifetime of a database. If they do change, all
|
||||
# indexes created with this function become corrupted!
|
||||
cr.execute("ALTER FUNCTION unaccent(text) IMMUTABLE")
|
||||
except psycopg2.Error as e:
|
||||
_logger.warning("Unable to create PostgreSQL extensions : %s", e)
|
||||
_check_faketime_mode(name)
|
||||
|
||||
|
||||
@check_db_management_enabled
|
||||
def exp_create_database(db_name, demo, lang, user_password='admin', login='admin', country_code=None, phone=None):
|
||||
""" Similar to exp_create but blocking."""
|
||||
_logger.info('Create database `%s`.', db_name)
|
||||
_create_empty_database(db_name)
|
||||
_initialize_db(id, db_name, demo, lang, user_password, login, country_code, phone)
|
||||
return True
|
||||
|
||||
@check_db_management_enabled
|
||||
def exp_duplicate_database(db_original_name, db_name, neutralize_database=False):
|
||||
_logger.info('Duplicate database `%s` to `%s`.', db_original_name, db_name)
|
||||
odoo.sql_db.close_db(db_original_name)
|
||||
db = odoo.sql_db.db_connect('postgres')
|
||||
with closing(db.cursor()) as cr:
|
||||
# database-altering operations cannot be executed inside a transaction
|
||||
cr._cnx.autocommit = True
|
||||
_drop_conn(cr, db_original_name)
|
||||
cr.execute(sql.SQL("CREATE DATABASE {} ENCODING 'unicode' TEMPLATE {}").format(
|
||||
sql.Identifier(db_name),
|
||||
sql.Identifier(db_original_name)
|
||||
))
|
||||
|
||||
registry = odoo.modules.registry.Registry.new(db_name)
|
||||
with registry.cursor() as cr:
|
||||
# if it's a copy of a database, force generation of a new dbuuid
|
||||
env = odoo.api.Environment(cr, SUPERUSER_ID, {})
|
||||
env['ir.config_parameter'].init(force=True)
|
||||
if neutralize_database:
|
||||
odoo.modules.neutralize.neutralize_database(cr)
|
||||
|
||||
from_fs = odoo.tools.config.filestore(db_original_name)
|
||||
to_fs = odoo.tools.config.filestore(db_name)
|
||||
if os.path.exists(from_fs) and not os.path.exists(to_fs):
|
||||
shutil.copytree(from_fs, to_fs)
|
||||
return True
|
||||
|
||||
def _drop_conn(cr, db_name):
|
||||
# Try to terminate all other connections that might prevent
|
||||
# dropping the database
|
||||
try:
|
||||
# PostgreSQL 9.2 renamed pg_stat_activity.procpid to pid:
|
||||
# http://www.postgresql.org/docs/9.2/static/release-9-2.html#AEN110389
|
||||
pid_col = 'pid' if cr._cnx.server_version >= 90200 else 'procpid'
|
||||
|
||||
cr.execute("""SELECT pg_terminate_backend(%(pid_col)s)
|
||||
FROM pg_stat_activity
|
||||
WHERE datname = %%s AND
|
||||
%(pid_col)s != pg_backend_pid()""" % {'pid_col': pid_col},
|
||||
(db_name,))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@check_db_management_enabled
|
||||
def exp_drop(db_name):
|
||||
if db_name not in list_dbs(True):
|
||||
return False
|
||||
odoo.modules.registry.Registry.delete(db_name)
|
||||
odoo.sql_db.close_db(db_name)
|
||||
|
||||
db = odoo.sql_db.db_connect('postgres')
|
||||
with closing(db.cursor()) as cr:
|
||||
# database-altering operations cannot be executed inside a transaction
|
||||
cr._cnx.autocommit = True
|
||||
_drop_conn(cr, db_name)
|
||||
|
||||
try:
|
||||
cr.execute(sql.SQL('DROP DATABASE {}').format(sql.Identifier(db_name)))
|
||||
except Exception as e:
|
||||
_logger.info('DROP DB: %s failed:\n%s', db_name, e)
|
||||
raise Exception("Couldn't drop database %s: %s" % (db_name, e))
|
||||
else:
|
||||
_logger.info('DROP DB: %s', db_name)
|
||||
|
||||
fs = odoo.tools.config.filestore(db_name)
|
||||
if os.path.exists(fs):
|
||||
shutil.rmtree(fs)
|
||||
return True
|
||||
|
||||
@check_db_management_enabled
|
||||
def exp_dump(db_name, format):
|
||||
with tempfile.TemporaryFile(mode='w+b') as t:
|
||||
dump_db(db_name, t, format)
|
||||
t.seek(0)
|
||||
return base64.b64encode(t.read()).decode()
|
||||
|
||||
@check_db_management_enabled
|
||||
def dump_db_manifest(cr):
|
||||
pg_version = "%d.%d" % divmod(cr._obj.connection.server_version / 100, 100)
|
||||
cr.execute("SELECT name, latest_version FROM ir_module_module WHERE state = 'installed'")
|
||||
modules = dict(cr.fetchall())
|
||||
manifest = {
|
||||
'odoo_dump': '1',
|
||||
'db_name': cr.dbname,
|
||||
'version': odoo.release.version,
|
||||
'version_info': odoo.release.version_info,
|
||||
'major_version': odoo.release.major_version,
|
||||
'pg_version': pg_version,
|
||||
'modules': modules,
|
||||
}
|
||||
return manifest
|
||||
|
||||
@check_db_management_enabled
|
||||
def dump_db(db_name, stream, backup_format='zip'):
|
||||
"""Dump database `db` into file-like object `stream` if stream is None
|
||||
return a file object with the dump """
|
||||
|
||||
_logger.info('DUMP DB: %s format %s', db_name, backup_format)
|
||||
|
||||
cmd = [find_pg_tool('pg_dump'), '--no-owner', db_name]
|
||||
env = exec_pg_environ()
|
||||
|
||||
if backup_format == 'zip':
|
||||
with tempfile.TemporaryDirectory() as dump_dir:
|
||||
filestore = odoo.tools.config.filestore(db_name)
|
||||
if os.path.exists(filestore):
|
||||
shutil.copytree(filestore, os.path.join(dump_dir, 'filestore'))
|
||||
with open(os.path.join(dump_dir, 'manifest.json'), 'w') as fh:
|
||||
db = odoo.sql_db.db_connect(db_name)
|
||||
with db.cursor() as cr:
|
||||
json.dump(dump_db_manifest(cr), fh, indent=4)
|
||||
cmd.insert(-1, '--file=' + os.path.join(dump_dir, 'dump.sql'))
|
||||
subprocess.run(cmd, env=env, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT, check=True)
|
||||
if stream:
|
||||
odoo.tools.osutil.zip_dir(dump_dir, stream, include_dir=False, fnct_sort=lambda file_name: file_name != 'dump.sql')
|
||||
else:
|
||||
t=tempfile.TemporaryFile()
|
||||
odoo.tools.osutil.zip_dir(dump_dir, t, include_dir=False, fnct_sort=lambda file_name: file_name != 'dump.sql')
|
||||
t.seek(0)
|
||||
return t
|
||||
else:
|
||||
cmd.insert(-1, '--format=c')
|
||||
stdout = subprocess.Popen(cmd, env=env, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE).stdout
|
||||
if stream:
|
||||
shutil.copyfileobj(stdout, stream)
|
||||
else:
|
||||
return stdout
|
||||
|
||||
@check_db_management_enabled
|
||||
def exp_restore(db_name, data, copy=False):
|
||||
def chunks(d, n=8192):
|
||||
for i in range(0, len(d), n):
|
||||
yield d[i:i+n]
|
||||
data_file = tempfile.NamedTemporaryFile(delete=False)
|
||||
try:
|
||||
for chunk in chunks(data):
|
||||
data_file.write(base64.b64decode(chunk))
|
||||
data_file.close()
|
||||
restore_db(db_name, data_file.name, copy=copy)
|
||||
finally:
|
||||
os.unlink(data_file.name)
|
||||
return True
|
||||
|
||||
@check_db_management_enabled
|
||||
def restore_db(db, dump_file, copy=False, neutralize_database=False):
|
||||
assert isinstance(db, str)
|
||||
if exp_db_exist(db):
|
||||
_logger.warning('RESTORE DB: %s already exists', db)
|
||||
raise Exception("Database already exists")
|
||||
|
||||
_logger.info('RESTORING DB: %s', db)
|
||||
_create_empty_database(db)
|
||||
|
||||
filestore_path = None
|
||||
with tempfile.TemporaryDirectory() as dump_dir:
|
||||
if zipfile.is_zipfile(dump_file):
|
||||
# v8 format
|
||||
with zipfile.ZipFile(dump_file, 'r') as z:
|
||||
# only extract known members!
|
||||
filestore = [m for m in z.namelist() if m.startswith('filestore/')]
|
||||
z.extractall(dump_dir, ['dump.sql'] + filestore)
|
||||
|
||||
if filestore:
|
||||
filestore_path = os.path.join(dump_dir, 'filestore')
|
||||
|
||||
pg_cmd = 'psql'
|
||||
pg_args = ['-q', '-f', os.path.join(dump_dir, 'dump.sql')]
|
||||
|
||||
else:
|
||||
# <= 7.0 format (raw pg_dump output)
|
||||
pg_cmd = 'pg_restore'
|
||||
pg_args = ['--no-owner', dump_file]
|
||||
|
||||
r = subprocess.run(
|
||||
[find_pg_tool(pg_cmd), '--dbname=' + db, *pg_args],
|
||||
env=exec_pg_environ(),
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.STDOUT,
|
||||
)
|
||||
if r.returncode != 0:
|
||||
raise Exception("Couldn't restore database")
|
||||
|
||||
registry = odoo.modules.registry.Registry.new(db)
|
||||
with registry.cursor() as cr:
|
||||
env = odoo.api.Environment(cr, SUPERUSER_ID, {})
|
||||
if copy:
|
||||
# if it's a copy of a database, force generation of a new dbuuid
|
||||
env['ir.config_parameter'].init(force=True)
|
||||
if neutralize_database:
|
||||
odoo.modules.neutralize.neutralize_database(cr)
|
||||
|
||||
if filestore_path:
|
||||
filestore_dest = env['ir.attachment']._filestore()
|
||||
shutil.move(filestore_path, filestore_dest)
|
||||
|
||||
_logger.info('RESTORE DB: %s', db)
|
||||
|
||||
@check_db_management_enabled
|
||||
def exp_rename(old_name, new_name):
|
||||
odoo.modules.registry.Registry.delete(old_name)
|
||||
odoo.sql_db.close_db(old_name)
|
||||
|
||||
db = odoo.sql_db.db_connect('postgres')
|
||||
with closing(db.cursor()) as cr:
|
||||
# database-altering operations cannot be executed inside a transaction
|
||||
cr._cnx.autocommit = True
|
||||
_drop_conn(cr, old_name)
|
||||
try:
|
||||
cr.execute(sql.SQL('ALTER DATABASE {} RENAME TO {}').format(sql.Identifier(old_name), sql.Identifier(new_name)))
|
||||
_logger.info('RENAME DB: %s -> %s', old_name, new_name)
|
||||
except Exception as e:
|
||||
_logger.info('RENAME DB: %s -> %s failed:\n%s', old_name, new_name, e)
|
||||
raise Exception("Couldn't rename database %s to %s: %s" % (old_name, new_name, e))
|
||||
|
||||
old_fs = odoo.tools.config.filestore(old_name)
|
||||
new_fs = odoo.tools.config.filestore(new_name)
|
||||
if os.path.exists(old_fs) and not os.path.exists(new_fs):
|
||||
shutil.move(old_fs, new_fs)
|
||||
return True
|
||||
|
||||
@check_db_management_enabled
|
||||
def exp_change_admin_password(new_password):
|
||||
odoo.tools.config.set_admin_password(new_password)
|
||||
odoo.tools.config.save(['admin_passwd'])
|
||||
return True
|
||||
|
||||
@check_db_management_enabled
|
||||
def exp_migrate_databases(databases):
|
||||
for db in databases:
|
||||
_logger.info('migrate database %s', db)
|
||||
odoo.tools.config['update']['base'] = True
|
||||
odoo.modules.registry.Registry.new(db, force_demo=False, update_module=True)
|
||||
return True
|
||||
|
||||
#----------------------------------------------------------
|
||||
# No master password required
|
||||
#----------------------------------------------------------
|
||||
|
||||
@odoo.tools.mute_logger('odoo.sql_db')
|
||||
def exp_db_exist(db_name):
|
||||
## Not True: in fact, check if connection to database is possible. The database may exists
|
||||
try:
|
||||
db = odoo.sql_db.db_connect(db_name)
|
||||
with db.cursor():
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def list_dbs(force=False):
|
||||
if not odoo.tools.config['list_db'] and not force:
|
||||
raise odoo.exceptions.AccessDenied()
|
||||
|
||||
if not odoo.tools.config['dbfilter'] and odoo.tools.config['db_name']:
|
||||
# In case --db-filter is not provided and --database is passed, Odoo will not
|
||||
# fetch the list of databases available on the postgres server and instead will
|
||||
# use the value of --database as comma seperated list of exposed databases.
|
||||
res = sorted(db.strip() for db in odoo.tools.config['db_name'].split(','))
|
||||
return res
|
||||
|
||||
chosen_template = odoo.tools.config['db_template']
|
||||
templates_list = tuple(set(['postgres', chosen_template]))
|
||||
db = odoo.sql_db.db_connect('postgres')
|
||||
with closing(db.cursor()) as cr:
|
||||
try:
|
||||
cr.execute("select datname from pg_database where datdba=(select usesysid from pg_user where usename=current_user) and not datistemplate and datallowconn and datname not in %s order by datname", (templates_list,))
|
||||
res = [odoo.tools.ustr(name) for (name,) in cr.fetchall()]
|
||||
except Exception:
|
||||
_logger.exception('Listing databases failed:')
|
||||
res = []
|
||||
return res
|
||||
|
||||
def list_db_incompatible(databases):
|
||||
""""Check a list of databases if they are compatible with this version of Odoo
|
||||
|
||||
:param databases: A list of existing Postgresql databases
|
||||
:return: A list of databases that are incompatible
|
||||
"""
|
||||
incompatible_databases = []
|
||||
server_version = '.'.join(str(v) for v in version_info[:2])
|
||||
for database_name in databases:
|
||||
with closing(db_connect(database_name).cursor()) as cr:
|
||||
if odoo.tools.table_exists(cr, 'ir_module_module'):
|
||||
cr.execute("SELECT latest_version FROM ir_module_module WHERE name=%s", ('base',))
|
||||
base_version = cr.fetchone()
|
||||
if not base_version or not base_version[0]:
|
||||
incompatible_databases.append(database_name)
|
||||
else:
|
||||
# e.g. 10.saas~15
|
||||
local_version = '.'.join(base_version[0].split('.')[:2])
|
||||
if local_version != server_version:
|
||||
incompatible_databases.append(database_name)
|
||||
else:
|
||||
incompatible_databases.append(database_name)
|
||||
for database_name in incompatible_databases:
|
||||
# release connection
|
||||
odoo.sql_db.close_db(database_name)
|
||||
return incompatible_databases
|
||||
|
||||
|
||||
def exp_list(document=False):
|
||||
if not odoo.tools.config['list_db']:
|
||||
raise odoo.exceptions.AccessDenied()
|
||||
return list_dbs()
|
||||
|
||||
def exp_list_lang():
|
||||
return odoo.tools.scan_languages()
|
||||
|
||||
def exp_list_countries():
|
||||
list_countries = []
|
||||
root = ET.parse(os.path.join(odoo.tools.config['root_path'], 'addons/base/data/res_country_data.xml')).getroot()
|
||||
for country in root.find('data').findall('record[@model="res.country"]'):
|
||||
name = country.find('field[@name="name"]').text
|
||||
code = country.find('field[@name="code"]').text
|
||||
list_countries.append([code, name])
|
||||
return sorted(list_countries, key=lambda c: c[1])
|
||||
|
||||
def exp_server_version():
|
||||
""" Return the version of the server
|
||||
Used by the client to verify the compatibility with its own version
|
||||
"""
|
||||
return odoo.release.version
|
||||
|
||||
#----------------------------------------------------------
|
||||
# db service dispatch
|
||||
#----------------------------------------------------------
|
||||
|
||||
def dispatch(method, params):
|
||||
g = globals()
|
||||
exp_method_name = 'exp_' + method
|
||||
if method in ['db_exist', 'list', 'list_lang', 'server_version']:
|
||||
return g[exp_method_name](*params)
|
||||
elif exp_method_name in g:
|
||||
passwd = params[0]
|
||||
params = params[1:]
|
||||
check_super(passwd)
|
||||
return g[exp_method_name](*params)
|
||||
else:
|
||||
raise KeyError("Method not found: %s" % method)
|
||||
212
odoo-bringout-oca-ocb-base/odoo/service/model.py
Normal file
212
odoo-bringout-oca-ocb-base/odoo/service/model.py
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
import logging
|
||||
import random
|
||||
import threading
|
||||
import time
|
||||
from collections.abc import Mapping, Sequence
|
||||
from functools import partial
|
||||
|
||||
from psycopg2 import IntegrityError, OperationalError, errorcodes
|
||||
|
||||
import odoo
|
||||
from odoo.exceptions import UserError, ValidationError, AccessError
|
||||
from odoo.models import BaseModel
|
||||
from odoo.http import request
|
||||
from odoo.tools import DotDict
|
||||
from odoo.tools.translate import _, translate_sql_constraint
|
||||
from . import security
|
||||
from ..tools import lazy
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
PG_CONCURRENCY_ERRORS_TO_RETRY = (errorcodes.LOCK_NOT_AVAILABLE, errorcodes.SERIALIZATION_FAILURE, errorcodes.DEADLOCK_DETECTED)
|
||||
MAX_TRIES_ON_CONCURRENCY_FAILURE = 5
|
||||
|
||||
|
||||
def get_public_method(model, name):
|
||||
""" Get the public unbound method from a model.
|
||||
When the method does not exist or is inaccessible, raise appropriate errors.
|
||||
Accessible methods are public (in sense that python defined it:
|
||||
not prefixed with "_") and are not decorated with `@api.private`.
|
||||
"""
|
||||
assert isinstance(model, BaseModel), f"{model!r} is not a BaseModel for {name}"
|
||||
cls = type(model)
|
||||
method = getattr(cls, name, None)
|
||||
if not callable(method):
|
||||
raise AttributeError(f"The method '{model._name}.{name}' does not exist") # noqa: TRY004
|
||||
for mro_cls in cls.mro():
|
||||
cla_method = getattr(mro_cls, name, None)
|
||||
if not cla_method:
|
||||
continue
|
||||
if name.startswith('_') or getattr(cla_method, '_api_private', False):
|
||||
raise AccessError(f"Private methods (such as '{model._name}.{name}') cannot be called remotely.")
|
||||
return method
|
||||
|
||||
def dispatch(method, params):
|
||||
db, uid, passwd = params[0], int(params[1]), params[2]
|
||||
security.check(db, uid, passwd)
|
||||
|
||||
threading.current_thread().dbname = db
|
||||
threading.current_thread().uid = uid
|
||||
registry = odoo.registry(db).check_signaling()
|
||||
with registry.manage_changes():
|
||||
if method == 'execute':
|
||||
res = execute(db, uid, *params[3:])
|
||||
elif method == 'execute_kw':
|
||||
res = execute_kw(db, uid, *params[3:])
|
||||
else:
|
||||
raise NameError("Method not available %s" % method)
|
||||
return res
|
||||
|
||||
|
||||
def execute_cr(cr, uid, obj, method, *args, **kw):
|
||||
# clean cache etc if we retry the same transaction
|
||||
cr.reset()
|
||||
env = odoo.api.Environment(cr, uid, {})
|
||||
recs = env.get(obj)
|
||||
if recs is None:
|
||||
raise UserError(_("Object %s doesn't exist", obj))
|
||||
get_public_method(recs, method) # Don't use the result, call_kw will redo the getattr
|
||||
result = retrying(partial(odoo.api.call_kw, recs, method, args, kw), env)
|
||||
# force evaluation of lazy values before the cursor is closed, as it would
|
||||
# error afterwards if the lazy isn't already evaluated (and cached)
|
||||
for l in _traverse_containers(result, lazy):
|
||||
_0 = l._value
|
||||
return result
|
||||
|
||||
|
||||
def execute_kw(db, uid, obj, method, args, kw=None):
|
||||
return execute(db, uid, obj, method, *args, **kw or {})
|
||||
|
||||
|
||||
def execute(db, uid, obj, method, *args, **kw):
|
||||
with odoo.registry(db).cursor() as cr:
|
||||
res = execute_cr(cr, uid, obj, method, *args, **kw)
|
||||
if res is None:
|
||||
_logger.info('The method %s of the object %s can not return `None` !', method, obj)
|
||||
return res
|
||||
|
||||
|
||||
def _as_validation_error(env, exc):
|
||||
""" Return the IntegrityError encapsuled in a nice ValidationError """
|
||||
|
||||
unknown = _('Unknown')
|
||||
model = DotDict({'_name': unknown.lower(), '_description': unknown})
|
||||
field = DotDict({'name': unknown.lower(), 'string': unknown})
|
||||
for _name, rclass in env.registry.items():
|
||||
if exc.diag.table_name == rclass._table:
|
||||
model = rclass
|
||||
field = model._fields.get(exc.diag.column_name) or field
|
||||
break
|
||||
|
||||
if exc.pgcode == errorcodes.NOT_NULL_VIOLATION:
|
||||
return ValidationError(_(
|
||||
"The operation cannot be completed:\n"
|
||||
"- Create/update: a mandatory field is not set.\n"
|
||||
"- Delete: another model requires the record being deleted."
|
||||
" If possible, archive it instead.\n\n"
|
||||
"Model: %(model_name)s (%(model_tech_name)s)\n"
|
||||
"Field: %(field_name)s (%(field_tech_name)s)\n",
|
||||
model_name=model._description,
|
||||
model_tech_name=model._name,
|
||||
field_name=field.string,
|
||||
field_tech_name=field.name,
|
||||
))
|
||||
|
||||
if exc.pgcode == errorcodes.FOREIGN_KEY_VIOLATION:
|
||||
return ValidationError(_(
|
||||
"The operation cannot be completed: another model requires "
|
||||
"the record being deleted. If possible, archive it instead.\n\n"
|
||||
"Model: %(model_name)s (%(model_tech_name)s)\n"
|
||||
"Constraint: %(constraint)s\n",
|
||||
model_name=model._description,
|
||||
model_tech_name=model._name,
|
||||
constraint=exc.diag.constraint_name,
|
||||
))
|
||||
|
||||
if exc.diag.constraint_name in env.registry._sql_constraints:
|
||||
return ValidationError(_(
|
||||
"The operation cannot be completed: %s",
|
||||
translate_sql_constraint(env.cr, exc.diag.constraint_name, env.context.get('lang', 'en_US'))
|
||||
))
|
||||
|
||||
return ValidationError(_("The operation cannot be completed: %s", exc.args[0]))
|
||||
|
||||
|
||||
def retrying(func, env):
|
||||
"""
|
||||
Call ``func`` until the function returns without serialisation
|
||||
error. A serialisation error occurs when two requests in independent
|
||||
cursors perform incompatible changes (such as writing different
|
||||
values on a same record). By default, it retries up to 5 times.
|
||||
|
||||
:param callable func: The function to call, you can pass arguments
|
||||
using :func:`functools.partial`:.
|
||||
:param odoo.api.Environment env: The environment where the registry
|
||||
and the cursor are taken.
|
||||
"""
|
||||
try:
|
||||
for tryno in range(1, MAX_TRIES_ON_CONCURRENCY_FAILURE + 1):
|
||||
tryleft = MAX_TRIES_ON_CONCURRENCY_FAILURE - tryno
|
||||
try:
|
||||
result = func()
|
||||
if not env.cr._closed:
|
||||
env.cr.flush() # submit the changes to the database
|
||||
break
|
||||
except (IntegrityError, OperationalError) as exc:
|
||||
if env.cr._closed:
|
||||
raise
|
||||
env.cr.rollback()
|
||||
env.reset()
|
||||
env.registry.reset_changes()
|
||||
if request:
|
||||
request.session = request._get_session_and_dbname()[0]
|
||||
# Rewind files in case of failure
|
||||
for filename, file in request.httprequest.files.items():
|
||||
if hasattr(file, "seekable") and file.seekable():
|
||||
file.seek(0)
|
||||
else:
|
||||
raise RuntimeError(f"Cannot retry request on input file {filename!r} after serialization failure") from exc
|
||||
if isinstance(exc, IntegrityError):
|
||||
raise _as_validation_error(env, exc) from exc
|
||||
if exc.pgcode not in PG_CONCURRENCY_ERRORS_TO_RETRY:
|
||||
raise
|
||||
if not tryleft:
|
||||
_logger.info("%s, maximum number of tries reached!", errorcodes.lookup(exc.pgcode))
|
||||
raise
|
||||
|
||||
wait_time = random.uniform(0.0, 2 ** tryno)
|
||||
_logger.info("%s, %s tries left, try again in %.04f sec...", errorcodes.lookup(exc.pgcode), tryleft, wait_time)
|
||||
time.sleep(wait_time)
|
||||
else:
|
||||
# handled in the "if not tryleft" case
|
||||
raise RuntimeError("unreachable")
|
||||
|
||||
except Exception:
|
||||
env.reset()
|
||||
env.registry.reset_changes()
|
||||
raise
|
||||
|
||||
if not env.cr.closed:
|
||||
env.cr.commit() # effectively commits and execute post-commits
|
||||
env.registry.signal_changes()
|
||||
return result
|
||||
|
||||
|
||||
def _traverse_containers(val, type_):
|
||||
""" Yields atoms filtered by specified ``type_`` (or type tuple), traverses
|
||||
through standard containers (non-string mappings or sequences) *unless*
|
||||
they're selected by the type filter
|
||||
"""
|
||||
from odoo.models import BaseModel
|
||||
if isinstance(val, type_):
|
||||
yield val
|
||||
elif isinstance(val, (str, bytes, BaseModel)):
|
||||
return
|
||||
elif isinstance(val, Mapping):
|
||||
for k, v in val.items():
|
||||
yield from _traverse_containers(k, type_)
|
||||
yield from _traverse_containers(v, type_)
|
||||
elif isinstance(val, Sequence):
|
||||
for v in val:
|
||||
yield from _traverse_containers(v, type_)
|
||||
20
odoo-bringout-oca-ocb-base/odoo/service/security.py
Normal file
20
odoo-bringout-oca-ocb-base/odoo/service/security.py
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import odoo
|
||||
import odoo.exceptions
|
||||
|
||||
def check(db, uid, passwd):
|
||||
res_users = odoo.registry(db)['res.users']
|
||||
return res_users.check(db, uid, passwd)
|
||||
|
||||
def compute_session_token(session, env):
|
||||
self = env['res.users'].browse(session.uid)
|
||||
return self._compute_session_token(session.sid)
|
||||
|
||||
def check_session(session, env):
|
||||
self = env['res.users'].browse(session.uid)
|
||||
expected = self._compute_session_token(session.sid)
|
||||
if expected and odoo.tools.misc.consteq(expected, session.session_token):
|
||||
return True
|
||||
return False
|
||||
1450
odoo-bringout-oca-ocb-base/odoo/service/server.py
Normal file
1450
odoo-bringout-oca-ocb-base/odoo/service/server.py
Normal file
File diff suppressed because it is too large
Load diff
11
odoo-bringout-oca-ocb-base/odoo/service/wsgi_server.py
Normal file
11
odoo-bringout-oca-ocb-base/odoo/service/wsgi_server.py
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import warnings
|
||||
import odoo.http
|
||||
|
||||
|
||||
def application(environ, start_response):
|
||||
|
||||
warnings.warn("The WSGI application entrypoint moved from "
|
||||
"odoo.service.wsgi_server.application to odoo.http.root "
|
||||
"in 15.3.",
|
||||
DeprecationWarning, stacklevel=1)
|
||||
return odoo.http.root(environ, start_response)
|
||||
Loading…
Add table
Add a link
Reference in a new issue