=== modified file 'addons/openerp/controllers/database.py' --- addons/openerp/controllers/database.py 2017-07-11 12:19:46 +0000 +++ addons/openerp/controllers/database.py 2017-11-21 17:05:56 +0000 @@ -22,10 +22,12 @@ import re import time import os +import sys import cherrypy import formencode +from openobject import paths from openobject.controllers import BaseController from openobject.tools import url, expose, redirect, validate, error_handler import openobject @@ -35,6 +37,9 @@ from openerp.utils import rpc, get_server_version, is_server_local, serve_file from tempfile import NamedTemporaryFile import shutil +import ConfigParser +from ConfigParser import NoOptionError, NoSectionError +import threading def get_lang_list(): langs = [('en_US', 'English (US)')] @@ -50,6 +55,8 @@ except: return [] +class DatabaseExist(Exception): pass + class ReplacePasswordField(openobject.widgets.PasswordField): params = { 'autocomplete': 'Autocomplete field', @@ -116,6 +123,23 @@ validator = openobject.validators.Schema(chained_validators=[formencode.validators.FieldsMatch("admin_password","confirm_password")]) +class FormAutoCreate(DBForm): + name = "auto_create" + string = _('Instance Auto Creation') + action = '/openerp/database/do_auto_create' + submit_text = _('Start auto creation') + #form_attrs = {'onsubmit': 'return window.confirm(_("Do you really want to drop the selected database?"))'} + fields = [ + ReplacePasswordField(name='password', label=_('Super admin password:')), + ] + + +class AutoCreateProgress(DBForm): + name = "get_auto_create_progress" + string = _('Auto Creation Progress') + action = '/openerp/database/get_auto_create_progress' + + class FormDrop(DBForm): name = "drop" string = _('Drop database') @@ -127,6 +151,7 @@ ReplacePasswordField(name='password', label=_('Drop password:')), ] + class FormBackup(DBForm): name = "backup" string = _('Backup database') @@ -137,10 +162,12 @@ ReplacePasswordField(name='password', label=_('Backup password:')), ] + class FileField(openobject.widgets.FileField): def adjust_value(self, value, **params): return False + class FormRestore(DBForm): name = "restore" string = _('Restore database') @@ -169,6 +196,7 @@ _FORMS = { + 'auto_create': FormAutoCreate(), 'create': FormCreate(), 'drop': FormDrop(), 'backup': FormBackup(), @@ -176,9 +204,13 @@ 'password': FormPassword() } + class DatabaseCreationError(Exception): pass + + class DatabaseCreationCrash(DatabaseCreationError): pass + class Database(BaseController): _cp_path = "/openerp/database" @@ -248,7 +280,7 @@ break else: time.sleep(1) - except: + except Exception as e: raise DatabaseCreationCrash() except DatabaseCreationCrash: self.msg = {'message': (_("The server crashed during installation.\nWe suggest you to drop this database.")), @@ -258,15 +290,252 @@ self.msg = {'message': _('Bad super admin password'), 'title' : e.title} return self.create() - except Exception: + except Exception as e: self.msg = {'message':_("Could not create database.")} - return self.create() if ok: raise redirect('/openerp/menu', {'next': '/openerp/home'}) raise redirect('/openerp/login', db=dbname) + @expose(template="/openerp/controllers/templates/auto_create.mako") + def auto_create(self, tg_errors=None, **kw): + form = _FORMS['auto_create'] + error = self.msg + self.msg = {} + return dict(form=form, error=error) + + @expose() + def get_auto_create_progress(self, **kw): + config_file_name = 'uf_auto_install.conf' + if sys.platform == 'win32': + config_file_path = os.path.join(paths.root(), '..', 'UFautoInstall', config_file_name) + else: + config_file_path = os.path.join(paths.root(), '..', 'unifield-server', 'UFautoInstall', config_file_name) + if not os.path.exists(config_file_path): + return False + config = ConfigParser.ConfigParser() + config.read(config_file_path) + dbname = config.get('instance', 'db_name') + self.resume, self.progress, self.state, self.error, monitor_status = rpc.session.execute_db('creation_get_resume_progress', dbname) + my_dict = { + 'resume': self.resume, + 'progress': self.progress, + 'state': self.state, + 'error': self.error, + 'monitor_status': monitor_status, + } + import json + return json.dumps(my_dict) + + @expose(template="/openerp/controllers/templates/auto_create_progress.mako") + def auto_create_progress(self, tg_errors=None, **kw): + finish = "" + finished = "False" + data_collected = "False" + return dict(finish=finish, percent=self.progress, resume=self.resume, total=finished, + data_collected=data_collected) + + def check_not_empty_string(self, config, section, option): + if not config.has_option(section, option) or not config.get(section, option): + self.msg = {'message': ustr(_('The option \'%s\' from section \'[%s]\' cannot be empty, please set a value.') % (option, section)), + 'title': ustr(_('Empty option'))} + + def check_mandatory_int(self, config, section, option): + try: + value = config.getint(section, option) + except ValueError: + self.msg = {'message': ustr(_('The option \'%s\' from section \'[%s]\' have to be a int.') % (option, section)), + 'title': ustr(_('Wrong option value'))} + return + if not value: + self.msg = {'message': ustr(_('The option \'%s\' from section \'[%s]\' cannot be empty, please set a value.') % (option, section)), + 'title': ustr(_('Empty option'))} + + def check_possible_value(self, config, section, option, possible_values): + value = config.get(section, option) + if value not in possible_values: + self.msg = {'message': ustr(_('The option \'%s\' from section \'[%s]\' have to be one of those values: %r. (currently it is \'%s\').') % (option, section, possible_values, value)), + 'title': ustr(_('Wrong option'))} + + def check_config_file(self, file_path): + ''' + perform some basic checks to avoid crashing later + ''' + if not os.path.exists(file_path): + self.msg = {'message': ustr(_("The auto creation config file '%s' does not exists.") % file_path), + 'title': ustr(_('Auto creation file not found'))} + + config = ConfigParser.ConfigParser() + config.read(file_path) + try: + db_name = config.get('instance', 'db_name') + if not re.match('^[a-zA-Z][a-zA-Z0-9_-]+$', db_name): + self.msg = {'message': ustr(_("You must avoid all accents, space or special characters.")), + 'title': ustr(_('Bad database name'))} + return + + admin_password = config.get('instance', 'admin_password') + res = rpc.session.execute_db('check_super_password_validity', admin_password) + if res is not True: + self.msg = {'message': res, + 'title': ustr(_('Bad admin password'))} + return + + # check the mandatory string fields have a value + not_empty_string_option_list = ( + ('instance', 'oc'), + ('instance', 'admin_password'), + ('instance', 'sync_user'), + ('instance', 'sync_pwd'), + ('instance', 'instance_level'), + ('instance', 'parent_instance'), + ('instance', 'lang'), + ('backup', 'auto_bck_path'), + ('reconfigure', 'prop_instance_code'), + ('reconfigure', 'address_contact_name'), + ('reconfigure', 'address_street'), + ('reconfigure', 'address_street2'), + ('reconfigure', 'address_zip'), + ('reconfigure', 'address_city'), + ('reconfigure', 'address_country'), + ('reconfigure', 'address_phone'), + ('reconfigure', 'address_email'), + ('reconfigure', 'delivery_process'), + ('reconfigure', 'functional_currency'), + ) + for section, option in not_empty_string_option_list: + self.check_not_empty_string(config, section, option) + if self.msg: + return + + # check mandatory integer values + not_empty_int_option_list = ( + ('backup', 'auto_bck_interval_nb'), + ('partner', 'external_account_receivable'), + ('partner', 'external_account_payable'), + ('partner', 'internal_account_receivable'), + ('partner', 'internal_account_payable'), + ('company', 'default_counterpart'), + ('company', 'salaries_default_account'), + ('company', 'rebilling_intersection_account'), + ('company', 'intermission_counterpart'), + ('company', 'counterpart_bs_debit_balance'), + ('company', 'counterpart_bs_crebit_balance'), + ('company', 'debit_account_pl_positive'), + ('company', 'credit_account_pl_negative'), + ('company', 'scheduler_range_days'), + ) + for section, option in not_empty_int_option_list: + self.check_mandatory_int(config, section, option) + if self.msg: + return + + # check value is in possibles values + possible_value_list = ( + ('instance', 'instance_level', ('coordo', 'project')), + ('instance', 'lang', ('fr_MF', 'es_MF', 'en_MF')), + ('backup', 'auto_bck_interval_unit', ('minutes', 'hours', 'work_days', 'days', 'weeks', 'months')), + ('reconfigure', 'delivery_process', ('complex', 'simple')), + ) + + for section, option, possible_values in possible_value_list: + self.check_possible_value(config, section, option, possible_values) + if self.msg: + return + + except NoOptionError as e: + self.msg = {'message': ustr(_('No option \'%s\' found for the section \'[%s]\' in the config file \'%s\'') % (e.option, e.section, file_path)), + 'title': ustr(_('Option missing in configuration file'))} + return + except NoSectionError as e: + self.msg = {'message': ustr(_('No section \'%s\' found in the config file \'%s\'') % (e.section, file_path)), + 'title': ustr(_('Option missing in configuration file'))} + return + + def database_creation(self, password, dbname, admin_password): + try: + res = rpc.session.execute_db('create', password, dbname, False, 'en_US', admin_password) + while True: + try: + progress, users = rpc.session.execute_db('get_progress', password, res) + if progress == 1.0: + for x in users: + if x['login'] == 'admin': + rpc.session.login(dbname, 'admin', x['password']) + ok = True + break + else: + time.sleep(1) + except Exception as e: + raise DatabaseCreationCrash() + except DatabaseCreationCrash: + self.msg = {'message': (_("The server crashed during installation.\nWe suggest you to drop this database.")), + 'title': (_('Error during database creation'))} + except openobject.errors.AccessDenied, e: + self.msg = {'message': _('Bad super admin password'), + 'title' : e.title} + + def background_auto_creation(self, password, dbname, db_exists, config_dict): + if not db_exists: + # create database + self.database_creation(password, dbname, config_dict['instance'].get('admin_password')) + + rpc.session.execute_db('instance_auto_creation', password, dbname) + self.resume, self.progress, self.state, self.error, monitor_status = rpc.session.execute_db('creation_get_resume_progress', dbname) + + @expose() + @validate(form=_FORMS['auto_create']) + @error_handler(auto_create) + def do_auto_create(self, password, **kw): + self.msg = {} + self.progress = 0.03 + self.state = 'draft' + try: + config_file_name = 'uf_auto_install.conf' + if sys.platform == 'win32': + config_file_path = os.path.join(paths.root(), '..', 'UFautoInstall', config_file_name) + else: + config_file_path = os.path.join(paths.root(), '..', 'unifield-server', 'UFautoInstall', config_file_name) + + self.check_config_file(config_file_path) + if self.msg: + return self.auto_create() + config = ConfigParser.ConfigParser() + config.read(config_file_path) + + config_dict = {x:dict(config.items(x)) for x in config.sections()} + dbname = config_dict['instance'].get('db_name') + db_exists = False + + # check the database not already exists + if dbname in get_db_list(): + db_exists = True + self.resume = _('Database with this name exists, resume from the last point...\n') + else: + self.resume = _('Empty database creation in progress...\n') + #raise DatabaseExist + + create_thread = threading.Thread(target=self.background_auto_creation, + args=(password, dbname, db_exists, + config_dict)) + create_thread.start() + create_thread.join(0.5) + + except openobject.errors.AccessDenied, e: + self.msg = {'message': _('Wrong password'), + 'title' : e.title} + except DatabaseExist: + pass + #self.msg = {'message': ustr(_('The database already exist')), + # 'title': 'Database exist'} + except Exception as e: + self.msg = {'message' : _("Could not auto create database: %s") % e} + + if self.msg: + return self.auto_create() + return self.auto_create_progress() + @expose(template="/openerp/controllers/templates/database.mako") def drop(self, tg_errors=None, **kw): form = _FORMS['drop'] === added file 'addons/openerp/controllers/templates/auto_create.mako' --- addons/openerp/controllers/templates/auto_create.mako 1970-01-01 00:00:00 +0000 +++ addons/openerp/controllers/templates/auto_create.mako 2017-11-21 17:05:56 +0000 @@ -0,0 +1,70 @@ +<%inherit file="/openerp/controllers/templates/base_dispatch.mako"/> +<%def name="current_for(name)"><% + if form.name == name: context.write('current') +%>%def> +<%def name="header()"> +
If you have checked the following points, you can start the +process of instance auto creation by login with the Super admin password. Points to check: +