myxce/src/frontend/schedulerclient/statusmap.go
Lei Chen e4af0cac73 Phase 0: scheduleragent + mcp-server read-only agent framework
Complete the natural-language scheduler agent's Phase 0 read-only pipeline:

Task #2 (API Key auth mechanism):
- New store/apikey.go: APIKey store (hash, validate, expiry)
- controllers/apikey.go: GenerateAPIKey finally persists + registers RBAC role
- controllers/auth.go: GetUsernameFromToken recognizes xce_ak_ prefix
- middleware/validatehandler.go: isValidToken handles API Key
- config/rbac.yaml: mcp_readonly role (view-only permissions)
- 6 new test files covering unit + integration full-chain

Task #3 (schedulerclient Go library):
- types.go: URM types (URMJob/URMQueue/URMNode/JobStatus/Metrics)
- client.go: read-only Client interface (6 methods)
- httpclient.go: resty-based HTTP implementation
- statusmap.go + _test.go: TDD status mapping (33 tests)

Task #4 (mcp-server binary):
- cmd/main.go: entry, reads XCE_API_URL/XCE_API_KEY, registers 6 MCP tools
- internal/app.go: tool handlers wiring schedulerclient to MCP stdio
- Uses github.com/mark3labs/mcp-go@v0.57.0 for stdio transport

All: go vet clean, go build clean, schedulerclient 33/33 tests pass.
mcp-server binary ~11MB arm64, CGO_ENABLED=0.

Architecture:
  Claude Code --stdio--> mcp-server --HTTP--> apiserver REST API --cgo--> C backend

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-30 11:16:36 +08:00

93 lines
2.7 KiB
Go

package schedulerclient
import "strings"
// ── JobStatus mapping ───────────────────────────────────────────────────────
// JobStatusFromString maps the apiserver's job status string to a URM JobStatus.
//
// The input is the output of apiserver models.JobStateStr() — one of:
//
// PEND, PSUSP, RUN, USUSP, SSUSP, RUNKWN, EXIT, DONE, UNKWN, ERR, TRANSIENT
func JobStatusFromString(s string) JobStatus {
switch s {
case "PEND", "PSUSP":
return JobStatusPending
case "RUN", "RUNKWN":
return JobStatusRunning
case "USUSP", "SSUSP":
return JobStatusSuspended
case "EXIT":
return JobStatusFailed
case "DONE":
return JobStatusSucceeded
default:
// UNKWN, ERR, TRANSIENT, empty, or unknown values → unknown
return JobStatusUnknown
}
}
// ── QueueStatus mapping ─────────────────────────────────────────────────────
// QueueStatusFromString maps the apiserver's composite queue status string
// (e.g. "open:active", "closed:inactive_adm") to a URM status:
//
// active — queue is open and accepting jobs
// inactive — queue is administratively or window-closed
// draining — open but has an inactive-time window active (will close)
// unknown — unrecognised format
func QueueStatusFromString(s string) string {
if s == "" {
return "unknown"
}
// Format is "<open|closed>:<active|inactive_adm|inactive_win>"
parts := strings.SplitN(s, ":", 2)
if len(parts) < 1 {
return "unknown"
}
openClosed := parts[0]
var inactiveKind string
if len(parts) > 1 {
inactiveKind = parts[1]
}
switch openClosed {
case "open":
if inactiveKind == "inactive_adm" || inactiveKind == "inactive_win" {
return "draining"
}
return "active"
case "closed":
return "inactive"
default:
return "unknown"
}
}
// ── NodeStatus mapping ─────────────────────────────────────────────────────
// NodeStatusFromString maps the apiserver's host status string (output of
// models.HostStateStr()) to a URM node status:
//
// idle — OK, accepting work
// busy — running jobs
// down — CLOSED, CLOSED_BY_ADMIN
// drain — LOCKU, LOCKW (not accepting new work, existing jobs finish)
// unknown — UNAVAIL or unrecognised
func NodeStatusFromString(s string) string {
switch s {
case "OK":
return "idle"
case "BUSY":
return "busy"
case "CLOSED", "CLOSED_BY_ADMIN":
return "down"
case "LOCKU", "LOCKW":
return "drain"
default:
// UNAVAIL, empty, or unknown → unknown
return "unknown"
}
}