FastAPI service exposing Odoo instance status and module/package diagnostics
Project description
Odoo Instance API
A small FastAPI service that exposes a single endpoint (/status) for retrieving diagnostic information about a local Odoo installation in a structured JSON format.
It is designed to run on the same host as Odoo, read the Odoo configuration (/etc/odoo/odoo.conf by default), and query Postgres for installed databases and non-core modules. It also checks the Odoo pyenv environment for installed packages and queries PyPI for available updates (including pre-releases).
Finally, the app maps installed Odoo packages to database modules using the manifest path heuristic and reports version drifts across DB module version, installed package version, and PyPI latest version.
The intended use case is machine consumption, but the endpoint also supports HTML rendering for easy human inspection when needed
Rationale
Despite we can list modules with a Prometheus exporter, this is an antipattern for several reasons:
- Performance: Querying
ir_module_moduleacross multiple databases and making PyPI API calls on every scrape would be very expensive (around ~20 seconds per scrape) and could lead to timeouts or high latency in Prometheus, with no real benefit since this data is not needed in real-time for alerting or monitoring. - Cardinality: The number of non-core modules can vary widely across databases and instances. Exposing this via a Prometheus exporter would require complex label management and could lead to high cardinality issues in Prometheus.
- Execute on demand: This service is intended to be hit on-demand (e.g., via a button in
Odoo Instances) rather than being scraped frequently. It can provide a comprehensive snapshot of the Odoo instance state without impacting performance.
🚀 Getting started (dev)
1) Create an app pyenv virtualenv and install dependencies
pyenv virtualenv 3.11.8 odoo-instance-api
pyenv activate odoo-instance-api
pip install -r requirements.txt
export ODOO_VENV_PATH=/home/odoo/pyenv/versions/odoo-16/bin/python
2) Run the service
uvicorn odoo_instance_api.main:app --host 127.0.0.1 --port 8000
Or via the package CLI entrypoint:
odoo-instance-api --host 127.0.0.1 --port 8000
Run with debug logs:
odoo-instance-api --host 127.0.0.1 --port 8000 --log-level debug
3) Hit the endpoint
- JSON:
curl http://127.0.0.1:8000/status - HTML:
curl "http://127.0.0.1:8000/status?format=html"
In HTML mode, these same filters are available as in-page controls (checkbox/select/input) and are synced to the URL query string for shareable links.
Filter examples (work for both JSON and HTML):
- Only DB↔Installed drifts:
curl "http://127.0.0.1:8000/status?only_drift=true" - One database only:
curl "http://127.0.0.1:8000/status?database=odoov16" - Limit relation rows:
curl "http://127.0.0.1:8000/status?limit=50" - Combined:
curl "http://127.0.0.1:8000/status?database=odoov16&only_drift=true&limit=20"
🔧 How it works (core design)
- Single endpoint:
/statusreturns a JSON payload (or HTML when requested). - Entrypoint implementation:
odoo_instance_api/main.py - Odoo inspection environment: Uses
ODOO_VENV_PATH(defaults to/home/odoo/pyenv/versions/odoo-16/bin/python) to runpip list --format=jsonand detect installed Odoo packages. - PyPI checks: For any
odoo*packages found, it querieshttps://pypi.org/pypi/<package>/jsonand compares versions (including pre-releases) usingpackaging.version. - Database discovery: Reads Postgres credentials from
/etc/odoo/odoo.conf(override viaODOO_CONF_PATH) and lists non-template databases, excludingpostgres. - Non-core module listing: For each database, it queries
ir_module_modulewith an Odoo-core author filter and supports DB schemas using eitherinstalled_versionorlatest_version. - Package↔module mapping (Phase A): It inspects installed
odoo*distributions in the Odoopyenvenv, derives addon names from__manifest__.py/__openerp__.pypaths, and matches those names against DB module names. - Version drift diagnostics: For matched pairs, it reports whether versions differ across DB module version, installed package version, and PyPI latest version.
🧩 Environment overrides
Runtime model:
- The API itself runs in an app pyenv (for FastAPI/Uvicorn/dependencies).
- Odoo package inspection runs in the Odoo pyenv pointed by
ODOO_VENV_PATH.
| Env var | Purpose | Default |
|---|---|---|
ODOO_VENV_PATH |
Path to the Odoo Python executable (typically from pyenv) |
/home/odoo/pyenv/versions/odoo-16/bin/python |
ODOO_CONF_PATH |
Path to the Odoo config file (Postgres credentials) | /etc/odoo/odoo.conf |
ODOO_SOURCE_CODE_PATH |
Odoo source root or addons dir; modules under addons are treated as core-distributed | auto-discovered from ODOO_VENV_PATH when unset |
OIA_LOG_LEVEL |
Application log level (critical, error, warning, info, debug) |
INFO |
Centralized assumptions
Schema/runtime assumptions are centralized in odoo_instance_api/assumptions.py.
When Odoo/Postgres conventions change (table names, preferred version columns, core author filters, default paths), update assumptions once instead of patching multiple modules.
You can also override assumptions at runtime via environment variables:
OIA_DEFAULT_ODOO_PYTHON_PATHOIA_DEFAULT_ODOO_CONF_PATHOIA_DEFAULT_ODOO_SOURCE_CODE_PATHOIA_IR_MODULE_TABLEOIA_IR_MODULE_VERSION_COLUMNS(comma-separated)OIA_ODOO_CORE_AUTHORS(comma-separated)OIA_ODOO_CORE_MODULES(comma-separated)OIA_ODOO_PACKAGE_NAME_PREFIXES(comma-separated)OIA_ADDON_MANIFEST_FILENAMES(comma-separated)OIA_PYPI_PACKAGE_JSON_URL_TEMPLATEOIA_PYPI_HTTP_TIMEOUT_SECONDS
Compatibility shortcuts remain supported:
ODOO_VENV_PATH(takes precedence overOIA_DEFAULT_ODOO_PYTHON_PATH)ODOO_CONF_PATH(takes precedence overOIA_DEFAULT_ODOO_CONF_PATH)
Logging
- CLI flag:
--log-level {critical,error,warning,info,debug} - Environment override:
OIA_LOG_LEVEL(used as default when--log-levelis not provided)
Example:
export OIA_LOG_LEVEL=debug
odoo-instance-api --host 127.0.0.1 --port 8000
📦 Mapping output in /status
Optional query params
only_drift=true: keeps only rows where DB module and installed package versions are mismatched.database=<name>: filters output to a single database name.limit=<n>: limitspackage_module_relation_table.rowsto firstnrows.
Defaults remain unchanged when params are omitted.
The JSON response includes two mapping-oriented blocks:
package_module_mapping: detailed diagnostics grouped by databasepackage_module_relation_table: flattened table with one row per (database,pypi_package,db_module) relationuninstalled_module_matches: packages whose candidate modules exist in DB but are currently uninstalledcore_module_exclusions: configured module names treated as core and excluded from unmatched-module accounting
package_module_mapping includes:
method: currentlymanifest_path_matchby_database[]: mapping diagnostics per database, includingpackage_to_modules[],matched_modules[],unmatched_packages[], andunmatched_modules[]stats: global counters (matched_pairs,unmatched_packages,unmatched_modules)
mapping_summary.by_database[] also includes explicit unmatched details (not only counts):
unmatched_package_names[]unmatched_module_names[]
mapping_summary.totals includes deduplicated global lists:
unmatched_package_names[]unmatched_module_names[]
Each matched module includes version_drift flags:
db_vs_package_installeddb_vs_pypi_latestpackage_installed_vs_pypi_latest
package_module_relation_table includes:
countrows[]with:database,pypi_package,db_module,db_module_version,package_installed_version,package_pypi_latest_version, drift flags, and mapping confidence/reason.
Quick inspect command:
curl -s http://127.0.0.1:8000/status | jq '.package_module_relation_table.rows[] | {database, pypi_package, db_module, db_module_version, package_installed_version, package_pypi_latest_version, drift_db_vs_package_installed, drift_db_vs_pypi_latest, drift_package_installed_vs_pypi_latest}'
🔒 Recommended proxy configuration (NGINX)
This service is intended to run behind a proxy (e.g., NGINX) that can handle authentication, TLS, and routing.
Example NGINX snippet:
location /status {
proxy_pass http://127.0.0.1:8000/status;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 60;
proxy_connect_timeout 10;
}
🪲 Troubleshooting
- No databases returned? Verify the service user can read
/etc/odoo/odoo.confand that the Postgres user/credentials can connect. - No Odoo packages listed? Verify
ODOO_VENV_PATHpoints at the Odoo runtimepyenvenvironment and thatpip listworks there. - The service crashes on startup? It should not crash; errors are printed and the service continues with partial results.
- Need more diagnostics? Start with
--log-level debug(or setOIA_LOG_LEVEL=debug) and inspect logs for package counts, interpreter path selection, candidate counts, database discovery, and mapping stats.
🗺 Roadmap (next improvements)
- Add optional fuzzy fallback mapping
- Add a secondary heuristic layer for packages that do not expose manifest paths in wheel metadata.
- Keep confidence tiering explicit (
high/medium/low) and avoid auto-accepting ambiguous matches.
✅ Running as a systemd service
This repo includes an example systemd unit file: odoo-instance-api.service.
Install / enable
- Copy the unit file to systemd's directory:
sudo cp odoo-instance-api.service /etc/systemd/system/
- Reload systemd and enable the service:
sudo systemctl daemon-reload
sudo systemctl enable --now odoo-instance-api.service
- Check the service status:
sudo systemctl status odoo-instance-api.service
sudo journalctl -u odoo-instance-api.service -f
Notes
- The unit file assumes the code lives at
/opt/odoo_instance_api, runs Uvicorn from the app pyenv (/home/odoo/pyenv/versions/odoo-instance-api/bin/uvicorn) with moduleodoo_instance_api.main:app, and setsODOO_VENV_PATHto the Odoo pyenv Python (/home/odoo/pyenv/versions/odoo-16/bin/python). - Update
User=and the paths inExecStart=as needed for your environment. - Authentication and TLS should be handled by a reverse proxy (e.g., NGINX) in front of this service.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file odoo_instance_api-0.1.0.tar.gz.
File metadata
- Download URL: odoo_instance_api-0.1.0.tar.gz
- Upload date:
- Size: 30.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.8.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
714a4eeea4c24f8f37038ac8a60276a2caf6e3675bebe7a441a791bff1da6c05
|
|
| MD5 |
629005308a4d511d5850a4129dea7a06
|
|
| BLAKE2b-256 |
18fb326c8dddb9145f229c11f24f3e66a1a16294b045fcd1936b8b1db750163d
|
File details
Details for the file odoo_instance_api-0.1.0-py3-none-any.whl.
File metadata
- Download URL: odoo_instance_api-0.1.0-py3-none-any.whl
- Upload date:
- Size: 29.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.8.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8af918cc86351ce2fe57b1b93df1ac0da7f6d69ffb11190059e87a137cda5a47
|
|
| MD5 |
c5df48b7b09894cb1a8901dfe0dc43f4
|
|
| BLAKE2b-256 |
a834ac7759b0dc4706f3d88dc8569fef37d1ac2e52ac564f874597881c78c0e0
|