diff --git a/addons/bus/models/bus.py b/addons/bus/models/bus.py index e58bc8f02762a..53fdcd38e73bb 100644 --- a/addons/bus/models/bus.py +++ b/addons/bus/models/bus.py @@ -61,6 +61,13 @@ def sendmany(self, notifications): # transaction is not commited yet, there will be nothing to fetch, # and the longpolling will return no notification. def notify(): + wf = odoo.tools.config.get('webfaction', default=False) + if wf: + db_base = odoo.tools.config["webfaction_db_base"] + with odoo.sql_db.db_connect(db_base).cursor() as cr2: + cr2.execute("notify imbus, %s", (json_dump(list(channels)),)) + return + with odoo.sql_db.db_connect('postgres').cursor() as cr: cr.execute("notify imbus, %s", (json_dump(list(channels)),)) self._cr.after('commit', notify) @@ -155,6 +162,29 @@ def poll(self, dbname, channels, last, options=None, timeout=TIMEOUT): def loop(self): """ Dispatch postgres notifications to the relevant polling threads/greenlets """ _logger.info("Bus.loop listen imbus on db postgres") + wf = odoo.tools.config.get('webfaction', default=False) + if wf: + db_base = odoo.tools.config["webfaction_db_base"] + with odoo.sql_db.db_connect(db_base).cursor() as cr: + conn = cr._cnx + cr.execute("listen imbus") + cr.commit(); + while True: + if select.select([conn], [], [], TIMEOUT) == ([], [], []): + pass + else: + conn.poll() + channels = [] + while conn.notifies: + channels.extend(json.loads(conn.notifies.pop().payload)) + # dispatch to local threads/greenlets + events = set() + for channel in channels: + events.update(self.channels.pop(hashable(channel), [])) + for event in events: + event.set() + return + with odoo.sql_db.db_connect('postgres').cursor() as cr: conn = cr._cnx cr.execute("listen imbus") diff --git a/odoo/service/db.py b/odoo/service/db.py index 2696905c1706d..347aeba2c243f 100644 --- a/odoo/service/db.py +++ b/odoo/service/db.py @@ -78,6 +78,29 @@ def _initialize_db(id, db_name, demo, lang, user_password, login='admin', countr _logger.exception('CREATE DATABASE failed:') def _create_empty_database(name): + wf = odoo.tools.config.get('webfaction', default=False) + if wf: + _logger.info("WebFaction: Creating database '%s'...", name) + db_base = odoo.tools.config["webfaction_db_base"] + db_user = odoo.tools.config["db_user"] + res = exec_webfaction_sql(db_base, db_user, "SELECT datname FROM pg_database WHERE datname = %s", (name,)) + if res!=[]: + raise DatabaseExists("database %r already exists!" % (name,)) + + db_password = odoo.tools.config["db_password"] + + import xmlrpclib + server = xmlrpclib.ServerProxy(odoo.tools.config['webfaction_url']) + session_id, _ = server.login(odoo.tools.config['webfaction_user'], odoo.tools.config['webfaction_password']) + dbus = [ y['username'] for y in filter(lambda x: x['db_type']=='postgresql', server.list_db_users(session_id))] + + server.create_db(session_id, name, 'postgresql', db_password) + server.make_user_owner_of_db(session_id, db_user, name, 'postgresql') + if name not in dbus: + server.delete_db_user(session_id, name, 'postgresql') + _logger.info("WebFaction: Database '%s' created.", name) + return + db = odoo.sql_db.db_connect('postgres') with closing(db.cursor()) as cr: chosen_template = odoo.tools.config['db_template'] @@ -97,6 +120,10 @@ def exp_create_database(db_name, demo, lang, user_password='admin', login='admin return True def exp_duplicate_database(db_original_name, db_name): + wf = odoo.tools.config.get('webfaction', default=False) + if wf: + raise Exception("A WebFaction database duplication is not yet implemented") + _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') @@ -139,6 +166,16 @@ def exp_drop(db_name): odoo.modules.registry.Registry.delete(db_name) odoo.sql_db.close_db(db_name) + wf = odoo.tools.config.get('webfaction', default=False) + if wf: + _logger.info("WebFaction: Dropping database '%s'...", db_name) + import xmlrpclib + server = xmlrpclib.ServerProxy(odoo.tools.config['webfaction_url']) + session_id, _ = server.login(odoo.tools.config['webfaction_user'], odoo.tools.config['webfaction_password']) + server.delete_db(session_id, db_name, 'postgresql') + _logger.info("WebFaction: Database '%s' dropped.", db_name) + return True + db = odoo.sql_db.db_connect('postgres') with closing(db.cursor()) as cr: cr.autocommit(True) # avoid transaction block @@ -282,6 +319,10 @@ def restore_db(db, dump_file, copy=False): _logger.info('RESTORE DB: %s', db) def exp_rename(old_name, new_name): + wf = odoo.tools.config.get('webfaction', default=False) + if wf: + raise Exception("A WebFaction database cannot be renamed. Not supported by Webfaction API") + odoo.modules.registry.Registry.delete(old_name) odoo.sql_db.close_db(old_name) @@ -336,6 +377,19 @@ def list_dbs(force=False): chosen_template = odoo.tools.config['db_template'] templates_list = tuple(set(['postgres', chosen_template])) + + wf = odoo.tools.config.get('webfaction', default=False) + if wf: + _logger.info("WebFaction: Listing databases (force=%s)..." % force) + db_base = odoo.tools.config["webfaction_db_base"] + templates_list = tuple(list(templates_list) + [db_base]) + db_user = odoo.tools.config["db_user"] + res = exec_webfaction_sql(db_base, db_user, "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)) + res = [odoo.tools.ustr(name) for (name,) in res] + _logger.info("WebFaction: Database listing done. %s" % res) + res.sort() + return res + db = odoo.sql_db.db_connect('postgres') with closing(db.cursor()) as cr: try: @@ -396,3 +450,40 @@ def dispatch(method, params): return g[exp_method_name](*params) else: raise KeyError("Method not found: %s" % method) + +def exec_webfaction_sql(db_base, db_user, sql, params=()): + db = odoo.sql_db.db_connect(db_base) + ok = False + while not ok: + try: + with closing(db.cursor()) as cr: + cr.execute(sql, params) + res = cr.fetchall() + ok = True + except psycopg2.OperationalError as e: + # The next use of regular expressions is just a temporary solution. It's dirty, unelegant, unstable but fast to implement, really fast. + # Of course, it needs to be corrected ASAP and implement an elegant solution. + import re + m1 = re.search('^FATAL: no pg_hba.conf entry for host "[0-9.]+", user "%(db_user)s", database "%(db_base)s", SSL on\nFATAL: no pg_hba.conf entry for host "[0-9.]+", user "%(db_user)s", database "%(db_base)s", SSL off\n$' % dict(db_user=db_user, db_base=db_base), unicode(e)) + m2 = re.search('^FATAL: no pg_hba.conf entry for host "\[local\]", user "%(db_user)s", database "%(db_base)s", SSL off\n$' % dict(db_user=db_user, db_base=db_base), unicode(e)) + if m1 is None and m2 is None: + m = re.search('^FATAL: permission denied for database "%s"\nDETAIL: User does not have CONNECT privilege.$' % db_base, unicode(e)) + if m is not None: + _logger.info('WebFaction: Base database "%s" already created but user "%s" has no permissions -> Granting full access...' % (db_base, db_user)) + import xmlrpclib + server = xmlrpclib.ServerProxy(odoo.tools.config['webfaction_url']) + session_id, _ = server.login(odoo.tools.config['webfaction_user'], odoo.tools.config['webfaction_password']) + server.grant_db_permissions(session_id, db_user, db_base, 'postgresql') + _logger.info('WebFaction: User "%s" permissions to base database "%s" granted successfully.' % (db_user, db_base)) + else: + raise Exception("Unexpected error message string '%s'" % unicode(e)) + else: + _logger.info('WebFaction: Base database "%s" does not exist -> Creating...' % db_base) + import xmlrpclib + server = xmlrpclib.ServerProxy(odoo.tools.config['webfaction_url']) + session_id, _ = server.login(odoo.tools.config['webfaction_user'], odoo.tools.config['webfaction_password']) + server.create_db(session_id, db_base, 'postgresql', db_base) + server.make_user_owner_of_db(session_id, db_user, db_base, 'postgresql') + server.delete_db_user(session_id, db_base, 'postgresql') + _logger.info('WebFaction: Base database "%s" created successfully.' % db_base) + return res