New to Kestra?
Use blueprints to kickstart your first workflows.
Count overdue Odoo ir.cron scheduled actions via XML-RPC on a schedule, change nothing in Odoo, and alert Slack when background jobs stall.
Odoo runs almost every background process, invoicing automation, email queues, inventory reordering rules, through its own ir.cron scheduled-action worker, and when that worker gets stuck or dies, dozens of jobs quietly stop running at once with no error anyone sees in the UI. This blueprint turns a scheduled, read-only count into a Day 2 infrastructure health gate: io.kestra.plugin.odoo.Query asks Odoo's own XML-RPC API how many active scheduled actions are overdue past their nextcall time, a single Pebble expression classifies that count, and the result routes to a quiet log, an informational Slack note, or a Slack page depending on severity.
count_overdue_crons (io.kestra.plugin.odoo.Query) runs a SEARCH_COUNT against the ir.cron model with a domain filter for active = true and nextcall <= now() - overdue_grace_minutes. The cutoff timestamp is computed entirely in Kestra with now() | dateAdd(...) | date(...) before the request is sent, so Odoo only ever has to answer a single count query. This is a read: no record is created, written, or deleted. allowFailure: true keeps an unreachable instance or bad credentials from aborting the flow outright.classify_cron_health (io.kestra.plugin.core.debug.Return) evaluates a single Pebble ternary against the returned count: missing entirely becomes UNREACHABLE, at or above critical_overdue_count becomes CRITICAL, at or above warning_overdue_count becomes WARNING, otherwise HEALTHY.route_by_cron_health (io.kestra.plugin.core.flow.Switch) branches on that classification: HEALTHY logs a quiet confirmation; WARNING posts an informational Slack note; CRITICAL and UNREACHABLE each page Slack; a defaults fallback logs the raw value for any classification outside the four known cases.log_audit_result (io.kestra.plugin.core.log.Log) writes one audit line per run, independent of which Switch branch fired, so the check's own history stays queryable even if Slack delivery fails.errors block pages Slack separately if the flow fails outright, so a broken sentinel is never mistaken for a healthy instance.cron_overdue_schedule trigger checks every 15 minutes and ships disabled: true, with explicit inputs: overrides for overdue_grace_minutes and warning_overdue_count so the scheduled run's tolerance is pinned regardless of this flow's own defaults.status output (HEALTHY / WARNING / CRITICAL / UNREACHABLE) other flows can gate on.Odoo has no built-in alert for its own cron worker going down; the first sign is usually a business process that should have happened and did not. Kestra's scheduled, read-only count check gives that signal automatically: the schedule, the threshold classification, and the Slack call are all one auditable execution history, with the Switch branch ready to grow into real remediation (restarting the Odoo cron workers, opening a ticket) without rewriting the check itself.
odoo_url with XML-RPC enabled (on by default).ir.cron model.ODOO_USERNAME, ODOO_PASSWORD: Odoo login credentials used for XML-RPC authentication.SLACK_WEBHOOK: Slack incoming webhook used for every alert branch and the flow-failure alert.In Kestra OSS (no Enterprise secrets backend), secrets are supplied as environment variables prefixed SECRET_, base64-encoded, and read back in flows with {{ secret('NAME') }}, for example SECRET_SLACK_WEBHOOK=$(echo -n 'https://hooks.slack.com/services/<placeholder>' | base64). This keeps credentials out of the flow YAML but, per Kestra's own documentation, offers no encryption at rest or access control beyond the host environment; use the Enterprise secrets backend for stronger guarantees.
docker run -d --name odoo-db -e POSTGRES_USER=odoo -e POSTGRES_PASSWORD=odoo -e POSTGRES_DB=postgres postgres:15
docker run -d --name odoo-app --link odoo-db:db -p 8069:8069 odoo:17docker network ls for Kestra's network and attach the Odoo container to it:
docker network connect YOUR_KESTRA_NETWORK odoo-app
Then set odoo_url to http://odoo-app:8069. From a host-based Kestra install, use http://localhost:8069 instead.odoo_url, noting the database name for odoo_db.ODOO_USERNAME, ODOO_PASSWORD, and SLACK_WEBHOOK secrets, and run the flow once manually to confirm status reads HEALTHY on a freshly created instance.cron_overdue_schedule once pointed at a real instance.operation from SEARCH_COUNT to SEARCH_READ with fields: ["name", "nextcall"] to name the specific overdue scheduled actions in the Slack alert instead of just the count.name (for example, the invoicing or email-queue job) with its own tighter grace period.status into a KV store and alert only on state transitions instead of every run.CRITICAL branch into a remediation step that restarts the Odoo cron workers via your infrastructure's own automation.odoo_url: base URL of the Odoo instance.odoo_db: the Odoo database to connect to.overdue_grace_minutes: minutes past nextcall before a scheduled action counts as overdue.warning_overdue_count: overdue-count floor at or above which the check is classified WARNING.critical_overdue_count: overdue-count floor at or above which the check is classified CRITICAL.status: one of HEALTHY, WARNING, CRITICAL, UNREACHABLE.{{ outputs.classify_cron_health.value }}, also exposed as the flow output {{ outputs.status }}.now() | dateAdd(...) | date(...) cutoff computation runs in Kestra's own timezone context; if the Odoo server stores nextcall in a different timezone than Kestra expects, the grace period can be effectively shorter or longer than configured. Confirm both systems agree on UTC before trusting the exact threshold.SEARCH_COUNT; it never calls Odoo's CREATE, WRITE, or UNLINK operations, so there is nothing destructive to default to dry-run for.overdue_grace_minutes right after its last run if nextcall was computed far in the future incorrectly; check one connector's schedule before assuming the whole instance is unhealthy from a single WARNING.https:// in odoo_url for any instance reachable outside a trusted network.