Loading docker-celery-3.0.5/ansible/deploy.yml +1 −0 Original line number Diff line number Diff line Loading @@ -257,6 +257,7 @@ export MASTER_IP="{{ hostvars[groups['master'][0]]['ansible_host'] | default('127.0.0.1') }}" export JWT_SECRET="{{ jwt_secret | default('eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhaXJmbG93IiwiaWF0IjoxNzU1NDQ3NjMxLCJleHAiOjE3ODY5ODM2MzF9.CbxQayEt370iHhvYXhHMESsk1IWmi8QLMOcctIlbicXhybNtBbfboDUdUDMClwvkSuTjC0AOeSHL23pGyy0plQ') }}" export ENV="{{ _env }}" export WORKER_LABEL="{{ inventory_hostname }}" export WORKER_CONCURRENCY="{{ worker_concurrency | default(16) }}" COMPOSE_FILES="-f docker-compose.yaml" Loading docker-celery-3.0.5/ansible/update.yml +1 −1 Original line number Diff line number Diff line Loading @@ -267,7 +267,7 @@ export MASTER_IP="{{ hostvars[groups['master'][0]]['ansible_host'] | default('127.0.0.1') }}" export JWT_SECRET="{{ jwt_secret | default('eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhaXJmbG93IiwiaWF0IjoxNzU1NDQ3NjMxLCJleHAiOjE3ODY5ODM2MzF9.CbxQayEt370iHhvYXhHMESsk1IWmi8QLMOcctIlbicXhybNtBbfboDUdUDMClwvkSuTjC0AOeSHL23pGyy0plQ') }}" export ENV="{{ _env }}" export WORKER_LABEL="{{ ansible_host | default(inventory_hostname) }}" export WORKER_LABEL="{{ inventory_hostname }}" export WORKER_CONCURRENCY="{{ worker_concurrency | default(16) }}" export REGISTRY_PULL="{{ registry_pull | default('false') }}" Loading docker-celery-3.0.5/api_gateway/main.py +40 −0 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ from datetime import datetime, timezone import logging from prometheus_fastapi_instrumentator import Instrumentator from urllib.parse import urlparse from pathlib import Path from harbor import build_harbor_client_from_env, resolve_harbor_project Loading Loading @@ -80,6 +81,38 @@ SUPPORTED_PRIORITIES = {"high", "normal", "low"} PRIORITY_ALIASES = { "medium": "normal", } def extract_worker_label(worker_name: str) -> str: host_part = worker_name.split("@", 1)[1] if "@" in worker_name else worker_name return host_part.rsplit("-", 1)[0] if "-" in host_part else host_part def load_worker_inventory_hosts() -> Dict[str, str]: env_name = os.getenv("ENV", "").strip() if not env_name: return {} inventory_path = Path(f"deploy_configs/{env_name}/inventory.ini") if not inventory_path.exists(): return {} hosts: Dict[str, str] = {} in_worker_section = False for raw_line in inventory_path.read_text(encoding="utf-8").splitlines(): line = raw_line.strip() if not line or line.startswith("#"): continue if line.startswith("[") and line.endswith("]"): in_worker_section = line.lower() == "[worker]" continue if not in_worker_section: continue parts = line.split() if not parts: continue inventory_name = parts[0] for token in parts[1:]: if token.startswith("ansible_host="): hosts[inventory_name] = token.split("=", 1)[1] break return hosts PRIORITY_DAG_SUFFIXES = { "high": "__high", "normal": "", Loading Loading @@ -697,6 +730,7 @@ def get_flower_workers(db: Session = Depends(get_db), current_user: User = Depen try: FLOWER_AUTH_USER = os.getenv("_AIRFLOW_WWW_USER_USERNAME", AIRFLOW_USER) FLOWER_AUTH_PASS = os.getenv("_AIRFLOW_WWW_USER_PASSWORD", AIRFLOW_PASS) inventory_worker_hosts = load_worker_inventory_hosts() response = requests.get( f"{FLOWER_URL}/api/workers", Loading @@ -722,6 +756,12 @@ def get_flower_workers(db: Session = Depends(get_db), current_user: User = Depen # Merge occupied slots with flower response for worker_name, info in workers_data.items(): worker_label = extract_worker_label(worker_name) worker_ip = inventory_worker_hosts.get(worker_label, "") if not worker_ip and worker_label.count(".") == 3: worker_ip = worker_label info["worker_label"] = worker_label info["worker_ip"] = worker_ip if info.get('status') is False: info['occupied_slots'] = 0 continue Loading docker-celery-3.0.5/frontend_vue/src/components/WorkersView.vue +26 −20 Original line number Diff line number Diff line Loading @@ -22,6 +22,9 @@ <el-icon class="text-gray-400 text-xl"><Platform /></el-icon> <div class="flex flex-col"> <span class="font-medium text-gray-800">{{ scope.row.name }}</span> <span v-if="scope.row.raw_name && scope.row.raw_name !== scope.row.name" class="text-xs text-gray-400 font-mono"> {{ scope.row.raw_name }} </span> </div> </div> </template> Loading Loading @@ -89,6 +92,24 @@ const workers = ref([]) const loading = ref(false) let timer = null const parseWorkerLabel = (workerName) => { const parts = workerName.split('@') if (parts.length <= 1) return workerName const hostPart = parts[1] const lastHyphen = hostPart.lastIndexOf('-') return lastHyphen > 0 ? hostPart.slice(0, lastHyphen) : hostPart } const parseWorkerIp = (workerName, workerLabel) => { const exactIpMatch = String(workerLabel || '').match(/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/) if (exactIpMatch) return exactIpMatch[1] const parts = workerName.split('@') if (parts.length <= 1) return '' const hostPart = parts[1] const ipMatch = hostPart.match(/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(?:-|$)/) return ipMatch ? ipMatch[1] : '' } const fetchWorkers = async () => { try { loading.value = true Loading @@ -97,6 +118,8 @@ const fetchWorkers = async () => { workers.value = Object.keys(data).map(workerName => { const workerInfo = data[workerName] const workerLabel = workerInfo.worker_label || parseWorkerLabel(workerName) const workerIp = workerInfo.worker_ip || parseWorkerIp(workerName, workerLabel) const isOnline = workerInfo && workerInfo.status !== false // Flower omit status when online or set false when offline let activeTasks = 0 let processedTasks = 0 Loading @@ -113,27 +136,10 @@ const fetchWorkers = async () => { occupiedSlots = workerInfo.occupied_slots || 0 } // Parse IP from workerName. Expected format: celery@<IP>-<HOSTNAME> let ip = '' const parts = workerName.split('@') if (parts.length > 1) { const hostPart = parts[1] // Extract IP if it matches IP pattern before a hyphen const ipMatch = hostPart.match(/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})-/) if (ipMatch) { ip = ipMatch[1] } else { // If no hyphen, the whole part might be the IP const exactIpMatch = hostPart.match(/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/) if (exactIpMatch) { ip = exactIpMatch[1] } } } return { name: workerName, ip: ip, name: workerLabel || workerName, raw_name: workerName, ip: workerIp, status: isOnline, active: activeTasks, processed: processedTasks, Loading Loading
docker-celery-3.0.5/ansible/deploy.yml +1 −0 Original line number Diff line number Diff line Loading @@ -257,6 +257,7 @@ export MASTER_IP="{{ hostvars[groups['master'][0]]['ansible_host'] | default('127.0.0.1') }}" export JWT_SECRET="{{ jwt_secret | default('eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhaXJmbG93IiwiaWF0IjoxNzU1NDQ3NjMxLCJleHAiOjE3ODY5ODM2MzF9.CbxQayEt370iHhvYXhHMESsk1IWmi8QLMOcctIlbicXhybNtBbfboDUdUDMClwvkSuTjC0AOeSHL23pGyy0plQ') }}" export ENV="{{ _env }}" export WORKER_LABEL="{{ inventory_hostname }}" export WORKER_CONCURRENCY="{{ worker_concurrency | default(16) }}" COMPOSE_FILES="-f docker-compose.yaml" Loading
docker-celery-3.0.5/ansible/update.yml +1 −1 Original line number Diff line number Diff line Loading @@ -267,7 +267,7 @@ export MASTER_IP="{{ hostvars[groups['master'][0]]['ansible_host'] | default('127.0.0.1') }}" export JWT_SECRET="{{ jwt_secret | default('eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhaXJmbG93IiwiaWF0IjoxNzU1NDQ3NjMxLCJleHAiOjE3ODY5ODM2MzF9.CbxQayEt370iHhvYXhHMESsk1IWmi8QLMOcctIlbicXhybNtBbfboDUdUDMClwvkSuTjC0AOeSHL23pGyy0plQ') }}" export ENV="{{ _env }}" export WORKER_LABEL="{{ ansible_host | default(inventory_hostname) }}" export WORKER_LABEL="{{ inventory_hostname }}" export WORKER_CONCURRENCY="{{ worker_concurrency | default(16) }}" export REGISTRY_PULL="{{ registry_pull | default('false') }}" Loading
docker-celery-3.0.5/api_gateway/main.py +40 −0 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ from datetime import datetime, timezone import logging from prometheus_fastapi_instrumentator import Instrumentator from urllib.parse import urlparse from pathlib import Path from harbor import build_harbor_client_from_env, resolve_harbor_project Loading Loading @@ -80,6 +81,38 @@ SUPPORTED_PRIORITIES = {"high", "normal", "low"} PRIORITY_ALIASES = { "medium": "normal", } def extract_worker_label(worker_name: str) -> str: host_part = worker_name.split("@", 1)[1] if "@" in worker_name else worker_name return host_part.rsplit("-", 1)[0] if "-" in host_part else host_part def load_worker_inventory_hosts() -> Dict[str, str]: env_name = os.getenv("ENV", "").strip() if not env_name: return {} inventory_path = Path(f"deploy_configs/{env_name}/inventory.ini") if not inventory_path.exists(): return {} hosts: Dict[str, str] = {} in_worker_section = False for raw_line in inventory_path.read_text(encoding="utf-8").splitlines(): line = raw_line.strip() if not line or line.startswith("#"): continue if line.startswith("[") and line.endswith("]"): in_worker_section = line.lower() == "[worker]" continue if not in_worker_section: continue parts = line.split() if not parts: continue inventory_name = parts[0] for token in parts[1:]: if token.startswith("ansible_host="): hosts[inventory_name] = token.split("=", 1)[1] break return hosts PRIORITY_DAG_SUFFIXES = { "high": "__high", "normal": "", Loading Loading @@ -697,6 +730,7 @@ def get_flower_workers(db: Session = Depends(get_db), current_user: User = Depen try: FLOWER_AUTH_USER = os.getenv("_AIRFLOW_WWW_USER_USERNAME", AIRFLOW_USER) FLOWER_AUTH_PASS = os.getenv("_AIRFLOW_WWW_USER_PASSWORD", AIRFLOW_PASS) inventory_worker_hosts = load_worker_inventory_hosts() response = requests.get( f"{FLOWER_URL}/api/workers", Loading @@ -722,6 +756,12 @@ def get_flower_workers(db: Session = Depends(get_db), current_user: User = Depen # Merge occupied slots with flower response for worker_name, info in workers_data.items(): worker_label = extract_worker_label(worker_name) worker_ip = inventory_worker_hosts.get(worker_label, "") if not worker_ip and worker_label.count(".") == 3: worker_ip = worker_label info["worker_label"] = worker_label info["worker_ip"] = worker_ip if info.get('status') is False: info['occupied_slots'] = 0 continue Loading
docker-celery-3.0.5/frontend_vue/src/components/WorkersView.vue +26 −20 Original line number Diff line number Diff line Loading @@ -22,6 +22,9 @@ <el-icon class="text-gray-400 text-xl"><Platform /></el-icon> <div class="flex flex-col"> <span class="font-medium text-gray-800">{{ scope.row.name }}</span> <span v-if="scope.row.raw_name && scope.row.raw_name !== scope.row.name" class="text-xs text-gray-400 font-mono"> {{ scope.row.raw_name }} </span> </div> </div> </template> Loading Loading @@ -89,6 +92,24 @@ const workers = ref([]) const loading = ref(false) let timer = null const parseWorkerLabel = (workerName) => { const parts = workerName.split('@') if (parts.length <= 1) return workerName const hostPart = parts[1] const lastHyphen = hostPart.lastIndexOf('-') return lastHyphen > 0 ? hostPart.slice(0, lastHyphen) : hostPart } const parseWorkerIp = (workerName, workerLabel) => { const exactIpMatch = String(workerLabel || '').match(/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/) if (exactIpMatch) return exactIpMatch[1] const parts = workerName.split('@') if (parts.length <= 1) return '' const hostPart = parts[1] const ipMatch = hostPart.match(/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(?:-|$)/) return ipMatch ? ipMatch[1] : '' } const fetchWorkers = async () => { try { loading.value = true Loading @@ -97,6 +118,8 @@ const fetchWorkers = async () => { workers.value = Object.keys(data).map(workerName => { const workerInfo = data[workerName] const workerLabel = workerInfo.worker_label || parseWorkerLabel(workerName) const workerIp = workerInfo.worker_ip || parseWorkerIp(workerName, workerLabel) const isOnline = workerInfo && workerInfo.status !== false // Flower omit status when online or set false when offline let activeTasks = 0 let processedTasks = 0 Loading @@ -113,27 +136,10 @@ const fetchWorkers = async () => { occupiedSlots = workerInfo.occupied_slots || 0 } // Parse IP from workerName. Expected format: celery@<IP>-<HOSTNAME> let ip = '' const parts = workerName.split('@') if (parts.length > 1) { const hostPart = parts[1] // Extract IP if it matches IP pattern before a hyphen const ipMatch = hostPart.match(/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})-/) if (ipMatch) { ip = ipMatch[1] } else { // If no hyphen, the whole part might be the IP const exactIpMatch = hostPart.match(/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/) if (exactIpMatch) { ip = exactIpMatch[1] } } } return { name: workerName, ip: ip, name: workerLabel || workerName, raw_name: workerName, ip: workerIp, status: isOnline, active: activeTasks, processed: processedTasks, Loading