summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2019-03-22 16:26:33 +0100
committerChristophe Simonis <chs@odoo.com>2019-03-22 16:26:33 +0100
commit3a0bedd655f92ca70c1bf0e69646d3e71fc554eb (patch)
treebadd8f2948452f3de8fea4f8f28b6f4580e7fdd3
parenta11147a091e616beddd01a0f801241334c63dfc2 (diff)
parent48e4c18fc411df7db5ae703d767324016ac21e88 (diff)
[MERGE] forward port branch saas-15 up to 48e4c18fc41
-rw-r--r--odoo/addons/base/ir/ir_logging.py25
-rw-r--r--odoo/service/server.py120
2 files changed, 115 insertions, 30 deletions
diff --git a/odoo/addons/base/ir/ir_logging.py b/odoo/addons/base/ir/ir_logging.py
index 0397bff7425..4e3caef0794 100644
--- a/odoo/addons/base/ir/ir_logging.py
+++ b/odoo/addons/base/ir/ir_logging.py
@@ -1,13 +1,27 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from odoo import fields, models
+from odoo import api, fields, models
class IrLogging(models.Model):
_name = 'ir.logging'
_order = 'id DESC'
- create_date = fields.Datetime(readonly=True)
- create_uid = fields.Integer(string='Uid', readonly=True) # Integer not m2o is intentionnal
+ # The _log_access fields are defined manually for the following reasons:
+ #
+ # - The entries in ir_logging are filled in with sql queries bypassing the orm. As the --log-db
+ # cli option allows to insert ir_logging entries into a remote database, the one2many *_uid
+ # fields make no sense in the first place but we will keep it for backward compatibility.
+ #
+ # - Also, when an ir_logging entry is triggered by the orm (when using --log-db) at the moment
+ # it is making changes to the res.users model, the ALTER TABLE will aquire an exclusive lock
+ # on res_users, preventing the ir_logging INSERT to be processed, hence the ongoing module
+ # install/update will hang forever as the orm is blocked by the ir_logging query that will
+ # never occur.
+ create_uid = fields.Integer(string='Created by', readonly=True)
+ create_date = fields.Datetime(string='Created on', readonly=True)
+ write_uid = fields.Integer(string='Last Updated by', readonly=True)
+ write_date = fields.Datetime(string='Last Updated on', readonly=True)
+
name = fields.Char(required=True)
type = fields.Selection([('client', 'Client'), ('server', 'Server')], required=True, index=True)
dbname = fields.Char(string='Database Name', index=True)
@@ -16,3 +30,8 @@ class IrLogging(models.Model):
path = fields.Char(required=True)
func = fields.Char(string='Function', required=True)
line = fields.Char(required=True)
+
+ @api.model_cr
+ def init(self):
+ super(IrLogging, self).init()
+ self._cr.execute("ALTER TABLE ir_logging DROP CONSTRAINT IF EXISTS ir_logging_write_uid_fkey")
diff --git a/odoo/service/server.py b/odoo/service/server.py
index aca5fa60e3a..54a6f0cbbf6 100644
--- a/odoo/service/server.py
+++ b/odoo/service/server.py
@@ -25,9 +25,25 @@ if os.name == 'posix':
import fcntl
import resource
import psutil
+ try:
+ import inotify
+ from inotify.adapters import InotifyTrees
+ from inotify.constants import IN_MODIFY, IN_CREATE, IN_MOVED_TO
+ INOTIFY_LISTEN_EVENTS = IN_MODIFY | IN_CREATE | IN_MOVED_TO
+ except ImportError:
+ inotify = None
else:
# Windows shim
signal.SIGHUP = -1
+ inotify = None
+
+if not inotify:
+ try:
+ import watchdog
+ from watchdog.observers import Observer
+ from watchdog.events import FileCreatedEvent, FileModifiedEvent, FileMovedEvent
+ except ImportError:
+ watchdog = None
# Optional process names for workers
try:
@@ -44,13 +60,6 @@ from odoo.tools import stripped_sys_argv, dumpstacks, log_ormcache_stats
_logger = logging.getLogger(__name__)
-try:
- import watchdog
- from watchdog.observers import Observer
- from watchdog.events import FileCreatedEvent, FileModifiedEvent, FileMovedEvent
-except ImportError:
- watchdog = None
-
SLEEP_INTERVAL = 60 # 1 min
def memory_info(process):
@@ -128,7 +137,24 @@ class ThreadedWSGIServerReloadable(LoggingBaseWSGIServerMixIn, werkzeug.serving.
#----------------------------------------------------------
# FileSystem Watcher for autoreload and cache invalidation
#----------------------------------------------------------
-class FSWatcher(object):
+class FSWatcherBase(object):
+ def handle_file(self, path):
+ if path.endswith('.py') and not os.path.basename(path).startswith('.~'):
+ try:
+ source = open(path, 'rb').read() + b'\n'
+ compile(source, path, 'exec')
+ except IOError:
+ _logger.error('autoreload: python code change detected, IOError for %s', path)
+ except SyntaxError:
+ _logger.error('autoreload: python code change detected, SyntaxError in %s', path)
+ else:
+ if not getattr(odoo, 'phoenix', False):
+ _logger.info('autoreload: python code updated, autoreload activated')
+ restart()
+ return True
+
+
+class FSWatcherWatchdog(FSWatcherBase):
def __init__(self):
self.observer = Observer()
for path in odoo.modules.module.ad_paths:
@@ -139,27 +165,60 @@ class FSWatcher(object):
if isinstance(event, (FileCreatedEvent, FileModifiedEvent, FileMovedEvent)):
if not event.is_directory:
path = getattr(event, 'dest_path', event.src_path)
- if path.endswith('.py') and not os.path.basename(path).startswith('.~'):
- try:
- source = open(path, 'rb').read() + b'\n'
- compile(source, path, 'exec')
- except FileNotFoundError:
- _logger.error('autoreload: python code change detected, FileNotFound for %s', path)
- except SyntaxError:
- _logger.error('autoreload: python code change detected, SyntaxError in %s', path)
- else:
- if not getattr(odoo, 'phoenix', False):
- _logger.info('autoreload: python code updated, autoreload activated')
- restart()
+ self.handle_file(path)
def start(self):
self.observer.start()
- _logger.info('AutoReload watcher running')
+ _logger.info('AutoReload watcher running with watchdog')
def stop(self):
self.observer.stop()
self.observer.join()
+
+class FSWatcherInotify(FSWatcherBase):
+ def __init__(self):
+ self.started = False
+ # ignore warnings from inotify in case we have duplicate addons paths.
+ inotify.adapters._LOGGER.setLevel(logging.ERROR)
+ # recreate a list as InotifyTrees' __init__ deletes the list's items
+ paths_to_watch = []
+ for path in odoo.modules.module.ad_paths:
+ paths_to_watch.append(path)
+ _logger.info('Watching addons folder %s', path)
+ self.watcher = InotifyTrees(paths_to_watch, mask=INOTIFY_LISTEN_EVENTS, block_duration_s=.5)
+
+ def run(self):
+ _logger.info('AutoReload watcher running with inotify')
+ dir_creation_events = set(('IN_MOVED_TO', 'IN_CREATE'))
+ while self.started:
+ for event in self.watcher.event_gen(timeout_s=0, yield_nones=False):
+ (_, type_names, path, filename) = event
+ if 'IN_ISDIR' not in type_names:
+ # despite not having IN_DELETE in the watcher's mask, the
+ # watcher sends these events when a directory is deleted.
+ if 'IN_DELETE' not in type_names:
+ full_path = os.path.join(path, filename)
+ if self.handle_file(full_path):
+ return
+ elif dir_creation_events.intersection(type_names):
+ full_path = os.path.join(path, filename)
+ for root, _, files in os.walk(full_path):
+ for file in files:
+ if self.handle_file(os.path.join(root, file)):
+ return
+
+ def start(self):
+ self.started = True
+ self.thread = threading.Thread(target=self.run, name="odoo.service.autoreload.watcher")
+ self.thread.setDaemon(True)
+ self.thread.start()
+
+ def stop(self):
+ self.started = False
+ self.thread.join()
+
+
#----------------------------------------------------------
# Servers: Threaded, Gevented and Prefork
#----------------------------------------------------------
@@ -1001,21 +1060,28 @@ def start(preload=None, stop=False):
server = ThreadedServer(odoo.service.wsgi_server.application)
watcher = None
- if 'reload' in config['dev_mode']:
- if watchdog:
- watcher = FSWatcher()
+ if 'reload' in config['dev_mode'] and not odoo.evented:
+ if inotify:
+ watcher = FSWatcherInotify()
+ watcher.start()
+ elif watchdog:
+ watcher = FSWatcherWatchdog()
watcher.start()
else:
- _logger.warning("'watchdog' module not installed. Code autoreload feature is disabled")
+ if os.name == 'posix' and platform.system() != 'Darwin':
+ module = 'inotify'
+ else:
+ module = 'watchdog'
+ _logger.warning("'%s' module not installed. Code autoreload feature is disabled", module)
if 'werkzeug' in config['dev_mode']:
server.app = DebuggedApplication(server.app, evalex=True)
rc = server.run(preload, stop)
+ if watcher:
+ watcher.stop()
# like the legend of the phoenix, all ends with beginnings
if getattr(odoo, 'phoenix', False):
- if watcher:
- watcher.stop()
_reexec()
return rc if rc else 0