1 # -*- coding: utf-8 -*-
8 from openerp import SUPERUSER_ID
15 _logger = logging.getLogger(__name__)
19 self_id_protect = threading.Semaphore()
21 # This should be moved to openerp.modules.db, along side initialize().
22 def _initialize_db(id, db_name, demo, lang, user_password):
25 self_actions[id]['progress'] = 0
26 cr = openerp.sql_db.db_connect(db_name).cursor()
27 openerp.modules.db.initialize(cr) # TODO this should be removed as it is done by pooler.restart_pool.
28 openerp.tools.config['lang'] = lang
32 pool = openerp.pooler.restart_pool(db_name, demo, self_actions[id],
33 update_module=True)[1]
35 cr = openerp.sql_db.db_connect(db_name).cursor()
38 modobj = pool.get('ir.module.module')
39 mids = modobj.search(cr, SUPERUSER_ID, [('state', '=', 'installed')])
40 modobj.update_translations(cr, SUPERUSER_ID, mids, lang)
42 # update admin's password and lang
43 values = {'password': user_password, 'lang': lang}
44 pool.get('res.users').write(cr, SUPERUSER_ID, [SUPERUSER_ID], values)
46 cr.execute('SELECT login, password FROM res_users ORDER BY login')
47 self_actions[id].update(users=cr.dictfetchall(), clean=True)
51 self_actions[id].update(clean=False, exception=e)
52 _logger.exception('CREATE DATABASE failed:')
53 self_actions[id]['traceback'] = traceback.format_exc()
57 def dispatch(method, params):
58 if method in [ 'create', 'get_progress', 'drop', 'dump',
60 'change_admin_password', 'migrate_databases',
61 'create_database', 'duplicate_database' ]:
64 security.check_super(passwd)
65 elif method in [ 'db_exist', 'list', 'list_lang', 'server_version' ]:
67 # No security check for these methods
70 raise KeyError("Method not found: %s" % method)
71 fn = globals()['exp_' + method]
74 def _create_empty_database(name):
75 db = openerp.sql_db.db_connect('postgres')
77 chosen_template = openerp.tools.config['db_template']
78 cr.execute("""SELECT datname
80 WHERE datname = %s """,
83 raise openerp.exceptions.Warning(" %s database already exists!" % name )
85 cr.autocommit(True) # avoid transaction block
86 cr.execute("""CREATE DATABASE "%s" ENCODING 'unicode' TEMPLATE "%s" """ % (name, chosen_template))
90 def exp_create(db_name, demo, lang, user_password='admin'):
91 self_id_protect.acquire()
95 self_id_protect.release()
97 self_actions[id] = {'clean': False}
99 _create_empty_database(db_name)
101 _logger.info('CREATE DATABASE %s', db_name.lower())
102 create_thread = threading.Thread(target=_initialize_db,
103 args=(id, db_name, demo, lang, user_password))
104 create_thread.start()
105 self_actions[id]['thread'] = create_thread
108 def exp_create_database(db_name, demo, lang, user_password='admin'):
109 """ Similar to exp_create but blocking."""
110 self_id_protect.acquire()
114 self_id_protect.release()
116 self_actions[id] = {'clean': False}
118 _logger.info('Create database `%s`.', db_name)
119 _create_empty_database(db_name)
120 _initialize_db(id, db_name, demo, lang, user_password)
123 def exp_duplicate_database(db_original_name, db_name):
124 _logger.info('Duplicate database `%s` to `%s`.', db_original_name, db_name)
125 openerp.sql_db.close_db(db_original_name)
126 db = openerp.sql_db.db_connect('postgres')
129 cr.autocommit(True) # avoid transaction block
130 cr.execute("""CREATE DATABASE "%s" ENCODING 'unicode' TEMPLATE "%s" """ % (db_name, db_original_name))
135 def exp_get_progress(id):
136 if self_actions[id]['thread'].isAlive():
137 # return openerp.modules.init_progress[db_name]
138 return min(self_actions[id].get('progress', 0),0.95), []
140 clean = self_actions[id]['clean']
142 users = self_actions[id]['users']
144 # Remove the None passwords as they can't be marshalled by XML-RPC.
145 if user['password'] is None:
146 user['password'] = ''
150 e = self_actions[id]['exception'] # TODO this seems wrong: actions[id]['traceback'] is set, but not 'exception'.
154 def exp_drop(db_name):
155 if not exp_db_exist(db_name):
157 openerp.modules.registry.RegistryManager.delete(db_name)
158 openerp.sql_db.close_db(db_name)
160 db = openerp.sql_db.db_connect('postgres')
162 cr.autocommit(True) # avoid transaction block
164 # Try to terminate all other connections that might prevent
165 # dropping the database
168 # PostgreSQL 9.2 renamed pg_stat_activity.procpid to pid:
169 # http://www.postgresql.org/docs/9.2/static/release-9-2.html#AEN110389
170 pid_col = 'pid' if cr._cnx.server_version >= 90200 else 'procpid'
172 cr.execute("""SELECT pg_terminate_backend(%(pid_col)s)
173 FROM pg_stat_activity
174 WHERE datname = %%s AND
175 %(pid_col)s != pg_backend_pid()""" % {'pid_col': pid_col},
181 cr.execute('DROP DATABASE "%s"' % db_name)
183 _logger.error('DROP DB: %s failed:\n%s', db_name, e)
184 raise Exception("Couldn't drop database %s: %s" % (db_name, e))
186 _logger.info('DROP DB: %s', db_name)
191 @contextlib.contextmanager
192 def _set_pg_password_in_environment():
193 """ On Win32, pg_dump (and pg_restore) require that
194 :envvar:`PGPASSWORD` be set
196 This context management method handles setting
197 :envvar:`PGPASSWORD` iif win32 and the envvar is not already
198 set, and removing it afterwards.
200 if os.name != 'nt' or os.environ.get('PGPASSWORD'):
203 os.environ['PGPASSWORD'] = openerp.tools.config['db_password']
207 del os.environ['PGPASSWORD']
210 def exp_dump(db_name):
211 with _set_pg_password_in_environment():
212 cmd = ['pg_dump', '--format=c', '--no-owner']
213 if openerp.tools.config['db_user']:
214 cmd.append('--username=' + openerp.tools.config['db_user'])
215 if openerp.tools.config['db_host']:
216 cmd.append('--host=' + openerp.tools.config['db_host'])
217 if openerp.tools.config['db_port']:
218 cmd.append('--port=' + str(openerp.tools.config['db_port']))
221 stdin, stdout = openerp.tools.exec_pg_command_pipe(*tuple(cmd))
228 'DUMP DB: %s failed! Please verify the configuration of the database password on the server. '
229 'It should be provided as a -w <PASSWD> command-line option, or as `db_password` in the '
230 'server configuration file.\n %s', db_name, data)
231 raise Exception, "Couldn't dump database"
232 _logger.info('DUMP DB successful: %s', db_name)
234 return base64.encodestring(data)
236 def exp_restore(db_name, data):
237 with _set_pg_password_in_environment():
238 if exp_db_exist(db_name):
239 _logger.warning('RESTORE DB: %s already exists', db_name)
240 raise Exception, "Database already exists"
242 _create_empty_database(db_name)
244 cmd = ['pg_restore', '--no-owner']
245 if openerp.tools.config['db_user']:
246 cmd.append('--username=' + openerp.tools.config['db_user'])
247 if openerp.tools.config['db_host']:
248 cmd.append('--host=' + openerp.tools.config['db_host'])
249 if openerp.tools.config['db_port']:
250 cmd.append('--port=' + str(openerp.tools.config['db_port']))
251 cmd.append('--dbname=' + db_name)
254 buf=base64.decodestring(data)
256 tmpfile = (os.environ['TMP'] or 'C:\\') + os.tmpnam()
257 file(tmpfile, 'wb').write(buf)
259 args2.append(tmpfile)
261 stdin, stdout = openerp.tools.exec_pg_command_pipe(*args2)
262 if not os.name == "nt":
263 stdin.write(base64.decodestring(data))
267 raise Exception, "Couldn't restore database"
268 _logger.info('RESTORE DB: %s', db_name)
272 def exp_rename(old_name, new_name):
273 openerp.modules.registry.RegistryManager.delete(old_name)
274 openerp.sql_db.close_db(old_name)
276 db = openerp.sql_db.db_connect('postgres')
278 cr.autocommit(True) # avoid transaction block
281 cr.execute('ALTER DATABASE "%s" RENAME TO "%s"' % (old_name, new_name))
283 _logger.error('RENAME DB: %s -> %s failed:\n%s', old_name, new_name, e)
284 raise Exception("Couldn't rename database %s to %s: %s" % (old_name, new_name, e))
286 fs = os.path.join(openerp.tools.config['root_path'], 'filestore')
287 if os.path.exists(os.path.join(fs, old_name)):
288 os.rename(os.path.join(fs, old_name), os.path.join(fs, new_name))
290 _logger.info('RENAME DB: %s -> %s', old_name, new_name)
295 def exp_db_exist(db_name):
296 ## Not True: in fact, check if connection to database is possible. The database may exists
297 return bool(openerp.sql_db.db_connect(db_name))
299 def exp_list(document=False):
300 if not openerp.tools.config['list_db'] and not document:
301 raise openerp.exceptions.AccessDenied()
302 chosen_template = openerp.tools.config['db_template']
303 templates_list = tuple(set(['template0', 'template1', 'postgres', chosen_template]))
304 db = openerp.sql_db.db_connect('postgres')
308 db_user = openerp.tools.config["db_user"]
309 if not db_user and os.name == 'posix':
311 db_user = pwd.getpwuid(os.getuid())[0]
313 cr.execute("select usename from pg_user where usesysid=(select datdba from pg_database where datname=%s)", (openerp.tools.config["db_name"],))
315 db_user = res and str(res[0])
317 cr.execute("select datname from pg_database where datdba=(select usesysid from pg_user where usename=%s) and datname not in %s order by datname", (db_user, templates_list))
319 cr.execute("select datname from pg_database where datname not in %s order by datname", (templates_list,))
320 res = [str(name) for (name,) in cr.fetchall()]
328 def exp_change_admin_password(new_password):
329 openerp.tools.config['admin_passwd'] = new_password
330 openerp.tools.config.save()
334 return openerp.tools.scan_languages()
336 def exp_server_version():
337 """ Return the version of the server
338 Used by the client to verify the compatibility with its own version
340 return release.version
342 def exp_migrate_databases(databases):
344 _logger.info('migrate database %s', db)
345 openerp.tools.config['update']['base'] = True
346 openerp.pooler.restart_pool(db, force_demo=False, update_module=True)
349 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: