Module Architecture
flowchart TD
BASE["sadeem_saas_base\nSAAS Base"]
NPM["sadeem_nginx_proxy_manager\nNginx Proxy Manager"]
CF["sadeem_cloudflare\nCloudflare"]
RS["sadeem.root.server (inherit)\ninstall Portainer via SSH\ncreate admin + API key"]
P["sadeem.portainer\nServer URL · API key\nstack & container operations"]
ENV["sadeem.portainer.environment\nDocker host · capacity tracking\nNginx · Cloudflare defaults"]
IMG["sadeem.portainer.image\nDocker image config\nenvironment assignment strategy"]
IMGTAG["sadeem.portainer.image.tag\nversion tags per image"]
IMGENV["sadeem.portainer.image.environment\nimage to environment mapping"]
PG["sadeem.postgresql.server\nPostgres server config\nadded by portainer module"]
EXT[/Portainer API\nAPI key auth/]
BASE --> RS
NPM --> P
CF --> P
RS --> A1([action_install_portainer\ndeploy via SSH + create admin + API key])
RS --> A2([action_install_portainer_master\ndeploy as Portainer master node])
P --> A3([action_confirm\nvalidate API key → active])
P --> A4([action_open_portainer_panel\nopen Portainer web UI])
P --> A5([action_create_stack\ndeploy new Docker stack])
P --> A6([action_update_stack\nredeploy with new compose config])
P --> A7([action_stack_actions\nstart or stop stack])
P --> A8([action_delete_stack\nremove stack from Docker host])
P --> A9([action_check_stack_status\nGET stack + container states → chatter])
P --> A10([action_docker_actions\nrestart / start / stop container])
P --> A11([action_docker_exec\nexecute command in container])
ENV --> A12([get_best_environment\npick env by sequence or lowest usage])
ENV --> A13([get_environment_info\nsync live capacity from Portainer])
IMG --> IMGTAG
IMG --> IMGENV
IMGENV --> ENV
P --> ENV
P --> PG
A3 --> EXT
A5 --> EXT
A6 --> EXT
A7 --> EXT
A8 --> EXT
A9 --> EXT
A10 --> EXT
A11 --> EXT
Module Description
Integrates Odoo with the Portainer API for Docker infrastructure orchestration.
| Area |
What it does |
| Portainer Servers |
Connect via API key; install Portainer (agent or master) on root servers via SSH with auto admin setup |
| Environments |
Track Docker hosts with real-time container capacity; carry Nginx/Cloudflare/domain defaults for subscriptions |
| Stack Management |
Create, update, start, stop, delete Docker stacks; check stack and container health |
| Container Control |
Restart, start, stop, and exec commands in containers |
| Image Config |
Define Docker images with version tags and environment assignment strategies |
| Environment Selection |
get_best_environment() picks optimal Docker host by sequence or lowest usage |
Dependencies
mail
sadeem_saas_base
sadeem_nginx_proxy_manager
sadeem_cloudflare
SAAS Management
└─ Infrastructure
└─ Portainer
├─ Portainer Servers
├─ Environments
├─ Image Config
└─ Image Tags
Key Fields
Portainer Server (sadeem.portainer)
| Field |
Type |
Description |
| portainer_link |
Char |
URL of the Portainer server (required) |
| ip |
Char |
IP address of the Portainer server (required) |
| portainer_access_token |
Char |
Portainer API key for authentication |
| state |
Selection |
Server status: draft, confirm, cancel |
| server_id |
Many2one |
Linked sadeem.root.server (for SSH operations) |
| skip_ssl_verify |
Boolean |
Skip SSL certificate verification when calling Portainer API (default: True) |
| master_portainer |
Boolean |
Whether this is a master/central Portainer node |
Portainer Environment (sadeem.portainer.environment)
| Field |
Type |
Description |
| portainer_environment_id |
Integer |
Portainer's internal environment/endpoint ID (required) |
| portainer_id |
Many2one |
Parent Portainer server (required) |
| ip |
Char |
IP of the Docker host (required) |
| next_port |
Integer |
Next available port to assign to new containers (default: 10000) |
| max_running_container_count |
Integer |
Configured capacity limit for this environment |
| available_container_capacity |
Integer |
Computed remaining slots (max − container_run) |
| container_run / container_stop / container_pause / container_total |
Integer |
Live container counts (synced from Portainer) |
| state |
Selection |
Capacity status: normal, over |
| postgresql_server_external |
Boolean |
Whether to use an external PostgreSQL server |
| postgresql_server_id |
Many2one |
External PostgreSQL server for this environment |
| cloudflare_dns |
Boolean |
Whether Cloudflare DNS is managed for this environment |
| cloudflare_account_id |
Many2one |
Cloudflare connector for DNS record creation |
| cloudflare_domain_zone_id |
Char |
Cloudflare Zone ID for the environment's domain |
| cloudflare_create_record |
Boolean |
Auto-create DNS records for new subscriptions |
| cloudflare_proxied_record |
Boolean |
Whether auto-created DNS records are proxied |
| domain_for_new_sub |
Char |
Base domain used when generating subscription subdomains |
| subdomain_start |
Char |
Prefix for generated subdomains |
| nginx_default_cert_id |
Integer |
Default SSL certificate ID on the NPM server |
| nginx_proxy_server_id |
Many2one |
NPM connector for this environment |
Docker Image Config (sadeem.portainer.image)
| Field |
Type |
Description |
| odoo_version |
Char |
Version label displayed to clients (required) |
| config_file |
Char |
Path to the Docker Compose configuration file (required) |
| image_tags |
Many2many |
Linked version tags for this image |
| environment_id |
Many2one |
Default environment for this image (optional) |
| select_environment_by |
Selection |
Environment selection strategy: sequence or used (low usage) |
| image_environment_id |
One2many |
Per-environment assignment overrides |
Image Environment Mapping (sadeem.portainer.image.environment)
| Field |
Type |
Description |
| image_id |
Many2one |
Image this mapping belongs to (required) |
| environment_id |
Many2one |
Target environment |
| sequence |
Integer |
Priority order for environment selection |
| max_running_container_count |
Integer |
Capacity override for this image in this environment |
| available_container_capacity |
Integer |
Computed available capacity |
Lifecycle & States
Portainer Server States
| State |
Meaning |
draft |
API key not yet verified |
confirm |
API key valid, server is active |
cancel |
Server disabled |
Transitions:
- draft → confirm: action_confirm — validates portainer_link and portainer_access_token are set
- confirm → draft: action_reset_draft
- Any → cancel: action_cancel
Environment Capacity States
| State |
Meaning |
normal |
Running containers below max capacity |
over |
Running containers at or above max capacity |
Updated automatically when get_environment_info syncs container counts from Portainer.
Key Actions
| Action |
Model |
Description |
action_install_portainer |
sadeem.root.server (inherit) |
Deploys Portainer agent via SSH on the root server; creates admin user and API key |
action_install_portainer_master |
sadeem.root.server (inherit) |
Deploys Portainer as master node |
action_confirm |
sadeem.portainer |
Validates API credentials; moves to confirm state |
action_open_portainer_panel |
sadeem.portainer |
Returns URL action to open Portainer web UI |
action_create_stack |
sadeem.portainer |
Deploys a new Docker stack from a compose string |
action_update_stack |
sadeem.portainer |
Redeploys an existing stack with new compose config |
action_stack_actions |
sadeem.portainer |
Starts or stops a Docker stack |
action_delete_stack |
sadeem.portainer |
Removes a stack from the Docker host |
action_check_stack_status |
sadeem.portainer |
Gets live stack and container states from Portainer; posts to chatter |
action_docker_actions |
sadeem.portainer |
Restart / start / stop a specific container |
action_docker_exec |
sadeem.portainer |
Executes a shell command inside a running container |
get_environment_info |
sadeem.portainer.environment |
Syncs live container counts and resource info from Portainer |
get_best_environment |
sadeem.portainer.image |
Selects optimal environment by sequence or lowest container usage |
Warnings & Important Notes
- Missing API credentials:
action_confirm raises UserError('Missing required data') if portainer_link or portainer_access_token is not set.
- SSL verification skipped by default:
skip_ssl_verify=True on the portainer server — change this in production if using valid certs.
- Port tracking:
next_port on the environment is incremented each time a subscription is provisioned; it must be initialized correctly to avoid port conflicts.
- Capacity tracking:
available_container_capacity is computed as max_running_container_count − container_run; the environment moves to over state when this reaches zero.
- Environment selection strategy:
select_environment_by = 'sequence' picks the first available environment in order; 'used' picks the one with lowest running container count.
- PostgreSQL server inheritance: Fields on
sadeem.postgresql.server are extended by this module (added portainer_id linking).