4
The server recipe actually includes a general engine to install Python
5
code that needs access to the OpenERP API and build
6
configuration-aware executables.
8
As usual, it tries and do so by bridging the standard Python packaging
9
practice (setuptools-style console scripts, as in
10
``zc.recipe.egg:scripts``) and OpenERP specificities.
12
We call such scripts *OpenERP scripts* to distinguish them among the
13
more general concept of console scripts.
15
.. warning:: OpenERP scripts are currently supported for OpenERP ≥ 6.1 only.
21
OpenERP scripts can do great in situations where an RPC script might
22
not be powerful enough or not practical. Some examples:
24
* specific batch jobs, especially for large databases (you get to
25
control the transaction).
26
* introspection tools.
27
* general-purposes test launchers that don't have any knowledge of
28
OpenERP specifics, such as ``nose``. See :ref:`command_line_options`
29
for details about that.
31
OpenERP vs RPC scripts for administrative tasks
32
-----------------------------------------------
34
There are several Python distributions that wrap the OpenERP RPC APIs
35
for easy use within Python code.
37
Using an RPC script for administrative tasks usually leads to
38
wrap it in a shell script, with the admin password in clear text.
40
In this author's experience of applicative maintainance,
41
this always turns to be an
42
easy source of breakage that may look to be trivial at first sight but
43
has actually two nasty properties : it may stay unnoticed for a while,
44
and it lies at the interface between responsibilities.
46
In case of password change, the persons who can do it
47
in the database and in the system usually differ, and may not
48
communicate on a regular basis. In enterprise hosting environments,
49
you may have to explain stuff to several project managers with
50
different responsabilities, go through crisis management meetings,
51
etc. Who wants to waste hours of their life interacting with people
52
under stress to try and persuade them that it's only a matter of
53
changing an obscure password ?
56
OpenERP scripts vs Openerp cron jobs
57
------------------------------------
59
Because they are part of addons, OpenERP cron jobs also have full
60
unrestricted access to the internal API, and obviously don't suffer
61
from the password plague.
63
Some ideas to make a choice:
65
* who should control, schedule and tune execution (a system administrator or
67
* which one the script author finds easiest to write for
68
* reuse and distribution issues : OpenERP scripts are in Python
69
distributions, cron jobs are in addons.
70
* OpenERP scripts must implement their own transaction control,
71
whereas cron jobs don't bother about it but rely on the framework's
74
Perhaps, the best is not to choose : put the bulk of the logic in some
75
technical addon, it's easy to rewrap it in an OpenERP script and as a
79
Declaring OpenERP Scripts
80
~~~~~~~~~~~~~~~~~~~~~~~~~
81
There are several cases, depending on the script authors
82
intentions. Script authors should therefore state clearly in their
83
documentation how to declare them.
85
Assume to fix ideas there is a Python distribution ``my.script``
86
that declares a console script entry point named ``my_script`` in its
92
my_script = my.script.main:run
95
The first thing to do is to require that distribution using the
102
:ref:`How that distribution can be made available to buildout
103
<making_available>` is a different question.
107
The following configuration::
111
openerp_scripts = my_script
113
Produces an executable ``bin/my_script-openerp-one``, that can import
114
OpenERP server and addons code, and in which the OpenERP configuration
115
related to the appropriate buildout part (here, ``openerp-one``) is
116
loaded in the standard ``openerp.tools.config``, for use in the
117
script. The script has to take care of all database management operations.
119
Optionally, it's possible to specify the name of the produced script::
123
openerp_scripts = my_script=wished_name
125
That would build the script as ``bin/wished_name``.
128
enough for scripts that'd take care of many bootstrapping details, but
129
there is a more integrated way that script authors should be aware of:
130
the special ``session`` argument.
132
.. _arguments_session:
134
Arguments and session
135
---------------------
136
.. note:: new in version 1.7.0
138
An ``arguments`` parameter, similar to the one of
139
``zc.recipe.egg:scripts`` can be specified::
143
openerp_scripts = my_script arguments=2,3
145
This is a raw string that will be used as the string of arguments for
146
the callable specified in the entry point, as in ``main(2,3)`` in that
149
There is a special argument: ``session``, which is an object provided
150
by the recipe to expose OpenERP API in a convenient manner for script
152
:py:class:`anybox.recipe.openerp.runtime.session.Session` to learn
153
what can be done with it.
155
Scripts written for these ``session`` objects must be declared as such::
159
openerp_scripts = my_script arguments=session
161
.. _command_line_options:
166
In some cases, it is useful to do some operations, such as preloading
167
a database, before actual running of the script. This is intended for
168
scripts which have no special knowledge of OpenERP but may in turn
169
call some code meant for OpenERP, that'd need some preparations to
170
already have been performed.
172
The main use-case is unit tests launchers.
174
For these, the ``command-line-options`` modifier tells the recipe to
175
produce an executable that will implement some additional command-line
176
options parsing and perform some actions accordingly. On the
177
command-line ``--`` is used as a separator between those additional
178
options and the regular arguments expected by the script.
184
openerp_scripts = nosetests command-line-options=-d
186
This produces a ``bin/nosetests_openerp-three``, which you can use
189
bin/nosetests_openerp-three -d mydb -- [NOSE REGULAR OPTIONS & ARGUMENTS]
191
Currently available command-line-options:
193
:-d DB_NAME: preload the specified database
195
Writing OpenERP Scripts
196
~~~~~~~~~~~~~~~~~~~~~~~
198
Script authors have to:
200
* write their script as a callable within a setuptools
201
distribution. Usually that'd be a function ``my_run`` at toplevel of
202
a ``my/script/main.py`` file
203
* declare that callable in ``setup.py`` like this::
208
my_script = my.script.main:my_run
210
* (recommended) use the
211
:py:class:`anybox.recipe.openerp.runtime.session.Session` API. For
212
that, let your callable accept a ``session`` argument, and tell
213
users to :ref:`pass it in their buildout configuration <arguments_session>`.
215
* write the actual script! Here's a silly example, that outputs the
216
total of users in the database::
218
from argparse import ArgumentsParser
221
# command-line arguments handling is up to the script
222
parser = ArgumentsParser()
223
parser.add_argument('-d', '--database',
224
help="Database to work on", required=True)
225
arguments = parser.parse_args()
228
session.open(arguments.database)
231
users = session.registry('res.users').search(
232
session.cr, session.uid, [])
234
print("There are %d users in database %r" % (
235
len(users), arguments.database))
237
# Transaction control is up to the script
238
session.rollback() # we didn't write anything, but one never knows
240
.. _making_available:
242
Making the distribution available
243
---------------------------------
245
In order to be used by the recipe, the distribution that holds the
246
script code has to be *required* with the ``eggs`` option. But how can
247
buildout retrieve it ? There's nothing specific to the OpenERP recipe
248
about that, it works in the exact same way as for the standard
249
``zc.recipe.eggs`` recipe.
251
We list here some possibilities, as a convenience for readers without
252
a more general buildout experience.
254
* provide it locally and tell buildout to "develop" it::
257
develop = my_script_distribution_path
259
paths are interpreted relative to the buildout directory, but may be
262
* put it on the `Python Package Index <https://pypi.python.org>`_
263
* put it in a private index and use the ``index`` main buildout option
264
* prebuild an egg and put it in the eggs directory (can be shared
265
between several buildouts).
266
* put a source distribution (tarball) or an egg on some HTTP server,
267
and use the ``find-links`` global buildout option.
268
* grab it and develop it from an external VCS, using the
269
`gp.vcsdevelop <https://pypi.python.org/gp.vcsdevelop>`_ buildout extension.
270
* use one of the other VCS-oriented buildout extensions (such as
271
`mr.developer <https://pypi.python.org/pypi/mr.developer/>`_
273
.. note:: the releasing features (freeze, extract) of the recipe are
274
aware of ``gp.vcsdevelop`` and will control the revision it
275
uses. There's no such support of ``mr.developer`` right now.
281
.. note:: new in version 1.8.0
283
The recipe provides a toolkit for database management, including
284
upgrade scripts generation, to fulfill two seemingly contradictory goals:
286
* **Uniformity**: all buildout-driven
287
installations have upgrade scripts with the same command-line
288
arguments, similar output, and all the costly details that matter
289
for industrialisation, or simply execution by a pure system
290
administrator, such as success log line, proper status code, already
291
taken care of. Even for one-shot delicate upgrades, repetition is
292
paramount (early detection of problems through rehearsals).
293
* **Flexibility**: "one-size-fits all" is precisely what the recipe is
294
meant to avoid. In the sensitive case of upgrades, we know that an
295
guess-based approach that would work in 90% of cases is not good enough.
297
To accomodate these two needs, the installation-dependent
298
flexibility is given back to the user (a
299
project maintainer in that case) by letting her write the actual
300
upgrade logic in the simplest way possible. The recipe rewraps it and
301
produces the actual executable, with its command-line parsing, etc.
303
Project maintainers have to produce a callable using the
304
high-level methods of
305
:py:class:`anybox.recipe.openerp.runtime.session.Session`. Here's an
308
def run_upgrade(session, logger):
309
db_version = session.db_version # this is the state after
311
if db_version < '1.0':
312
session.update_modules(['account_account'])
314
logger.warn("Not upgrading account_account, as we know it "
315
"to be currently a problem with our setup. ")
316
session.update_modules(['crm', 'sales'])
318
Such callables (source file and name) can be declared in the
319
buildout configuration with the ``upgrade_script`` option::
321
upgrade_script = my_upgrade.py run_upgrade
323
The default is ``upgrade.py run``. The path is interpreted relative to
324
the buildout directory.
326
If the specified source file is not found, the recipe will initialize it
327
with the simplest possible one : update of all modules. That is
328
expected to work 90% of the time. The package manager can then modify
329
it according to needs, and maybe track it in version control.
331
In truth, upgrade scripts are nothing but OpenERP scripts, with the
332
entry point console script being provided by the recipe itself, and
333
in turn relaying to that user-level callable.
334
See :py:mod:`anybox.recipe.openerp.runtime.upgrade` for more details
337
Usage for instance creation
338
---------------------------
339
For projects with a fixed number of modules to install at a given
340
point of code history, upgrade scripts can be used to install a
343
def upgrade(session, logger):
344
"""Create or upgrade an instance or my_project."""
345
if session.is_initialization:
346
logger.info("Installing modules on fresh database")
347
session.install_modules(['my_module'])
352
Not having a command-line argument for modules ot install in
353
the resulting script *is a strength*.
354
It means that CI robots, deployment tools
355
and the like will be able to install it with zero additional
358
The default script produced by the recipe also detects initializations
359
and logs information on how to customize::
361
2013-10-14 17:16:17,785 WARNING Usage of upgrade script for initialization detected. You should consider customizing the present upgrade script to add modules install commands. The present script is at : /home/gracinet/openerp/recipe/testing-buildouts/upgrade.py (byte-compiled form)
362
2013-10-14 17:16:17,786 INFO Initialization successful. Total time: 22 seconds.
365
.. note:: the ``is_initialization`` attribute is new in version 1.8.1
368
Options of the produced executable upgrade script
369
-------------------------------------------------
371
Command-line parsing is done with `argparse
372
<http://docs.python.org/2/library/argparse.html>`_. If you have any doubt,
373
use ``--help`` with the version you have. Here's the current state::
375
$ bin/upgrade_openerp -h
376
usage: upgrade_openerp [-h] [--log-file LOG_FILE] [--log-level LOG_LEVEL]
377
[--console-log-level CONSOLE_LOG_LEVEL] [-q]
381
-h, --help show this help message and exit
382
--log-file LOG_FILE File to log sub-operations to, relative to the current
383
working directory, supports homedir expansion ('~' on
384
POSIX systems). (default: upgrade.log)
385
--log-level LOG_LEVEL
386
Main OpenERP logging level. Does not affect the
387
logging from the main upgrade script itself. (default:
389
--console-log-level CONSOLE_LOG_LEVEL
390
Level for the upgrade process console logging. This is
391
for the main upgrade script itself meaning that
392
usually only major steps should be logged (default:
394
-q, --quiet Suppress console output from the main upgrade script
395
(lower level stages can still write) (default: False)
396
-d DB_NAME, --db-name DB_NAME
397
Database name. If ommitted, the general default values
398
from OpenERP config file or libpq will apply.
399
--init-load-demo-data
400
Demo data will be loaded with module installations if
401
and only if this modifier is specified (default:
408
Here's the output of a run of the default upgrade script::
410
$ bin/upgrade_openerp -d testrecipe
411
Starting upgrade, logging details to /home/gracinet/openerp/recipe/testing-buildouts/upgrade.log at level INFO, and major steps to console at level INFO
413
2013-09-21 18:53:23,471 WARNING Expected package version file '/home/gracinet/openerp/recipe/testing-buildouts/VERSION.txt' does not exist. version won't be set in database at the end of upgrade. Consider including such a version file in your project *before* version dependent logic is actually needed.
414
2013-09-21 18:53:23,471 INFO Database 'testrecipe' loaded. Actual upgrade begins.
415
2013-09-21 18:53:23,471 INFO Default upgrade procedure : updating all modules.
416
2013-09-21 18:53:54,029 INFO Upgrade successful. Total time: 32 seconds.
418
The same with a version file::
420
$ bin/upgrade_openerp -d testrecipe
421
Starting upgrade, logging details to /home/gracinet/openerp/recipe/testing-buildouts/upgrade.log at level INFO, and major steps to console at level INFO
423
2013-09-22 19:23:17,908 INFO Read package version: 6.6.6-final from /home/gracinet/openerp/recipe/testing-buildouts/VERSION.txt
424
2013-09-22 19:23:17,908 INFO Database 'testrecipe' loaded. Actual upgrade begins.
425
2013-09-22 19:23:17,909 INFO Default upgrade procedure : updating all modules.
426
2013-09-22 19:23:48,626 INFO setting version 6.6.6-final in database
427
2013-09-22 19:23:48,635 INFO Upgrade successful. Total time: 32 seconds.
432
The familiar ``start_openerp``, and its less pervasing siblings
433
(``gunicorn_openerp``, ``test_openerp``, …) are also special cases of
436
What is special with them amounts to the following:
438
* the entry points are declared by the recipe itself, not by a
439
third-party Python distribution.
440
* the recipe includes some initialization code in the final
441
executable, in a way that the configuration presently could not allow.
442
* often, they don't use the session objects, but rewrap instead the mainline
445
In particular, you can control the names of the startup scripts with
446
the ``openerp_scripts`` option. For instance, to
447
replace ``bin/start_openerp`` with ``bin/oerp``, just do::
451
openerp_scripts = openerp_starter=oerp
453
List of internal entry points
454
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
456
Here's the list of currently available internal entry points.
458
:openerp_starter: main OpenERP startup script (dynamically added
459
behing the scenes by the recipe)
460
:openerp_tester: uniform script to start OpenERP, launch all tests and
461
exit. This can be achieved with the main startup
462
scripts, but options differ among OpenERP versions.
463
(also dynamically added behind the scenes).
464
:openerp_upgrader: entry point for the upgrade script
465
:openerp_cron_worker: entry point for the cron worker script that gets
466
built for gunicorn setups.
467
:oe: entry point declared by ``openerp-command`` and used by the recipe.
468
:gunicorn: entry point declared by ``gunicorn`` and used by the recipe.
470
.. note:: For these entry points, the ``command-line-options`` and
471
``arguments`` modifiers have no effect.