Workflow-based Topology Provisioning Orchestrator for Hybrid Cloud Infrastructure
Cloud Engine is a hybrid cloud orchestration platform that provisions and manages network slices — groups of interconnected virtual machines — across heterogeneous compute infrastructure. Organizations using Cloud Engine may run their workloads entirely on-premise, entirely on public cloud, or across both simultaneously: the platform treats on-premise KVM/libvirt clusters, private OpenStack deployments, and public cloud providers (AWS, in progress) as interchangeable backends behind a single unified API. The target infrastructure belongs to the organization; Cloud Engine is the control plane that drives it.
All provisioning is orchestrated through Temporal durable workflows with topological DAG execution and automatic saga-style rollback on failure. A suite of seven FastAPI microservices handles the platform logic: quota management, topology-aware VM placement (via Google OR-Tools), network address allocation, and user/workspace lifecycle. The frontend is a Next.js single-page application with a visual drag-and-drop slice topology editor and an in-browser noVNC console.
Cloud Engine was built to lower the operational barrier of multi-VM network experimentation for the people who need it most but rarely have a purpose-built tool for it: IT teams managing shared lab infrastructure across multiple projects, system administrators who spend hours manually scripting VM and network setup for each tenant, researchers who need reproducible, isolated network topologies without touching the underlying hypervisor, and education centers running networking or distributed systems courses where every student group needs their own isolated environment on demand. Instead of writing provisioning scripts or navigating raw hypervisor CLIs, users describe a topology — which VMs exist, how they connect, what firewall rules apply — and Cloud Engine handles the rest: placement, VLAN assignment, port wiring, image import, and full lifecycle management, with durable retries and rollback built in.
- Dual infrastructure support — same workflow engine provisions VMs on KVM/libvirt (SSH + bash + OVS) and OpenStack (Nova/Neutron/Glance/Keystone), selected per slice at runtime
- AWS driver (in progress) — third infrastructure backend targeting EC2/VPC/ENI/Security Groups; the same Temporal DAG workflow and activity interface apply with a new
aws-cluster-activity-workerpollingaws-cluster-queue - Durable workflow orchestration — Temporal-based DAG interpreter runs provisioning steps in parallel phases; saga compensation stack undoes completed steps on any failure
- Topology-aware VM placement — Google OR-Tools bin-packing assigns VMs to physical servers based on real-time CPU/RAM/disk availability queried from Prometheus
- Multi-domain RBAC — platform roles (
administrador,coordinador,usuario_estandar) combined with per-workspace roles (propietario,colaborador); admins can act on any workspace without membership - Visual slice editor — React Flow canvas for drag-and-drop VM and link topology design, with property panels and named template save/load
- In-browser VNC console — noVNC client routed through a VNC token service and per-cluster websockify proxy; tokens are time-limited and connection params are snapshotted at issuance
- Workspace quota system — per-workspace vCPU/RAM/storage/slice-count limits enforced transactionally; workspace types define quota tiers; restricted types require admin approval
- Slice operator assignments — workspace members can delegate slice operation access to specific users outside their workspace
- End-to-end structured logging — all services emit JSON logs (structlog) collected by Grafana Alloy via the Docker socket and stored in Loki;
trace_idfrom Kong'scorrelation-idplugin spans the full request path - Real-time cluster metrics — custom
ce_exporter(Prometheus, port 9200) on each compute node exposes host CPU/RAM/disk and per-VM metrics at 1-second resolution; pre-built Grafana dashboards per infrastructure - Kong API gateway — DB-less declarative config with RS256 JWT validation (Keycloak-issued), CORS, per-consumer rate limiting, and correlation ID injection
TEST_MODE— bypass Temporal entirely; the slice-manager prints the fullWorkflowSpecto stdout and immediately simulates completion, enabling end-to-end planning tests without cluster infrastructure
Browser
│
▼
Nginx (TLS :443)
│
├──► Next.js Frontend (React Flow editor, noVNC, role-based routing)
│
└──► Kong API Gateway (RS256 JWT · CORS · rate-limit · trace-id)
│
▼
Business Layer — 7 FastAPI microservices
┌──────────┬──────────┬────────────┬─────────────────┐
│ slice- │ catalog │ quota- │ placement │
│ manager │ │ accounting │ (OR-Tools) │
├──────────┴──────────┴────────────┴─────────────────┤
│ networking-security │ vnc-proxy │ admin- │
│ (VLAN/MAC/IP/port) │ (WS relay) │ management │
└───────────────────────┴─────────────┴───────────────┘
│ │
PostgreSQL Temporal gRPC
(mydb) │
▼
Temporal Workflow Engine
┌────────────────────────────────┐
│ create-slice (DAG + saga) │
│ delete-slice (DAG, no saga) │
│ manage-vm (power actions) │
└────────────────────────────────┘
│
┌─────────────────────── ┴ ───────────────────────────┐
▼ ▼
linux-cluster-queue openstack-cluster-queue
KVM Activity Worker OpenStack Activity Worker
SSH + bash scripts + OVS + libvirt/QEMU Nova · Neutron · Glance · Keystone
│ │
Linux Cluster Headnode API OpenStack cluster direct API calls
(FastAPI + paramiko)
│
├──► business-layer-queue (notify.* activities → slice-manager internal)
│
└──► Observability
Alloy → Loki → Grafana (logs)
ce_exporter → Prometheus → Grafana (metrics)
The workflow worker interprets WorkflowSpec as a topological DAG: it repeatedly selects steps whose declared dependencies are satisfied and runs them in parallel via asyncio.gather. Activities prefixed notify.* are always routed to business-layer-queue (calls back into slice-manager); all other activities go to the infrastructure-specific driver queue (linux-cluster-queue or openstack-cluster-queue) determined by the target infrastructure's configuration.
AWS support (in progress): A third driver (
aws-cluster-activity-worker) is planned for EC2/VPC. The workflow DAG is infrastructure-agnostic by design — only the activity implementations change. Network control at the VLAN level is replaced by VPC subnets, ENIs, and Security Groups, preserving the topology abstraction.
| Layer | Technology |
|---|---|
| Frontend | Next.js 16, React 19, TypeScript 5, Tailwind CSS 4, React Flow (@xyflow/react), noVNC 1.5 |
| Auth & Gateway | Keycloak 24 (OIDC, RS256 JWT), Kong 3.6 (DB-less, declarative), Nginx |
| Business Layer | Python 3.11, FastAPI 0.110, psycopg2, structlog 24, Google OR-Tools 9.11, websockets 12 |
| Workflow Engine | Temporal 1.31, Python 3.12, temporalio SDK 1.7, httpx 0.27 |
| KVM Driver | Python 3.11, paramiko 3.4, Open vSwitch, libvirt / QEMU / KVM |
| OpenStack Driver | Python 3.12, Nova / Neutron / Glance / Keystone REST APIs |
| Data | PostgreSQL 16, Elasticsearch 7.17 (Temporal workflow visibility) |
| Observability | Grafana Loki 3.7, Grafana Alloy 1.17, Prometheus 2.51, Grafana, custom ce_exporter |
| Infrastructure | Docker Compose (26 services, 5 isolated networks), Nginx TLS |
- Docker 24+ and Docker Compose v2
- A reachable KVM cluster (headnode IP + SSH private key) and/or OpenStack cluster with admin credentials
— or set
TEST_MODE=trueto skip infrastructure entirely and test the planning layer only - All
.envfiles populated (see Configuration) - TLS certificates — generated automatically by
scripts/setup.sh(self-signed)
git clone <repo-url>
cd Cloud_Engine_Project
# 1. Copy and fill in the env files
cp business_layer/.env.example business_layer/.env
cp linux-cluster-services/.env.example linux-cluster-services/.env
cp openstack-cluster-services/.env.example openstack-cluster-services/.env
# ... (see Configuration below)
# 2. Bring up the full stack
bash scripts/setup.sh
# Access points:
# Web UI: https://localhost
# Temporal UI: http://127.0.0.1:8081
# Grafana: https://localhost/grafanaSetup script flags:
| Flag | Effect |
|---|---|
--skip-validate |
Skip .env completeness checks |
--no-build |
Skip Docker image rebuilds (faster restart) |
To rebuild a single service without restarting the whole stack:
docker compose up -d --build slice-managerNote: After recreating any Kong-fronted service, run
docker compose restart kong— Kong caches the upstream IP and will return 502s until restarted.
Each component reads from its own .env file. scripts/validate.sh checks all of them for unfilled placeholder values.
| Variable | Description |
|---|---|
DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASSWORD |
PostgreSQL connection |
TEMPORAL_HOST, TEMPORAL_PORT, TEMPORAL_NAMESPACE, TEMPORAL_WORKFLOW_QUEUE |
Temporal server |
TEST_MODE |
true = bypass Temporal, simulate completion locally |
PLACEMENT_URL, NETWORKING_URL, QUOTA_URL, CATALOG_URL |
Internal service URLs |
IMAGES_VOLUME_PATH |
Mount path of the shared image volume in the catalog container |
KC_ADMIN_USER, KC_ADMIN_PASSWORD |
Keycloak master-realm admin credentials |
| Variable | Description |
|---|---|
API_KEY |
Bearer token required on all headnode API calls |
SSH_KEY_PATH |
Path to the private key mounted into the headnode container |
HEADNODE_IP |
Management IP of the KVM headnode (default 10.0.10.4) |
| Variable | Description |
|---|---|
OS_AUTH_URL |
Keystone identity endpoint |
OS_USERNAME, OS_PASSWORD, OS_PROJECT_NAME, OS_DOMAIN_NAME |
Admin OpenStack credentials |
EXTERNAL_NETWORK_NAME |
Provider network name for external-access ports |
Cloud_Engine_Project/
├── docker-compose.yml # Canonical unified compose (26 services)
├── scripts/
│ ├── setup.sh # First-time bring-up: validate → TLS → Kong config → compose up
│ └── validate.sh # .env completeness and cross-service consistency checks
│
├── business_layer/ # Internal microservices + shared DB + Grafana
│ ├── database/ # PostgreSQL schema (db_cloud_v1_postgres.sql) + seed data
│ ├── slice_manager/ # Orchestrator — sole external API entry point
│ ├── image_catalog/ # VM image and flavor management
│ ├── quota_accounting/ # Workspace quota enforcement
│ ├── placement/ # VM-to-server bin-packing (OR-Tools)
│ ├── networking_security/ # VLAN/MAC/IP/VNC-port allocation
│ ├── vnc_proxy/ # WebSocket relay to cluster websockify
│ ├── admin_management/ # User CRUD + Keycloak sync
│ └── grafana/ # Pre-provisioned dashboards (metrics + logs)
│
├── workflow_engine/
│ ├── temporal_server/ # Temporal server config + namespace init scripts
│ ├── workflow_worker/ # DAG interpreter: create-slice, delete-slice, manage-vm
│ ├── linux_cluster_activity_worker/ # KVM driver activities (polls linux-cluster-queue)
│ ├── openstack_cluster_activity_worker/ # OpenStack driver activities (polls openstack-cluster-queue)
│ └── business_layer_activity_worker/ # notify.* activities (polls business-layer-queue)
│
├── linux-cluster-services/ # Headnode REST API (FastAPI + paramiko + OVS/libvirt)
├── linux_driver_scripts/ # Bash scripts executed on cluster nodes via SSH
├── openstack-cluster-services/ # VNC token service + websockify + metrics for OpenStack
├── observability/ # Loki config + Grafana Alloy log pipeline
├── frontend/
│ ├── keycloak/ # Realm JSON import (fixed RSA key pair)
│ ├── kong/ # DB-less declarative gateway config
│ └── webui/ # Next.js 16 App Router application
└── documentation/ # API spec, flow examples, changelogs
All requests are routed through Kong (https://localhost/api/...). Kong validates the RS256 JWT issued by Keycloak and injects user_id and system_role claims.
| Method | Path | Description |
|---|---|---|
GET |
/api/slices |
List all slices |
POST |
/api/slices |
Create a slice → triggers create-slice Temporal workflow |
GET |
/api/slices/{id} |
Get slice detail (VMs, interfaces, links, security rules) |
DELETE |
/api/slices/{id} |
Delete a slice → triggers delete-slice Temporal workflow |
POST |
/api/slices/{id}/vms/{vm_id}/power |
Power action (start / stop / pause / resume) |
POST |
/api/slices/{id}/vms/{vm_id}/vnc-token |
Issue a time-limited VNC console token |
GET/POST/DELETE |
/api/slices/{id}/operators |
Manage per-slice operator assignments |
| Method | Path | Description |
|---|---|---|
GET |
/api/workspaces |
List workspaces visible to caller |
POST |
/api/workspaces |
Create a workspace (may require admin approval) |
POST |
/api/workspaces/{id}/approve |
Admin approves a pending workspace |
GET/POST/DELETE |
/api/workspaces/{id}/members |
Manage workspace membership |
| Method | Path | Description |
|---|---|---|
GET |
/catalog/images |
List available VM images |
POST |
/catalog/images/upload |
Upload a VM image to the shared catalog volume |
GET |
/catalog/flavors |
List available compute flavors |
POST |
/api/admin/users |
Create a platform user (also registers in Keycloak) |
POST |
/api/admin/infrastructures |
Register a compute infrastructure with AZs and servers |
GET/POST |
/api/admin/workspace-types |
Manage quota tier templates |
GET/POST |
/api/templates |
Manage per-user named slice topology templates |
See business_layer/documentation/API.md for the full request/response schema reference.
A custom ce_exporter (port 9200) runs on each compute node and exposes:
- Host metrics —
ce_host_cpu_total,ce_host_memory_total_bytes,ce_host_filesystem_size_bytes, and their utilization counterparts - VM aggregate metrics —
ce_host_vm_cpu_seconds_total,ce_host_vm_vcpu_total,ce_host_vm_memory_rss_bytes,ce_instances_used_bytes
Prometheus scrapes at 1-second resolution. Grafana dashboards are provisioned per infrastructure at startup; the Cloud Engine — Infraestructura dashboard provides CPU/RAM/disk timeseries alongside scheduler reservation data from PostgreSQL.
All services emit structured JSON logs with a shared schema:
| Field | Description |
|---|---|
ts |
ISO 8601 timestamp |
level |
debug / info / warning / error — used as a Loki stream label |
service |
Service name (e.g. slice-manager, workflow-worker) |
msg |
Human-readable message |
trace_id |
Kong correlation-id — spans the full request path |
slice_id |
Present on all log lines related to a specific slice |
workflow_id |
Temporal workflow run ID |
Grafana Alloy collects logs from all containers via the Docker socket, parses the JSON, and pushes to Loki. The Cloud Engine — Logs Grafana dashboard enables log exploration filtered by service, level, trace_id, or slice_id.
Set TEST_MODE=true in business_layer/.env to run the full planning path (quota check, placement, network allocation, WorkflowSpec construction) without a live Temporal server or cluster infrastructure:
# business_layer/.env
TEST_MODE=trueThe slice-manager will print the complete WorkflowSpec or DeleteWorkflowSpec JSON to stdout and immediately transition the slice to activo / eliminado, skipping all Temporal and driver interactions. This is useful for iterating on the planning layer in isolation.
- Fork the repository and create a feature branch off
main - Keep changes scoped — one logical change per pull request
- Open a pull request against
mainwith a clear description of the change and how to test it - No formal CLA required; contributions are accepted under Apache 2.0
Licensed under the Apache License 2.0.
Copyright 2026 Tony F. & Christian F.