web_services: use pg_dump in batch mode.
[odoo/odoo.git] / bin / service / web_services.py
index c9113cc..3c21202 100644 (file)
 ##############################################################################
 
 import base64
-import logging
 import os
 import security
-import string
 import thread
 import threading
 import time
@@ -38,7 +36,8 @@ import release
 import sql_db
 import tools
 import locale
-logging.basicConfig()
+import logging
+from cStringIO import StringIO
 
 class db(netsvc.ExportService):
     def __init__(self, name="db"):
@@ -68,6 +67,14 @@ class db(netsvc.ExportService):
     
     def new_dispatch(self,method,auth,params):
         pass
+    def _create_empty_database(self, name):
+        db = sql_db.db_connect('template1')
+        cr = db.cursor()
+        try:
+            cr.autocommit(True) # avoid transaction block
+            cr.execute("""CREATE DATABASE "%s" ENCODING 'unicode' TEMPLATE "template0" """ % name)
+        finally:
+            cr.close()
 
     def exp_create(self, db_name, demo, lang, user_password='admin'):
         self.id_protect.acquire()
@@ -77,21 +84,13 @@ class db(netsvc.ExportService):
 
         self.actions[id] = {'clean': False}
 
-        db = sql_db.db_connect('template1')
-        cr = db.cursor()
-        try:
-            cr.autocommit(True) # avoid transaction block
-            cr.execute('CREATE DATABASE "%s" ENCODING \'unicode\'' % db_name)
-        finally:
-            cr.close()
-            del db
+        self._create_empty_database(db_name)
 
         class DBInitialize(object):
             def __call__(self, serv, id, db_name, demo, lang, user_password='admin'):
                 cr = None
                 try:
                     serv.actions[id]['progress'] = 0
-                    clean = False
                     cr = sql_db.db_connect(db_name).cursor()
                     tools.init_db(cr)
                     cr.commit()
@@ -119,7 +118,6 @@ class db(netsvc.ExportService):
                 except Exception, e:
                     serv.actions[id]['clean'] = False
                     serv.actions[id]['exception'] = e
-                    from cStringIO import StringIO
                     import traceback
                     e_str = StringIO()
                     traceback.print_exc(file=e_str)
@@ -146,11 +144,11 @@ class db(netsvc.ExportService):
             clean = self.actions[id]['clean']
             if clean:
                 users = self.actions[id]['users']
-                del self.actions[id]
+                self.actions.pop(id)
                 return (1.0, users)
             else:
                 e = self.actions[id]['exception']
-                del self.actions[id]
+                self.actions.pop(id)
                 raise Exception, e
 
     def exp_drop(self, db_name):
@@ -188,7 +186,7 @@ class db(netsvc.ExportService):
 
         self._set_pg_psw_env_var()
 
-        cmd = ['pg_dump', '--format=c', '--no-owner']
+        cmd = ['pg_dump', '--format=c', '--no-owner' , '-w']
         if tools.config['db_user']:
             cmd.append('--username=' + tools.config['db_user'])
         if tools.config['db_host']:
@@ -217,21 +215,14 @@ class db(netsvc.ExportService):
 
         self._set_pg_psw_env_var()
 
-        if self.db_exist(db_name):
+        if self.exp_db_exist(db_name):
             logger.notifyChannel("web-services", netsvc.LOG_WARNING,
                     'RESTORE DB: %s already exists' % (db_name,))
             raise Exception, "Database already exists"
 
-        db = sql_db.db_connect('template1')
-        cr = db.cursor()
-        cr.autocommit(True) # avoid transaction block
-        try:
-            cr.execute("""CREATE DATABASE "%s" ENCODING 'unicode' TEMPLATE "template0" """ % db_name)
-        finally:
-            cr.close()
-            del db
+        self._create_empty_database(db_name)
 
-        cmd = ['pg_restore', '--no-owner']
+        cmd = ['pg_restore', '--no-owner', '-w']
         if tools.config['db_user']:
             cmd.append('--username=' + tools.config['db_user'])
         if tools.config['db_host']:
@@ -268,6 +259,7 @@ class db(netsvc.ExportService):
 
         db = sql_db.db_connect('template1')
         cr = db.cursor()
+        cr.autocommit(True) # avoid transaction block
         try:
             try:
                 cr.execute('ALTER DATABASE "%s" RENAME TO "%s"' % (old_name, new_name))
@@ -287,19 +279,16 @@ class db(netsvc.ExportService):
         return True
 
     def exp_db_exist(self, db_name):
-        try:
-            db = sql_db.db_connect(db_name)
-            return True
-        except:
-            return False
+        ## Not True: in fact, check if connection to database is possible. The database may exists
+        return bool(sql_db.db_connect(db_name))
+
+    def exp_list(self, document=False):
+        if not tools.config['list_db'] and not document:
+            raise Exception('AccessDenied')
 
-    def exp_list(self):
         db = sql_db.db_connect('template1')
         cr = db.cursor()
         try:
-            list_db = tools.config["list_db"]
-            if list_db == 'False':
-                return []
             try:
                 db_user = tools.config["db_user"]
                 if not db_user and os.name == 'posix':
@@ -314,7 +303,7 @@ class db(netsvc.ExportService):
                 else:
                     cr.execute("select decode(datname, 'escape') from pg_database where datname not in('template0', 'template1','postgres') order by datname")
                 res = [str(name) for (name,) in cr.fetchall()]
-            except:
+            except Exception:
                 res = []
         finally:
             cr.close()
@@ -350,7 +339,7 @@ class db(netsvc.ExportService):
                 self.abortResponse(1, inst.name, 'warning', inst.value)
             except except_osv, inst:
                 self.abortResponse(1, inst.name, inst.exc_type, inst.value)
-            except Exception, e:
+            except Exception:
                 import traceback
                 tb_s = reduce(lambda x, y: x+y, traceback.format_exception( sys.exc_type, sys.exc_value, sys.exc_traceback))
                 l.notifyChannel('web-services', netsvc.LOG_ERROR, tb_s)
@@ -393,9 +382,11 @@ class common(_ObjectService):
                 auth.logout(params[1])
             logger.notifyChannel("web-service", netsvc.LOG_INFO,'Logout %s from database %s'%(login,db))
             return True
-        elif method in ['about', 'timezone_get', 'get_server_environment', 'login_message', 'get_stats' ]:
+        elif method in ['about', 'timezone_get', 'get_server_environment',
+                        'login_message','get_stats', 'check_connectivity',
+                        'list_http_services']:
             pass
-        elif method in ['get_available_updates', 'get_migration_scripts', 'set_loglevel']:
+        elif method in ['get_available_updates', 'get_migration_scripts', 'set_loglevel', 'get_os_time', 'get_sqlcount']:
             passwd = params[0]
             params = params[1:]
             security.check_super(passwd)
@@ -446,8 +437,7 @@ GNU Public Licence.
         return info
 
     def exp_timezone_get(self, db, login, password):
-        return time.tzname[0]
-
+        return tools.misc.get_server_timezone()
 
     def exp_get_available_updates(self, contract_id, contract_password):
         import tools.maintenance as tm
@@ -501,22 +491,22 @@ GNU Public Licence.
                 try:
                     try:
                         base64_decoded = base64.decodestring(zips[module])
-                    except:
+                    except Exception:
                         l.notifyChannel('migration', netsvc.LOG_ERROR, 'unable to read the module %s' % (module,))
                         raise
 
-                    zip_contents = cStringIO.StringIO(base64_decoded)
+                    zip_contents = StringIO(base64_decoded)
                     zip_contents.seek(0)
                     try:
                         try:
                             tools.extract_zip_file(zip_contents, tools.config['addons_path'] )
-                        except:
+                        except Exception:
                             l.notifyChannel('migration', netsvc.LOG_ERROR, 'unable to extract the module %s' % (module, ))
                             rmtree(module)
                             raise
                     finally:
                         zip_contents.close()
-                except:
+                except Exception:
                     l.notifyChannel('migration', netsvc.LOG_ERROR, 'restore the previous version of the module %s' % (module, ))
                     nmp = os.path.join(backup_directory, module)
                     if os.path.isdir(nmp):
@@ -557,14 +547,13 @@ GNU Public Licence.
                     %(platform.release(), platform.version(), platform.architecture()[0],
                       os_lang, platform.python_version(),release.version)
         return environment
-    
 
     def exp_login_message(self):
         return tools.config.get('login_message', False)
 
-    def exp_set_loglevel(self,loglevel):
+    def exp_set_loglevel(self, loglevel, logger=None):
         l = netsvc.Logger()
-        l.set_loglevel(int(loglevel))
+        l.set_loglevel(int(loglevel), logger)
         return True
 
     def exp_get_stats(self):
@@ -573,6 +562,22 @@ GNU Public Licence.
         res += netsvc.Server.allStats()
         return res
 
+    def exp_list_http_services(self):
+        from service import http_server
+        return http_server.list_http_services()
+
+    def exp_check_connectivity(self):
+        return bool(sql_db.db_connect('template1'))
+        
+    def exp_get_os_time(self):
+        return os.times()
+
+    def exp_get_sqlcount(self):
+        logger = logging.getLogger('db.cursor')
+        if not logger.isEnabledFor(logging.DEBUG_SQL):
+            logger.warning("Counters of SQL will not be reliable unless DEBUG_SQL is set at the server's config.")
+        return sql_db.sql_counter
+
 common()
 
 class objects_proxy(netsvc.ExportService):
@@ -583,8 +588,10 @@ class objects_proxy(netsvc.ExportService):
     def dispatch(self, method, auth, params):
         (db, uid, passwd ) = params[0:3]
         params = params[3:]
-        if method not in ['execute','exec_workflow','obj_list']:
-            raise KeyError("Method not supported %s" % method)
+        if method == 'obj_list':
+            raise NameError("obj_list has been discontinued via RPC as of 6.0, please query ir.model directly!")
+        if method not in ['execute','exec_workflow']:
+            raise NameError("Method not available %s" % method)
         security.check(db,uid,passwd)
         ls = netsvc.LocalService('object_proxy')
         fn = getattr(ls, method)
@@ -709,21 +716,28 @@ class report_spool(netsvc.ExportService):
 
         def go(id, uid, ids, datas, context):
             cr = pooler.get_db(db).cursor()
+            import traceback
+            import sys
             try:
                 obj = netsvc.LocalService('report.'+object)
                 (result, format) = obj.create(cr, uid, ids, datas, context)
+                if not result:
+                    tb = sys.exc_info()
+                    self._reports[id]['exception'] = ExceptionWithTraceback('RML is not available at specified location or not enough data to print!', tb)
                 self._reports[id]['result'] = result
                 self._reports[id]['format'] = format
                 self._reports[id]['state'] = True
             except Exception, exception:
-                import traceback
-                import sys
+                
                 tb = sys.exc_info()
                 tb_s = "".join(traceback.format_exception(*tb))
                 logger = netsvc.Logger()
                 logger.notifyChannel('web-services', netsvc.LOG_ERROR,
                         'Exception: %s\n%s' % (str(exception), tb_s))
-                self._reports[id]['exception'] = ExceptionWithTraceback(tools.exception_to_unicode(exception), tb)
+                if hasattr(exception, 'name') and hasattr(exception, 'value'):
+                    self._reports[id]['exception'] = ExceptionWithTraceback(tools.ustr(exception.name), tools.ustr(exception.value))
+                else:
+                    self._reports[id]['exception'] = ExceptionWithTraceback(tools.exception_to_unicode(exception), tb)
                 self._reports[id]['state'] = True
             cr.commit()
             cr.close()
@@ -734,8 +748,9 @@ class report_spool(netsvc.ExportService):
 
     def _check_report(self, report_id):
         result = self._reports[report_id]
-        if result['exception']:
-            raise result['exception']
+        exc = result['exception']
+        if exc:
+            self.abortResponse(exc, exc.message, 'warning', exc.traceback)
         res = {'state': result['state']}
         if res['state']:
             if tools.config['reportgz']:
@@ -755,7 +770,6 @@ class report_spool(netsvc.ExportService):
         return res
 
     def exp_report_get(self, db, uid, report_id):
-
         if report_id in self._reports:
             if self._reports[report_id]['uid'] == uid:
                 return self._check_report(report_id)