Commit 1ccef72d authored by BO ZHANG's avatar BO ZHANG 🏀
Browse files

feat(worker): 在Flower监控中显示worker标签和IP

parent 2cfbb434
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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"
+1 −1
Original line number Diff line number Diff line
@@ -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') }}"

+40 −0
Original line number Diff line number Diff line
@@ -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

@@ -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": "",
@@ -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", 
@@ -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
+26 −20
Original line number Diff line number Diff line
@@ -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>
@@ -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
@@ -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
@@ -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,