Remove database management from RPC
issue5384 review29311002
This commit is contained in:
1 parent
7e359d5c2b
commit
5967d21d46
9 files changed
+48
-384
No files matched your search
@@ -1,3 +1,5 @@
|
||||
* Remove super password
|
||||
* Remove database management from RPC
|
||||
* Remove ModelView.view_header_get
|
||||
* Remove string attribute from views
|
||||
* Don't check write access on model for button with groups
|
||||
|
||||
@@ -219,15 +219,6 @@ The time in seconds until a session expires.
|
||||
|
||||
Default: `600`
|
||||
|
||||
super_pwd
|
||||
~~~~~~~~~
|
||||
|
||||
The server password used to authenticate from the client for database
|
||||
management tasks. It is encrypted using using the Unix `crypt(3)` routine.
|
||||
A password can be generated using this command line::
|
||||
|
||||
python -c 'import getpass,crypt,random,string; print crypt.crypt(getpass.getpass(), "".join(random.sample(string.ascii_letters + string.digits, 8)))'
|
||||
|
||||
report
|
||||
------
|
||||
|
||||
|
||||
@@ -65,27 +65,6 @@ class DatabaseInterface(object):
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def dump(database_name):
|
||||
'''
|
||||
Dump a database
|
||||
|
||||
:param database_name: the database name
|
||||
:return: the dump
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def restore(database_name, data):
|
||||
'''
|
||||
Restore a database
|
||||
|
||||
:param database_name: the database name
|
||||
:param data: the data
|
||||
:return: True if succeed
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
def list(self):
|
||||
'''
|
||||
Get the list of database
|
||||
|
||||
@@ -9,7 +9,6 @@ from MySQLdb import IntegrityError as DatabaseIntegrityError
|
||||
from MySQLdb import OperationalError as DatabaseOperationalError
|
||||
import os
|
||||
import time
|
||||
import tempfile
|
||||
import urllib
|
||||
|
||||
from sql import Flavor, Expression
|
||||
@@ -126,79 +125,6 @@ class Database(DatabaseInterface):
|
||||
cursor.execute('DROP DATABASE `' + database_name + '`')
|
||||
cls._list_cache = None
|
||||
|
||||
@staticmethod
|
||||
def dump(database_name):
|
||||
from trytond.tools import exec_command_pipe
|
||||
|
||||
cmd = ['mysqldump', '--no-create-db']
|
||||
uri = parse_uri(config.get('database', 'uri'))
|
||||
if uri.username:
|
||||
cmd.append('--user=' + uri.username)
|
||||
if uri.hostname:
|
||||
cmd.append('--host=' + uri.hostname)
|
||||
if uri.port:
|
||||
cmd.append('--port=' + str(uri.port))
|
||||
if uri.password:
|
||||
cmd.append('--password=' + uri.password)
|
||||
cmd.append(database_name)
|
||||
|
||||
pipe = exec_command_pipe(*tuple(cmd))
|
||||
pipe.stdin.close()
|
||||
data = pipe.stdout.read()
|
||||
res = pipe.wait()
|
||||
if res:
|
||||
raise Exception('Couldn\'t dump database!')
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def restore(database_name, data):
|
||||
from trytond.tools import exec_command_pipe
|
||||
|
||||
database = Database().connect()
|
||||
cursor = database.cursor(autocommit=True)
|
||||
database.create(cursor, database_name)
|
||||
cursor.commit()
|
||||
cursor.close()
|
||||
|
||||
cmd = ['mysql']
|
||||
uri = parse_uri(config.get('database', 'uri'))
|
||||
if uri.username:
|
||||
cmd.append('--user=' + uri.username)
|
||||
if uri.hostname:
|
||||
cmd.append('--host=' + uri.hostname)
|
||||
if uri.port:
|
||||
cmd.append('--port=' + str(uri.port))
|
||||
if uri.password:
|
||||
cmd.append('--password=' + uri.password)
|
||||
cmd.append(database_name)
|
||||
|
||||
fd, file_name = tempfile.mkstemp()
|
||||
with os.fdopen(fd, 'wb+') as fd:
|
||||
fd.write(data)
|
||||
|
||||
cmd.append('<')
|
||||
cmd.append(file_name)
|
||||
|
||||
args2 = tuple(cmd)
|
||||
|
||||
pipe = exec_command_pipe(*args2)
|
||||
pipe.stdin.close()
|
||||
res = pipe.wait()
|
||||
os.remove(file_name)
|
||||
if res:
|
||||
raise Exception('Couldn\'t restore database')
|
||||
|
||||
database = Database(database_name).connect()
|
||||
cursor = database.cursor()
|
||||
if not cursor.test():
|
||||
cursor.close()
|
||||
database.close()
|
||||
raise Exception('Couldn\'t restore database!')
|
||||
cursor.close()
|
||||
database.close()
|
||||
Database._list_cache = None
|
||||
return True
|
||||
|
||||
def list(self):
|
||||
now = time.time()
|
||||
timeout = config.getint('session', 'timeout')
|
||||
|
||||
@@ -146,84 +146,6 @@ class Database(DatabaseInterface):
|
||||
RE_VERSION.search(version).groups()))
|
||||
return self._version_cache[self.name]
|
||||
|
||||
@staticmethod
|
||||
def dump(database_name):
|
||||
from trytond.tools import exec_command_pipe
|
||||
|
||||
cmd = ['pg_dump', '--format=c', '--no-owner']
|
||||
env = {}
|
||||
uri = parse_uri(config.get('database', 'uri'))
|
||||
if uri.username:
|
||||
cmd.append('--username=' + uri.username)
|
||||
if uri.hostname:
|
||||
cmd.append('--host=' + uri.hostname)
|
||||
if uri.port:
|
||||
cmd.append('--port=' + str(uri.port))
|
||||
if uri.password:
|
||||
# if db_password is set in configuration we should pass
|
||||
# an environment variable PGPASSWORD to our subprocess
|
||||
# see libpg documentation
|
||||
env['PGPASSWORD'] = uri.password
|
||||
cmd.append(database_name)
|
||||
|
||||
pipe = exec_command_pipe(*tuple(cmd), env=env)
|
||||
pipe.stdin.close()
|
||||
data = pipe.stdout.read()
|
||||
res = pipe.wait()
|
||||
if res:
|
||||
raise Exception('Couldn\'t dump database!')
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def restore(database_name, data):
|
||||
from trytond.tools import exec_command_pipe
|
||||
|
||||
database = Database().connect()
|
||||
connection = database.get_connection(autocommit=True)
|
||||
database.create(connection, database_name)
|
||||
database.close()
|
||||
|
||||
cmd = ['pg_restore', '--no-owner']
|
||||
env = {}
|
||||
uri = parse_uri(config.get('database', 'uri'))
|
||||
if uri.username:
|
||||
cmd.append('--username=' + uri.username)
|
||||
if uri.hostname:
|
||||
cmd.append('--host=' + uri.hostname)
|
||||
if uri.port:
|
||||
cmd.append('--port=' + str(uri.port))
|
||||
if uri.password:
|
||||
env['PGPASSWORD'] = uri.password
|
||||
cmd.append('--dbname=' + database_name)
|
||||
args2 = tuple(cmd)
|
||||
|
||||
if os.name == "nt":
|
||||
tmpfile = (os.environ['TMP'] or 'C:\\') + os.tmpnam()
|
||||
with open(tmpfile, 'wb') as fp:
|
||||
fp.write(data)
|
||||
args2 = list(args2)
|
||||
args2.append(' ' + tmpfile)
|
||||
args2 = tuple(args2)
|
||||
|
||||
pipe = exec_command_pipe(*args2, env=env)
|
||||
if not os.name == "nt":
|
||||
pipe.stdin.write(data)
|
||||
pipe.stdin.close()
|
||||
res = pipe.wait()
|
||||
if res:
|
||||
raise Exception('Couldn\'t restore database')
|
||||
|
||||
database = Database(database_name).connect()
|
||||
cursor = database.get_connection().cursor()
|
||||
if not database.test():
|
||||
cursor.close()
|
||||
database.close()
|
||||
raise Exception('Couldn\'t restore database!')
|
||||
cursor.close()
|
||||
database.close()
|
||||
Database._list_cache = None
|
||||
return True
|
||||
|
||||
def list(self):
|
||||
now = time.time()
|
||||
timeout = config.getint('session', 'timeout')
|
||||
|
||||
@@ -279,31 +279,6 @@ class Database(DatabaseInterface):
|
||||
os.remove(os.path.join(config.get('database', 'path'),
|
||||
database_name + '.sqlite'))
|
||||
|
||||
@staticmethod
|
||||
def dump(database_name):
|
||||
if database_name == ':memory:':
|
||||
raise Exception('Unable to dump memory database!')
|
||||
if os.sep in database_name:
|
||||
raise Exception('Wrong database name!')
|
||||
path = os.path.join(config.get('database', 'path'),
|
||||
database_name + '.sqlite')
|
||||
with open(path, 'rb') as file_p:
|
||||
data = file_p.read()
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def restore(database_name, data):
|
||||
if database_name == ':memory:':
|
||||
raise Exception('Unable to restore memory database!')
|
||||
if os.sep in database_name:
|
||||
raise Exception('Wrong database name!')
|
||||
path = os.path.join(config.get('database', 'path'),
|
||||
database_name + '.sqlite')
|
||||
if os.path.isfile(path):
|
||||
raise Exception('Database already exists!')
|
||||
with open(path, 'wb') as file_p:
|
||||
file_p.write(data)
|
||||
|
||||
def list(self):
|
||||
res = []
|
||||
listdir = [':memory:']
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
import logging
|
||||
import time
|
||||
import pydoc
|
||||
from functools import wraps
|
||||
|
||||
@@ -48,11 +47,6 @@ def rpc(request, database_name):
|
||||
methods = {
|
||||
'common.db.login': login,
|
||||
'common.db.logout': logout,
|
||||
'common.db.db_exist': db_exist,
|
||||
'common.db.create': create,
|
||||
'common.db.restore': restore,
|
||||
'common.db.drop': drop,
|
||||
'common.db.dump': dump,
|
||||
'system.listMethods': list_method,
|
||||
'system.methodHelp': help_method,
|
||||
'system.methodSignature': lambda *a: 'signatures not supported',
|
||||
@@ -96,7 +90,6 @@ def logout(request, database_name):
|
||||
def root(request, *args):
|
||||
methods = {
|
||||
'common.server.version': lambda *a: __version__,
|
||||
'common.db.list_lang': list_lang,
|
||||
'common.db.list': db_list,
|
||||
}
|
||||
return methods[request.method](request, *request.params)
|
||||
@@ -107,31 +100,6 @@ def home(request):
|
||||
return redirect('/index.html') # XXX find a better way
|
||||
|
||||
|
||||
def list_lang(*args):
|
||||
return [
|
||||
('bg_BG', 'Български'),
|
||||
('ca_ES', 'Català'),
|
||||
('cs_CZ', 'Čeština'),
|
||||
('de_DE', 'Deutsch'),
|
||||
('en_US', 'English'),
|
||||
('es_AR', 'Español (Argentina)'),
|
||||
('es_EC', 'Español (Ecuador)'),
|
||||
('es_ES', 'Español (España)'),
|
||||
('es_CO', 'Español (Colombia)'),
|
||||
('es_MX', 'Español (México)'),
|
||||
('fr_FR', 'Français'),
|
||||
('hu_HU', 'Magyar'),
|
||||
('it_IT', 'Italiano'),
|
||||
('lt_LT', 'Lietuvių'),
|
||||
('lo_LA', 'ລາວ'),
|
||||
('nl_NL', 'Nederlands'),
|
||||
('pt_BR', 'Português (Brasil)'),
|
||||
('ru_RU', 'Russian'),
|
||||
('sl_SI', 'Slovenščina'),
|
||||
('zh_CN', '中国(简体)'),
|
||||
]
|
||||
|
||||
|
||||
def db_exist(request, database_name):
|
||||
Database = backend.get('Database')
|
||||
try:
|
||||
@@ -237,119 +205,3 @@ def _dispatch(request, pool, *args, **kwargs):
|
||||
logger.debug('Reset session failed', exc_info=True)
|
||||
logger.debug('Result: %s', result)
|
||||
return result
|
||||
|
||||
|
||||
def create(request, database_name, password, lang, admin_password):
|
||||
'''
|
||||
Create a database
|
||||
|
||||
:param database_name: the database name
|
||||
:param password: the server password
|
||||
:param lang: the default language for the database
|
||||
:param admin_password: the admin password
|
||||
:return: True if succeed
|
||||
'''
|
||||
Database = backend.get('Database')
|
||||
security.check_super(password)
|
||||
res = False
|
||||
|
||||
try:
|
||||
with Transaction().start(None, 0, close=True, autocommit=True) \
|
||||
as transaction:
|
||||
transaction.database.create(transaction.connection, database_name)
|
||||
|
||||
with Transaction().start(database_name, 0) as transaction,\
|
||||
transaction.connection.cursor() as cursor:
|
||||
Database(database_name).init()
|
||||
cursor.execute(*ir_configuration.insert(
|
||||
[ir_configuration.language], [[lang]]))
|
||||
|
||||
pool = Pool(database_name)
|
||||
pool.init(update=['res', 'ir'], lang=[lang])
|
||||
with Transaction().start(database_name, 0) as transaction:
|
||||
User = pool.get('res.user')
|
||||
Lang = pool.get('ir.lang')
|
||||
language, = Lang.search([('code', '=', lang)])
|
||||
language.translatable = True
|
||||
language.save()
|
||||
users = User.search([('login', '!=', 'root')])
|
||||
User.write(users, {
|
||||
'language': language.id,
|
||||
})
|
||||
admin, = User.search([('login', '=', 'admin')])
|
||||
User.write([admin], {
|
||||
'password': admin_password,
|
||||
})
|
||||
Module = pool.get('ir.module')
|
||||
if Module:
|
||||
Module.update_list()
|
||||
res = True
|
||||
except Exception:
|
||||
logger.error('CREATE DB: %s failed', database_name, exc_info=True)
|
||||
raise
|
||||
else:
|
||||
logger.info('CREATE DB: %s', database_name)
|
||||
return res
|
||||
|
||||
|
||||
def drop(request, database_name, password):
|
||||
Database = backend.get('Database')
|
||||
security.check_super(password)
|
||||
database = Database(database_name)
|
||||
database.close()
|
||||
# Sleep to let connections close
|
||||
time.sleep(1)
|
||||
|
||||
with Transaction().start(None, 0, close=True, autocommit=True) \
|
||||
as transaction:
|
||||
try:
|
||||
database.drop(transaction.connection, database_name)
|
||||
except Exception:
|
||||
logger.error('DROP DB: %s failed', database_name, exc_info=True)
|
||||
raise
|
||||
else:
|
||||
logger.info('DROP DB: %s', database_name)
|
||||
Pool.stop(database_name)
|
||||
Cache.drop(database_name)
|
||||
return True
|
||||
|
||||
|
||||
def dump(request, database_name, password):
|
||||
Database = backend.get('Database')
|
||||
security.check_super(password)
|
||||
Database(database_name).close()
|
||||
# Sleep to let connections close
|
||||
time.sleep(1)
|
||||
|
||||
data = Database.dump(database_name)
|
||||
logger.info('DUMP DB: %s', database_name)
|
||||
if bytes == str:
|
||||
return bytearray(data)
|
||||
else:
|
||||
return bytes(data)
|
||||
|
||||
|
||||
def restore(request, database_name, password, data, update=False):
|
||||
Database = backend.get('Database')
|
||||
security.check_super(password)
|
||||
try:
|
||||
Database(database_name).connect()
|
||||
existing = True
|
||||
except Exception:
|
||||
existing = False
|
||||
if existing:
|
||||
raise Exception('Database already exists!')
|
||||
Database.restore(database_name, data)
|
||||
logger.info('RESTORE DB: %s', database_name)
|
||||
if update:
|
||||
with Transaction().start(database_name, 0) as transaction,\
|
||||
transaction.connection.cursor() as cursor:
|
||||
cursor.execute(*ir_lang.select(ir_lang.code,
|
||||
where=ir_lang.translatable))
|
||||
lang = [x[0] for x in cursor.fetchall()]
|
||||
cursor.execute(*ir_module.select(ir_module.name,
|
||||
where=(ir_module.state == 'installed')))
|
||||
update = [x[0] for x in cursor.fetchall()]
|
||||
Pool(database_name).init(update=update, lang=lang)
|
||||
logger.info('Update/Init succeed!')
|
||||
return True
|
||||
@@ -1,10 +1,5 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
try:
|
||||
import crypt
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from trytond.pool import Pool
|
||||
from trytond.config import config
|
||||
from trytond.transaction import Transaction
|
||||
@@ -56,13 +51,6 @@ def logout(dbname, user, session):
|
||||
return name
|
||||
|
||||
|
||||
def check_super(passwd):
|
||||
cryptedpasswd = config.get('session', 'super_pwd')
|
||||
if cryptedpasswd and crypt.crypt(passwd, cryptedpasswd) == cryptedpasswd:
|
||||
return True
|
||||
raise Exception('AccessDenied')
|
||||
|
||||
|
||||
def check(dbname, user, session):
|
||||
DatabaseOperationalError = backend.get('DatabaseOperationalError')
|
||||
for count in range(config.getint('database', 'retry'), -1, -1):
|
||||
|
||||
@@ -11,31 +11,28 @@ import operator
|
||||
from functools import wraps
|
||||
|
||||
from lxml import etree
|
||||
from sql import Table
|
||||
|
||||
from trytond.pool import Pool, isregisteredby
|
||||
from trytond import backend
|
||||
from trytond.model import Workflow
|
||||
from trytond.model.fields import get_eval_fields
|
||||
from trytond.protocols.dispatcher import create, drop
|
||||
from trytond.tools import is_instance_method
|
||||
from trytond.transaction import Transaction
|
||||
from trytond import security
|
||||
from trytond.cache import Cache
|
||||
|
||||
__all__ = ['POOL', 'DB_NAME', 'USER', 'USER_PASSWORD', 'CONTEXT',
|
||||
__all__ = ['POOL', 'DB_NAME', 'USER', 'CONTEXT',
|
||||
'install_module', 'ModuleTestCase', 'with_transaction',
|
||||
'doctest_setup', 'doctest_teardown', 'doctest_checker',
|
||||
'suite', 'all_suite', 'modules_suite']
|
||||
|
||||
Pool.start()
|
||||
USER = 1
|
||||
USER_PASSWORD = 'admin'
|
||||
CONTEXT = {}
|
||||
DB_NAME = os.environ['DB_NAME']
|
||||
DB = backend.get('Database')(DB_NAME)
|
||||
Pool.test = True
|
||||
POOL = Pool(DB_NAME)
|
||||
security.check_super = lambda *a, **k: True
|
||||
|
||||
|
||||
def install_module(name):
|
||||
@@ -278,26 +275,58 @@ class ModuleTestCase(unittest.TestCase):
|
||||
})
|
||||
|
||||
|
||||
def db_exist():
|
||||
def db_exist(name=DB_NAME):
|
||||
Database = backend.get('Database')
|
||||
database = Database().connect()
|
||||
return DB_NAME in database.list()
|
||||
return name in database.list()
|
||||
|
||||
|
||||
def create_db():
|
||||
if not db_exist():
|
||||
create(None, DB_NAME, None, 'en_US', USER_PASSWORD)
|
||||
def create_db(name=DB_NAME, lang='en_US'):
|
||||
Database = backend.get('Database')
|
||||
if not db_exist(name):
|
||||
with Transaction().start(None, 0, close=True, autocommit=True) \
|
||||
as transaction:
|
||||
transaction.database.create(transaction.connection, name)
|
||||
|
||||
with Transaction().start(name, 0) as transaction,\
|
||||
transaction.connection.cursor() as cursor:
|
||||
Database(name).init()
|
||||
ir_configuration = Table('ir_configuration')
|
||||
cursor.execute(*ir_configuration.insert(
|
||||
[ir_configuration.language], [[lang]]))
|
||||
|
||||
pool = Pool(name)
|
||||
pool.init(update=['res', 'ir'], lang=[lang])
|
||||
with Transaction().start(name, 0) as transaction:
|
||||
User = pool.get('res.user')
|
||||
Lang = pool.get('ir.lang')
|
||||
language, = Lang.search([('code', '=', lang)])
|
||||
language.translatable = True
|
||||
language.save()
|
||||
users = User.search([('login', '!=', 'root')])
|
||||
User.write(users, {
|
||||
'language': language.id,
|
||||
})
|
||||
Module = pool.get('ir.module')
|
||||
Module.update_list()
|
||||
|
||||
|
||||
def drop_db():
|
||||
if db_exist():
|
||||
drop(None, DB_NAME, None)
|
||||
def drop_db(name=DB_NAME):
|
||||
if db_exist(name):
|
||||
Database = backend.get('Database')
|
||||
database = Database(name)
|
||||
|
||||
with Transaction().start(None, 0, close=True, autocommit=True) \
|
||||
as transaction:
|
||||
database.drop(transaction.connection, name)
|
||||
Pool.stop(name)
|
||||
Cache.drop(name)
|
||||
|
||||
|
||||
def drop_create():
|
||||
if db_exist():
|
||||
drop_db()
|
||||
create_db()
|
||||
def drop_create(name=DB_NAME, lang='en_US'):
|
||||
if db_exist(name):
|
||||
drop_db(name)
|
||||
create_db(name, lang)
|
||||
|
||||
doctest_setup = lambda test: drop_create()
|
||||
doctest_teardown = lambda test: drop_db()
|
||||
|
||||
Reference in new issue
Block a user