Commit Graph
161 Commits
Author SHA1 Message Date
Xavier Morel e37109c8c3 [REM] core: support for werkzeug interactive debugger
With the special support for postmortem debugging removed, the
likelihood of needing / wanting the werkzeug remote debugger seems
even more remote (as it works in strictly less situations, only for
frontend non-json requests).

So remove that as well.

closes odoo/odoo#115176

X-original-commit: a2022783b652299155c460294c00dbced9b619ac
Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
2023-03-14 13:30:19 +01:00
Xavier Morel eb5515bee9 [ADD] core: support for binding IPv6 in worker mode
Because Werkzeug supports ipv6 natively (and has since ~2010
pallets/werkzeug@13a76262a4), running
Odoo in threaded mode allows binding to an ipv6 address.

However the prefork server is part of Odoo, and only creates
AF_INET (ipv4) sockets. Thus switching from threaded to worker mode
breaks the bind.

Add a family switch on the `http-interface`, so binding to ipv6
addresses also works in ipv6. Gevent already does that switching
internally, so the evented worker works out of the box, it was only
the prefork / workers which did not.

Looking at both the Werkzeug and Gevent implementations:

- https://github.com/pallets/werkzeug/blob/f9906fa6d83dd668f9214acef458419756bbc062/src/werkzeug/serving.py#L607-L614
- https://github.com/gevent/gevent/blob/1e412d35526183b26c1abf2eb658cbef661f5f70/src/gevent/baseserver.py#L415-L433

Both primarily check whether the host contains `:`. Werkzeug also
checks if `socket.AF_INET6` exists, however gevent doesn't bother with
that. Looking at the
source (https://github.com/python/cpython/blob/7d801f245e2021d19daff105ce722f22aa844391/Modules/socketmodule.c#L7419-L7421),
it is indeed possible for `AF_INET6` to be missing, however that
requires specifically compiling Python in an environment which doesn't
support IPv6.

Meanwhile it's also possible to disable ipv6 at runtime, in which case
I'd assume the `socket.AF_INET6` constant is present, and creating the
socket fails, which nobody guards against. Therefore just ignore the
entire thing, it doesn't seem worth the hassle (or the questioning) to
check whether the constant is present, especially since this is only a
concern when the user specifically requires an IPv6 address.

Fixes #35782

closes odoo/odoo#114666

Signed-off-by: Julien Castiaux (juc) <juc@odoo.com>
2023-03-08 13:49:05 +01:00
Xavier-Do e9b170da38 [IMP] tests: refactor unittest classes
Odoo Test environments requires to modify many parts of the unittest
TestCase, Suite and Result.

The main initial reason is to **avoid to postpone result at the end of
the test suite**, because even if it is convenient to have all errors
visible after the tests in some case, odoo logs adds information during
the execution that can be useful to debug when a test fail, to have
context for an error. (see **OdooTestResult**)

We are also fixing the stack trace comming from a unittest and since
there is no proper way to hook inside the TestPartExecutor, a dirty hack
injects anoter result on the outcome to manage the error and complete
the stack trace. This was also a way to avoid to postpone subtest logs
at the end of the test case (see _ErrorCatcher)

`_feedErrorsToResult` was used to test the test suite behavior since
there are many customization and this is quite fragile, especially if
unittest changes behavior in other python version.

**Python 3.11** introduced python/cpython#664448d8 That, in a way, goes
in the same direction of the changed introduced with _ErrorCatcher:
immediately feed errors to resut instead of postponing it. But this also
removes `_feedErrorsToResult` that was used to test this behaviors, as
well as other ones.

Since odoo should remain multi-version, this amount of changes on the
initial behavior become to complicate to keep cross-version and the
(already in our mind for a while) solution to **vendor unittest** will
help to simplify most of our test code base.

This commit modified the vendored unittest files to simplify them as
much as possible to suite our needs.

Since the runner is still the unittest one, we need to inherit from
unittest.Testcase in order to have the right type.

This also means that we still have access to all TestCase methods
without overriding them all. This is convenient for assertion methods as
an example but the initial idea is to vendor our own version of TestCase
to avoid having trouble to adapte our miscommunications to future python
versions. A trade-off must be done to chose what should remain in our
code base. The idea is to keep logic closely linked to our changes in
our code base, mainly around the run method, but also addClassCleanup
wich need to be vendored for python 3.7, but assertions methods are
independent. Any logic can be moved fom unittest to our
vendored version in the future if needed.

X-original-commit: 9a5d1ea54be49e4cc8208c33e76a6bbd2414d5d0
Part-of: odoo/odoo#113850
2023-02-28 23:49:33 +01:00
tsm-odoo 780cdf1e49 [FIX] server: avoid werkzeug >= 2.1.1 connection close
Quick and dirty fix to avoid `'connection': 'close'` header with
websocket.

Part-of: odoo/odoo#112298
2023-02-10 14:37:30 +01:00
Xavier-Do a0732197f4 [FIX] tests: don't pregenerate asset bundles for upgrade tests
Pregenerate doesn't work for rtl languages. It shouldn't be a problem
but it was discovered that pregenerate run during upgrades leading to
errors

```
Traceback (most recent call last):
  File "/home/odoo/src/odoo/16.0/odoo/service/server.py", line 1314, in preload_registries
    env['ir.qweb']._pregenerate_assets_bundles()
  File "/home/odoo/src/odoo/16.0/addons/website/models/ir_qweb.py", line 180, in _pregenerate_assets_bundles
    _, _, _, id_unique, name = bundle_url.split('/')
ValueError: too many values to unpack (expected 5)tore the set of known environments as it was at setUp
```

This is because the rtl is an extra in the url breaking the logic.

Pregenerate is mainly usefull for tests on runbot
(or running tests locally), to speedup httpcases
avoiding generation at each page load.

IntegrityCases are postinstall but don't requires generating assets, or
at least no so often meaning that we can avoid the pregeneration in this
case. It should also avoid losing some time on pregeneration since it
is not useful.

This new logic only pregenerate if we have at least one HTTPCase in the
test suite. This should also speedup local testing when starting
non http post_install tests.

closes odoo/odoo#107107

X-original-commit: 43a5c17088707eec134d54ccf9341e86859f88c7
Signed-off-by: Christophe Monniez (moc) <moc@odoo.com>
Signed-off-by: Xavier Dollé (xdo) <xdo@odoo.com>
2022-12-02 17:44:13 +01:00
Denis Vermylen 3ef1ac0278 [FIX] server: close socket when graceful shutdown
Previously, when gracefully stopping the server, incoming requests got
queued in the socket's backlog until the workers all finished their
requests, then the socket closes and backlogged requests get their
error response.

This would leave the system administrator with the awkward choice
between giving existing requests time to finish and not creating too
big a downtime for new incoming requests before forcefully shutting the
server down.

As the workers will stop accepting new connections on the graceful stop
call, we can close the socket so the binded address is freed as well.

This allows a cleaner graceful restart of the service as we can
gracefully quit the Odoo server, start a new server on the same address
and let the old service finish its requests for as long as we want.

closes odoo/odoo#104952

X-original-commit: 006a5fa1499dc44be9f2e285637dd0b6a771be16
Signed-off-by: Olivier Dony <odo@odoo.com>
2022-11-07 03:26:11 +01:00
Xavier-Do b58967d8d1 [IMP] loading: pregenerate assets bundles after install
During tests on runbot, the main reason tours are slow to start is
because the first loading of "/web" need to generate assets bundles.
Generation can take up to 5 seconds time the number of tour.

Before this, the first requests to /web takes around 7 seconds on
runbot and the next ones less than 1 second.

After this commit, the first request to /web takes around 1.5 seconds

Note that the main difficulty is to choose when to generate the assets.
(With optimisations from following commits) the generation time is
arround 30 seconds (21 css + 12 js) the first time, for all modules.
If the attachements exists the generation time is arround 3 seconds
(2.5 js + 0.5 css) mainly because of globs to find usefull files.

In practice for runbot the ideal would be to generate them at
the end of the install so that it is shared for all post_install builds.

Doint it at the end of an install is not wanted in all cases, saas
pregenerated templates and upgrade may avoid doing that.

Doing it in a special runbot step (subcommand) is not possible for niglty
execution (no split, in one go). This needs to be in the code.
It is also not practical for devs wanting to have the same behaviour on
a local machine.

A solution to make the test conditionnal at install is to base the
condion on an existing test_module. test_assetsbundle is a good
candidate here. On runbot, the at_install step (befor split) will
automatically generate assets with this solution.

Assets are also generated before post_install tests. This should be fast
if they already exists and ensure that assets are up to date after
modifying sources locally or after downloading a database on runbot.
It should be fast enough for small database and may be even faster
in the future for small diffs.

Part-of: odoo/odoo#99176
2022-09-12 13:48:59 +02:00
tsm-odoo d68a3867f3 [FIX] service: fix attribute error on request handler
In order for websocket upgrade to work with firefox, the http version
must be set to `HTTP/1.1`.

Until now, this was done in the `send_response` method. The issue is
that this method is also used when sending an error. This is problematic
because we use environ to know whether or not the version should be changed
which means any error during `BaseHTTPRequestHandler.parse_request` (such as
wrong http version) would have led to an AttributeError being raised.

In order to solve this issue, this modification is done when making environ.
Moreover, we previously used the request uri to know whether or not the version
should be changed, this was not really reliable, we now use the upgrade header
for this purpose.

closes odoo/odoo#99535

Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
2022-09-05 18:35:31 +02:00
tsm-odoo 29468b1272 [IMP] config: deprecate longpolling port in favor of gevent port
This commit is part of the websocket integration in Odoo.
The longpolling port does not make sense anymore: longpolling has been dropped.
This commit deprecate the `--longpolling-port` option and replace it by the
`--gevent-port` option.

closes odoo/odoo#75510

Related: odoo/enterprise#23184
Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
2022-08-23 17:55:11 +02:00
tsm-odoo e06bb9a42d [ADD] bus: add websocket implementation
This commit is the first commit of the websocket integration in Odoo.
It focuses on the implementation of the websocket protocol as per RFC6455.

The implementation is tested thanks to the autobahn test suite.

A config parameter is available to customize the websocket connection:
   - websocket_keep_alive_timeout (default 600): Integer specifying how
     many seconds a websocket connection should be kept alive

Part-of: odoo/odoo#75510
2022-08-23 17:55:09 +02:00
Xavier Morel ba37803064 [IMP] core: reintroduce test stats
Uses a dedicated logger (for easier filtering / silencing) for
results output, and provides rough (module-level) stats in INFO but
detailed (test-level) in DEBUG.

Also updates the global query counter (`odoo.sql_db.query_counter`) to
update after each query rather than on close: with test cursors the
actual underlying counter is only rarely flushed.

closes odoo/odoo#95420

Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
2022-07-11 09:04:51 +02:00
Pierre Paridans 21a1d13306 [FIX] odoo: rlimit cannot be set at runtime on macOS Monterey (12.0+)
When attempting to run the Odoo server on macOS Monterey (currently
12.4), it crashes with the following traceback:

```
Traceback (most recent call last):
  File "/Users/app/Code/odoo/odoo-bin", line 8, in <module>
    odoo.cli.main()
  File "/Users/app/Code/odoo/odoo/cli/command.py", line 61, in main
    o.run(args)
  File "/Users/app/Code/odoo/odoo/cli/server.py", line 179, in run
    main(args)
  File "/Users/app/Code/odoo/odoo/cli/server.py", line 173, in main
    rc = odoo.service.server.start(preload=preload, stop=stop)
  File "/Users/app/Code/odoo/odoo/service/server.py", line 1342, in start
    rc = server.run(preload, stop)
  File "/Users/app/Code/odoo/odoo/service/server.py", line 553, in run
    self.start(stop=stop)
  File "/Users/app/Code/odoo/odoo/service/server.py", line 491, in start
    set_limit_memory_hard()
  File "/Users/app/Code/odoo/odoo/service/server.py", line 83, in set_limit_memory_hard
    resource.setrlimit(rlimit, (config['limit_memory_hard'], hard))
ValueError: current limit exceeds maximum limit
```

Actually, this issue is not specific to Odoo but affects Python on macOS
as a whole (see [1] and [2]).

As our memory management is based on Linux - which is our primary
deployment target - and non-POSIX systems were already excluded, this
commit escapes the rlimit modification on non-Linux systems to prevent
this kind of issue.

References:
[1] https://bugs.python.org/issue34602
[2] https://github.com/python/cpython/pull/14546

Related issue:
https://github.com/odoo/odoo/issues/79112

closes odoo/odoo#93381

Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
2022-06-16 17:26:55 +02:00
Christophe Monniez 27a7d4822f [FIX] server: replace deprecated daemon getter/setter
Since Python 3.5 the isDaemon and setDaemon methods can be replaced by a
property. As it's now deprecated in Python 3.10, the Time has come.

[0] https://docs.python.org/3.5/library/threading.html#threading.Thread.setDaemon

Part-of: odoo/odoo#91927
2022-05-23 08:29:53 +02:00
Christophe Monniez 65c8814a2f [FIX] various: replace deprecated currentThread method
CurrentThread is now really deprecated in Python 3.10 ... Time to
change.

Part-of: odoo/odoo#91927
2022-05-23 08:29:52 +02:00
Denis Vermylen ea913dd39d [FIX] server: close psql connections on shutdown
Stopping a threaded odoo server would spam the postgresql logs with
multiple:

<...> LOG:  could not receive data from client: Connection reset by peer

Let's avoid being rude and not hang up on postgresql connections
unexpectedly.

closes odoo/odoo#89955

X-original-commit: d0da17b167edee82381b6add573742e1cf663a9c
Signed-off-by: Raphael Collet <rco@odoo.com>
2022-04-28 09:59:56 +02:00
Victor Feyens 43ddbec741 [FIX] core: Too many arguments for logging format string (E1205)
Part-of: odoo/odoo#86332
2022-04-27 07:51:22 +02:00
Nicolas Martinelli 3bdba8bc44 [FIX] server.py: cron trigger in recovery
When setting up the PG replication, the slave is considered in recovery
mode. However, `LISTEN / NOTIFY` is not supported in this mode, leading
to an endless loop of crashes.

In recovery mode, we simply deactivate the feature.

closes odoo/odoo#87619

X-original-commit: 6389a64953081130d62f2a1703a27ddfa751098b
Signed-off-by: Nicolas Martinelli (nim) <nim@odoo.com>
2022-03-31 08:45:41 +02:00
Julien Castiaux c0647b5c52 [REF] core: HTTPocalypse (14) changes all addons
This commit is the 14th commit of a comprehensive refactor of our HTTP
framework. See odoo/odoo#78857 for complete historic, discussions and
rationnals.

* `request.uid = x` => `request.update_env(user=x)`.
* `request.context = x` => `request.update_env(context=x)`.
* `request.context = dict(request.context, x=y)`
   => `request.update_context(x=y)`.
* `request.cr = None` => `request.cr.close()`.
* `http.mono_db()` => `request.db`.
* `http.dispatch_rpc()` => `service.dispatch_rpc()`.
* `@service.model.check` => `service.model.retrying()`.
* `request.endpoint`
   => `env['ir.http']._match(request.httprequest.path)[0].endpoint`.
* `request.routing_iteration `=> `removed`.
* `request.jsonrequest` => `request.dispatcher.jsonrequest`.

Note that `request.params` is now set much later in the process. If you
are in a situation where you values from the query string or the
http body you can use `request.get_http_params()`.

Note that using the new `request.future_response`, it is possible to
add headers and cookies on the response object before the response
object is initialized. Please note that headers/cookies saved on
the future response will NOT be injected in case of error.

PR: odoo#78857
Task: 2571224
2022-02-24 13:30:51 +00:00
John Wilson 63c00f700d [FIX] various: --test-file on Windows
closes odoo/odoo#75341

Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
2021-09-14 06:00:52 +00:00
Xavier Morel 759e2c5829 [FIX] core: avoid feeding client invalid XML-RPC documents
The XML-RPC interface has a compatibility shim for binaries as
historically Odoo has returned "binary" data as base64 strings. To
avoid breakages during the Python 3 transition, the shim was
introduced to decode the output binary data (under the assumption that
it'd be ASCII-compatible).

In the case where the data is *not* ascii-compatible, however, it can
generate invalid XML documents: "C0" control codes (with the exception
of tab, LF, and CR) are not valid in XML 1.0 (which XML-RPC is an
application of), however they're perfectly valid string characters and
the standard library's marshaller does not check for them, embedding
them directly in the output document and breaking the client's
decoding.

Work around the issue by replacing such binary data with an empty
string.

While at it, move the bytes shim to the customized marshaller, this
way everything's at the same place and it's not necessary to waste
time trying to understand why the marshaller is just not calling what
it's supposed to call.

Fixes #61919

closes odoo/odoo#75973

Forward-port-of: #75952
Forward-port-of: #74699
X-original-commit: 1a0b3f7
Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
2021-09-06 12:03:31 +00:00
Raphael ColletandXavier Dollé 1595c0ee27 [REF] core: replace thread-local "envs" by cursor-bound "transaction"
Refactor the Environments object into a Transaction object, which is
bound to one cursor, and is no longer shared among several cursors.

The following methods/properties have been changed:
 - Environment.envs no longer works (because of the design change);
 - Environment.manage() is deprecated (no longer useful);
 - Environment.reset() is now an instance method;
 - env.clear_upon_failure() is deprecated in favor of cr.savepoint().

closes odoo/odoo#75598

Related: odoo/enterprise#20451
Signed-off-by: Raphael Collet (rco) <rco@openerp.com>
Co-authored-by: Xavier Dollé <xdo@odoo.com>
2021-09-03 15:45:46 +00:00
Fabien Meghazi 24b8a6178d [FIX] server: prevent inotify watches leak
Before this commit the PyInotify filesystem watcher used by the code
autoreload feature (`--dev=reload`) would not get a chance to free
it's inotify watches before the reexec, hence at each reexec triggered
by a code reload the inotify watches where accumulated until potentially
reaching the kernel limit `fs.inotify.max_user_watches`.

This patch ensures that inotify properly closes it's file descriptor
before we reexec:
https://github.com/dsoprea/PyInotify/blob/f77596a/inotify/adapters.py#L79

closes odoo/odoo#71302

X-original-commit: 8703ff1e3d9be6f2f5fce2e8c4e62589b05133fb
Signed-off-by: Raphael Collet (rco) <rco@openerp.com>
2021-05-26 18:30:16 +00:00
Alvaro Fuentes 8aa4f42442 [IMP] tests: add test_sequence order for tests
We have some tests in odoo/upgrade that are sensitive to the order on
which they are executed. Specifically: IntegrityCase tests need to be
run after all UpgradeCase tests across all Odoo modules.

To support this we implemented a sorting mechanism for tests based on
the test_sequence class attribute. This is intended to be used by meta
cases, not by individual tests.

closes odoo/odoo#66521

Related: odoo/upgrade#2184
Signed-off-by: Denis Ledoux (dle) <dle@odoo.com>
2021-02-19 12:51:00 +00:00
luz paz c9e29e5917 [FIX] *: correct typos
Various user facing an non-user-facing typos
Found via `codespell`

Closes odoo/odoo#65648

Signed-off-by: Martin Trigaux (mat) <mat@odoo.com>
2021-02-19 13:20:48 +00:00
Julien Castiaux 2d042dd2bd [FIX] bus: Resume all /longpolling/poll threads on ctrl-c
Start odoo in threading mode with bus installed. Login in the browser
using any internal user. Make sure the browser call the
/longpolling/poll uri. While the browser is waiting for a response, stop
the server. The server takes up to 50 seconds to stop.

When started in threading mode, a request to /longpolling/poll is served
by a casual http thread. It searches for messages enqueued in the bus
and returns them. If there are no message for the user in the queue yet,
it creates a `threading.Event`, attach it to the user in a shared
dictionnary and `wait()` on it with a timeout of 50 seconds (hardcoded
value). When the bus thread (the one responsible to listen on the
database) receives new messages, it `set()` the events which resume any
http thread that was waiting.

Because when we stop the server, there is no way to server new requests,
there are no way new messages arrive in the bus. All the threads that
were waiting for a new message will just wait until the event timeouts
which slow down the shutdown of the server.

Now we actively `set()` all events in order to resume all those workers
when we stop the server.

The `ImDispatch.poll` signature has been changed too so it is possible
to change (via code) the hardcoded default. The function was using the
object referenced by `TIMEOUT` at the time the function was defined,
using `timeout None` then `if None: timeout=TIMEOUT` ensures we lookup
the variable.

closes odoo/odoo#64530

Signed-off-by: Julien Castiaux <Julien00859@users.noreply.github.com>
2021-01-26 16:35:01 +00:00
Julien Castiaux 4b28f1162a [ADD] ir.cron.trigger: Manually schedule cron jobs
Introduce a way to schedule the execution of cron jobs *soon*. Triggered
jobs are included in the next execution batch.

Heavy refactor of the `ir.cron` model so the various parallel queries
use the (not so new) `SKIP LOCKED` postgresql select option which skip
rows that are locked instead of throwing an exception like `NOWAIT`
would do. Various methods has been renamed and the overall selection,
execution and update of job records have been re-architectured.

The cron workers can now to wake up early via a notification on the
`cron_trigger` channel of the meta `postgres` database.

closes odoo/odoo#62124

Task: 2368911
Signed-off-by: Raphael Collet (rco) <rco@openerp.com>
2020-12-09 14:36:28 +00:00
Xavier Morel 8cdd363238 [FIX] core: Thread.isAlive -> Thread.is_alive
Old naming has been deprecated for a long time, it's been removed
entirely in 3.9 (bpo-37804).

X-original-commit: c4a94bc99285aba085badeb1a41e8fe04562ef50
2020-12-08 08:26:37 +00:00
Xavier Morel 34575ceaa8 [IMP] core: make post-install tests deterministic
`registry._init_modules` is a set so its iteration order is
non-deterministic (it's randomised on interpreter initialisation
unless PYTHONHASHSEED is provide through the environment). This can
lead to annoying non-deterministic behavior: while the non-determinism
is only at the module level, it's easy enough for modules to have
python-level side-effects (e.g. patch methods, update globals, ...),
which may only be surfaced by an other module executing after them,
but not if said module executes before.

By sorting the modules we should make this much more reliable one way
or another.

closes odoo/odoo#60028

X-original-commit: f9169a468a2328a691ec4f32233ba3bad3622282
Signed-off-by: Xavier Dollé (xdo) <xdo@odoo.com>
2020-10-14 16:10:58 +00:00
Xavier Morel a183f2102b [FIX] core: make --test-file work with symlinked modules
While individual addons paths are normalised, module and resource
paths are not.

As a result, when symlinking modules into directories on the
addons path, the path of test modules is only half-normalized: it's
normalised up to the addon path (which likely did not need it in that
setup) but not above that.

This is an issue when using `--test-file`, because that path is fully
normalised, and so the path of the provided test file and that of the
corresponding test module will not match, leading to the tests
unexpectedly not getting run.

Normalize the test module's path before the comparison, using the same
routing used for --test-file.

closes odoo/odoo#59822

X-original-commit: 536809662e542994451be793cd09e87adbf31776
Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
2020-10-13 07:25:13 +00:00
Xavier Morel eb918180ec [FIX] core: remove duplicate test reporting
Leftover from testing a post-load report of all test failures.

Intent was to provide test failure details either during or after
loading, but feature was not actually developed and I forgot to remove
this bit before merging.

closes odoo/odoo#56186

Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
2020-08-20 08:32:21 +00:00
Xavier Morel a3ec322993 [IMP] core: tests reporting
* remove useless OdooTestRunner
* don't log results & time per-file, log a module-level tally instead
* add number of tests to post-test results
* generate a single test suite per module (see note)
* use the previous item to split out the at_install test-running in
  two steps: generating the suite for the module then running that
  suite, this way for modules which have no test, or for
  which all tests have been deselected by test tags, we can avoid some
  of the setup necessary to prepare for running tests but possibly
  quite expensive (e.g. `setup_models`)

Note: single test suite per module

I wanted to stop creating a test result for (essentially) every file
in the module, however because of the class-level ``addCleanup``, a
TestResult can't be reused by independent suites:

In order to run class-level cleanup, the test suite checks between
tests if the test it's *preparing* to run is in the same class as the
last test it ran, and if not applies the class-level cleanup.

The problem is that the "previous test class" is stored on the result
object, which is never cleaned up, and the "between tests" check is
really performed *before each test*.

This means when reusing results across suites it will run the
class-level cleanup at the end of one suite and immediately at the
start of the next, which will cause issues if class-level cleanups are
not idempotent (thankfully ``TestTestCursor`` has a non-idempotent
``tearDownClass` which let me discover the error).

Possible fixes are:

* don't reuse results
* clear the relevant states / attributes between suites
* put individual suites in a Big Suite for running

The latter seems simpler: just create a single suite for the entire
odoo-level module instead of creating one suite per test module.

Note to the note: the case of nested suite is taken in account, the
"end of suite" cleanup only runs at the end of the top-level suite, so
technically we don't have to unwrap suites for *that* purpose, we're
doing so in order to filter the test cases inside the suites. But
maybe we could integrate this feature to the suites themselves...

closes odoo/odoo#55185

Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
2020-08-19 14:08:21 +00:00
Xavier Morel c1c43bbe38 [REM] core: assertion reports
That's a not-very-useful subset of OdooTestResult, so:

* make results merge-able (aka add ability to update a result with the
  contents of another)
* remove support for test data files, and transmission of the
  assertion report thing through the data-files loading
* replace "legitimate" uses of assertion report by test result
* have run_unit_tests manipulate and return a result instead of weird
  flags & ternaries
2020-08-19 14:08:12 +00:00
Xavier Morel ec8a64a85c [REF] core: move testing-related functions to odoo/tests submodules
Attempts to clean up odoo/module and odoo/service a tad, they still
invoke testing-related utilities but are more logical in what
they *contain*.
2020-08-19 07:28:44 +00:00
Damien Bouvy fbf2299418 [IMP] odoo: avoid warning regarding resource arg format
`struct_rusage.ru_utime` and `struct_rusage.ru_stime` are float
seconds.

`setrlimit()` takes a tuple of *integers*, and recent versions of
Python have started triggering warnings:

   DeprecationWarning: an integer is required (got type
   float). Implicit conversion to integers using __int__ is
   deprecated, and may be removed in a future version of Python.

Convert the soft cpu time limit to an integer explicitly to suppress
the warning.

closes odoo/odoo#49710

Signed-off-by: Damien Bouvy (dbo) <dbo@odoo.com>
2020-08-05 14:09:28 +00:00
Xavier Morel 92903e22f8 [IMP] core: reporting after tests
Currently there are a few issues with testing reporting (at the
command-line):

1. there is a global report after at_install tests, but it gets
   "scrolled off" by long post_install tests, and is thus easy to
   miss
2. with test tags, it's easy to fat finger a typo and run 0 tests,
   which look like everything's running fine (no failure)

To improve this, print a global report at shutdown (in
`--stop-after-init` mode if tests are enabled) which recapitulates the
test results *and prints a warning if no tests were run at all*.

Also update the reporting collection to make this more reliable:

* have `run_unit_tests` return `None` if it has run no tests, the
  assertion reporting machinery counts this as neither success nor
  failure which is exactly what we want
* have load_test only report a success *if files were actually
  loaded* (by having `load_data` return that information)

Task 2301268

closes odoo/odoo#54812

Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
2020-07-27 09:30:51 +00:00
Olivier Dony 9a0d951ccc [ADD] server: allow env variable to control HTTP socket timeout
As indicated in the comment, it's much preferred to perform response
buffering at the reverse proxy level than to increase the socket
timeout. It will free up HTTP workers for other requests faster, while
the proxy does the work of buffering the stream on disk as needed.

/!\ The timeout is also used to protect from accidental DoS effects
in situations of low worker availability, due to idle connections
caused e.g. by wkhtmltopdf's connection pooling.
Setting a high timeout will make the protection less effective, so
ensuring you have enough free HTTP workers at all times becomes critical.

In our tests with nginx's defaut buffering on a typical hardware with
SSD storage, buffering up to 1GB responses did not require any change
of the socket timeout on the Odoo side, though your mileage may vary.
See also nginx's `proxy_buffering` and `proxy_max_temp_file_size` config
directives.

OPW-2247730
See also: #20158

closes odoo/odoo#51982

X-original-commit: d78ea126b8d2a72ae626880b0ec64bb7a39a07ac
Signed-off-by: Olivier Dony (odo) <odo@openerp.com>
2020-05-27 12:38:43 +00:00
Xavier Morel 9d37cecfca [FIX] core: iterations on LRU
Iteration methods on LRU were removed because they were not
thread-safe and it's not clear that making them thread-safe is the
correct thing to do, so not providing them seems saner.

I thought I'd looked for usages of the LRU but apparently didn't look
hard enough as I missed that it's used by the cron workers (apparently
using the threaded server we only run crons for dbs currently living
in the registry cache, the more you know).

Convert these to iterating on the LRU's internal mapping, and also
don't iterate on the LRU to clear its entries one by one when we can
just clear the entire thing safely, although Registry.delete_all
really seems completely unused.

closes odoo/odoo#49023

Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
2020-04-06 09:45:29 +00:00
Adrian Torres ee29cb9147 [FIX] tests: make --test-file great again
Sort of but not really, this commit fixes a special case in which
launching a --test-file of a file with at least two SavepointCases would
create a postgresql deadlock and it would be impossible to terminate the
Odoo process without sending a SIGKILL or waiting for the lock to
timeout.

This was introduced at #39368 and happens because of the way that
unittests unwraps suites, to keep it short, when it unwraps the custom
OdooSuite class internally, it ends up with a vanilla TestSuite with
which to run the different test cases, and since #39368 depends on the
overrides added to OdooSuite to function, the class cleanups are not
triggered at the end of a test class (rollback, cache cleanups, env
reset, registry reset, etc.).

The fix is to manually unwrap the suite of tests to keep OdooSuite as
the suite with which to call the tests, which was already done for
--test-enable (although for different reasons, --test-tags?) which is
why --test-enable didn't have any problems.

This commit also fixes a typo I found on the backport, which meant
classCleanups were not being executed if the setUpClass failed, but it
had no effect on classCleanups during tearDownClass.

Task-ID 2160398
Depends on #43135

closes odoo/odoo#43296

X-original-commit: 7a5ded7d40afc29043d356b5dece0dbe1fbd5ab3
Signed-off-by: Adrian Torres (adt) <adt@odoo.com>
2020-01-14 16:26:50 +00:00
Xavier Morel 80896a0bf3 [FIX] core: chrome doesn't abide by --http-port anymore
An http-port provided on the command line (may also have been an issue
for config files, didn't check) would not be taken in account anymore,
because `odoo.tests.common` would be imported during the import of
`odoo` itself (when loading odoo.service.server), itself importing
`odoo.tools.config` leading to a default configuration being set up.

* remove `odoo.tests.common.PORT`, `config['http_port']` should be
  used always
* defer the import of odoo.tests.common by moving it inside
  load_test_file
* stop generating default configs

closes odoo/odoo#43283

X-original-commit: 45871f498ea4cf3ada719692e69cd413883ab442
Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
2020-01-14 13:15:04 +00:00
Fabien Meghazi 543a3ad204 [FIX] server: limit concurrent http threads
Before this commit nothing prevented high concurrency on a threaded http
server to consume too much resources, ending up failing requests either
because the OS is unable to spawn that many threads
(`RuntimeError: can't start new thread`), either because the Odoo db
connection pool is full (`PoolError: The Connection Pool Is Full`).

This commit adds the ODOO_MAX_HTTP_THREADS environment variable which
allows to limit the amount of concurrent socket connections accepted by
a threaded server, implicitly limiting the amount of concurrent threads
running for http requests handling.

Note that if a value has been provided to ODOO_MAX_HTTP_THREADS that cannot
be parsed as an integer, a value will be automatically set to half the
db connection pool size (which defaults to 64). This dynamic value is
chosen because while most requests will borrow only one cursor
concurrently, there are some exceptions where some controllers might
allocate two or more cursors.

closes odoo/odoo#42327

X-original-commit: d42a951369ee87b50e834f73cd2af5cf031d43f7
Signed-off-by: Christophe Simonis <chs@odoo.com>
2019-12-23 18:56:09 +00:00
Fabien Meghazi 6ddf7a50b5 [IMP] server: prevent threaded server to exceed memory limit due to malloc's arenas
glibc's malloc() uses arenas [1] in order to efficiently handle memory
allocation of multi-threaded applications. This allows better memory
allocation handling in case of multiple threads that would be using
malloc() concurrently [2].

Due to the python's GIL, this optimization have no effect on
multithreaded python programs. Unfortunately, a downside of creating one
arena per cpu core is the increase of virtual memory which Odoo is based
upon in order to limit the memory usage for threaded workers.

On 32bit systems the default size of an arena is 512K while on 64bit
systems it's 64M [3], hence a threaded worker will quickly reach it's
default memory soft limit upon concurrent requests. We therefore set the
maximum arenas allowed to 2 unless the MALLOC_ARENA_MAX env variable is
set.

This commit also brings the following changes:
- allow to disable the memory hard limit for all servers if the provided
  value is 0 (instead of crashing)
- increase the log level for threaded server in case of limits reached

Note: Setting MALLOC_ARENA_MAX=0 allow to explicitely set the default
      glibs's malloc() behaviour.

[1] https://sourceware.org/glibc/wiki/MallocInternals#Arenas_and_Heaps
[2] https://www.gnu.org/software/libc/manual/html_node/The-GNU-Allocator.html
[3] https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=00ce48c;hb=0a8262a#l862

closes odoo/odoo#42323

X-original-commit: 85fe2c6e60f7f1f6ea72cb55e85f85d420ce6616
Signed-off-by: Christophe Simonis <chs@odoo.com>
2019-12-23 18:03:35 +00:00
Denis Vermylen 4b1668abc3 [FIX] server.py: <threaded> avoid registry lock upon shutdown
A deadlock can occur between threads when concurrent requests
acquire the registry lock and conflicting database-level locks
in different orders. The database won't be able to detect and
break the deadlock because it involves an external, Python-level
lock. This situation is more likely to occur during module
installations [1].

If the server is started with the `limit_time_real` option,
it should be able to abort the deadlocked requests after the
timeout, and restart. However that could not work because
the recovery initiated by `reload()` is blocked at the end
of the `stop()` method, as it cannot acquire the registry
lock either, necessary for `Registry.delete_all()`.

Since that deletion step is in fact not necessary, it can
be skipped, avoiding the deadlock entirely.

Indeed there's no real reason anymore to delete the DB's
registry upon shutdown. This was introduced for 7.0 by
b5daffc115, in order to perform
other cleanups (including cron agent threads). These other
cleanups are not necessary anymore, and when the stop()
method of the ThreadedServer completes, the next step is
either a restart of the whole process (via execve() through
_reexec()), or a full process exit. Keeping the registry in
memory for a few cycles until this happens makes no difference.

When such a deadlock occurs, it's always possible to manually
kill the server with 2 `kill` commands, or 1 `kill -9`.

~~~~~~~~~~~~~~~~~~~~~~
[1] Reproduction info:

The following deadlock was observed in Odoo threaded server mode:

1. incoming request spawns a new thread A
   A starts a transaction and does a "SELECT ... FROM res_users ..."
   getting an ACCESS SHARE lock on the table
2. incoming request spawns a new thread B
   B is a request that calls `button_immediate_install`, that will
   install new modules and alter the res_users table.
3. B takes and holds the registry lock and executes "ALTER TABLE
   res_users ...", that waits to get the ACCESS EXCLUSIVE lock on the
   table until A's transaction releases the ACCESS SHARE lock.
4. A continues code execution and reaches a .sudo() call, it tries to
   create a new environment. The creation of the new environment
   requires to wait for the registry's lock to be release but it's held
   by B.

-> A waits for B's registry lock to be released
-> B waits for A's ACCESS SHARE lock to be released
-> Deadlock that can't be broken except by force-killing the server

closes odoo/odoo#40664

X-original-commit: 9e67525418b3b0a48a796044ef24b227946ceb8f
Signed-off-by: Olivier Dony (odo) <odo@openerp.com>
2019-11-21 20:38:00 +00:00
Adrian Torres ec587297eb [IMP] tests: partially backport classCleanups from CPython 3.8
This commit partially backports bpo-24412, which allows the definition of
class cleanups (addClassCleanup) and module cleanups (omitted),
similar to instance cleanups (addCleanup).

This is useful for tests that override unittest's setUpClass and
could crash during its execution: If this happens, it is possible that a
bunch of crap is left in the database or even worse, the cursor becomes
completely fucked; Thanks to the addClassCleanup, we can undo the damage
done by the setUpClass.

Another benefit is that it is called unconditionally after tearDownClass
is called, so it can also be called as a replacement and/or safer
tearDownClass.
2019-11-06 14:07:04 +00:00
Xavier Morel 79313d8817 [FIX] core: SIGXCPU in worker processes
odoo/odoo#30688 (6ce2d6efb5) added an
indirection in prefork workers: Python-level signal handlers are
delayed until native calls have ended (e.g. accept() or
execute()). Running the actual work in a sub-thread allowed the main
thread to handle signals in all cases.

However there is apparently an issue with SIGXCPU on linux (possibly
other cases as well): SIGXCPU is delivered to the child thread (if
possible?) and Thread.join apparently stops it from redelivered to
the main thread (Thread.join is signal-interruptible since 3.2 but
possibly not Python-interruptible).

Blocking SIGXCPU on the child thread causes the OS to deliver on the
main thread and fixes the issue.

Also split set_limits so it sets the signal handler in the parent
thread but properly updates the soft limit in the child after each
request, as the goal is to put a hard limit on the CPU time per
request, not on the worker. 6ce2d6ef would set the limit once then
never update it, likely cycling workers more than desired.

While at it:

* block other signals with a handler set, they seem to work
  regardless on linux but other OS may have a different way of
  dispatching process-directed signals
* unset signals which are set by the prefork server but whose
  set behavior makes no sense in workers:

  - TERM and CHLD were already unset
  - HUP is used to restart the server, workers can just be killed
  - TTIN and TTOU configure the number of workers

closes odoo/odoo#39731

X-original-commit: 549bd199bad269e4e28efac933efac3f41495877
Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
2019-11-04 11:35:59 +00:00
Christophe Simonis d74b451805 [MERGE] forward port branch 13.0 up to f4105eb9c7 2019-10-09 02:08:17 +02:00
Christophe Simonis d67b2483e5 [MERGE] forward port branch saas-12.4 up to 9ed4872ea0
closes odoo/odoo#37372

Signed-off-by: Christophe Simonis <chs@odoo.com>
2019-09-24 17:45:37 +00:00
Denis Vermylen bd3e510edb [IMP] odoo: dump stacktrace of timed out threaded workers
So the logs contain some indication as to what was exceeding the limits.

closes odoo/odoo#37303

X-original-commit: 0a266f444a0abda026d09ad88024dca1c50c92b9
Signed-off-by: Denis Vermylen <Icallhimtest@users.noreply.github.com>
2019-09-23 14:16:04 +00:00
Denis Vermylen 4fb712f729 [FIX] service: do not call nonexistent function
omission in d26e253edd

kill -3 (SIGQUIT) is not processed like the other signals so it doesn't
go through this bit of code, but leaving the AttributeError lurking
there is not a good idea.

closes odoo/odoo#37221

X-original-commit: ac65ef0208a5ec814d263aeb44a98b47a5ee3947
Signed-off-by: Denis Vermylen <Icallhimtest@users.noreply.github.com>
2019-09-21 13:59:06 +00:00
Julien Castiaux 7c47eb1854 [IMP] module.py: deprecate openerp
[PEP-594] is deprecating the `imp` module, that module is used in
`module.py` in order to dynamically import addons using any of the
`odoo.addons` or `openerp.addons` import anchor.

We are deprecating `openerp` module/addons imports in v13 in order to
remove the support in v14 and greatly simplify how modules/addons are
loaded. If you are still using the old `import openerp` or `import
openerp.addons`, `import odoo` and `import odoo.addons` are drop-in
replacements.

The `odoo.modules.module.ad_paths` addon paths list has been deprecated
too. The list is now accessible on `odoo.addons.__path__` where they
are now directly loaded [2].

See also:

[PEP-594]: https://python.org/dev/peps/pep-0594/
[2]: https://packaging.python.org/guides/packaging-namespace-packages/

closes odoo/odoo#36597

Task: 2003936
Signed-off-by: Raphael Collet (rco) <rco@openerp.com>
2019-09-16 09:31:38 +00:00
Julien Castiaux d47083e6d2 [IMP] module.py: deprecate openerp
[PEP-594] is deprecating the `imp` module, that module is used in
`module.py` in order to dynamically import addons using any of the
`odoo.addons` or `openerp.addons` import anchor.

We are deprecating `openerp` module/addons imports in v13 in order to
remove the support in v14 and greatly simplify how modules/addons are
loaded. If you are still using the old `import openerp` or `import
openerp.addons`, `import odoo` and `import odoo.addons` are drop-in
replacements.

The `odoo.modules.module.ad_paths` addon paths list has been deprecated
too. The list is now accessible on `odoo.addons.__path__` where they
are now directly loaded [2].

See also:

[PEP-594]: https://python.org/dev/peps/pep-0594/
[2]: https://packaging.python.org/guides/packaging-namespace-packages/

closes odoo/odoo#36597

Task: 2003936
Signed-off-by: Raphael Collet (rco) <rco@openerp.com>
2019-09-20 05:58:16 +00:00