docs(admin-config-tool): commit spec docs and example configs

design.md, research.md (new), brief.md and requirements.md (updated
during spec authoring: Req 1.6 resolved/superseded by bundling, Req
5.5 added, Req 4.3/4.4 extended for limits.yaml) had been sitting
uncommitted since before implementation began -- the entire 18-task
implementation was built against these documents without them ever
landing in git.

src/configs/*.yaml + schema/*.schema.json: the example config
directory and bundled JSON schemas most of the test suite reads as
"real repo fixtures" (test_config_build.py, test_mapping.py,
test_validation.py, etc.) were likewise never committed -- those
tests would silently skip (skipif guards) rather than run for anyone
else checking out this branch.
This commit is contained in:
Rongsong Shen 2026-07-22 10:11:08 +00:00
parent 67cc361191
commit a14785108c
18 changed files with 1574 additions and 53 deletions

39
src/configs/hosts.yaml Normal file
View file

@ -0,0 +1,39 @@
---
hosts:
- hostname: default
type: "x86_64-linux-gnu"
model: "x86_64-linux-gnu"
max_slot: -1
description: "default host"
resource:
- name: mg
- name: maxswap
value: 10000
sched_stop:
- resname: r1m
sched: 3.5
stop: 4.5
- resname: r15s
sched: 4.0
stop: 10.0
dispatch_window:
- start_time: "9:00"
end_time: "18:00"
- start_time: "21:00"
end_time: "7:00"
- hostname: worker2
model: "!"
type: "!"
max_slot: 256
resource:
- name: gstring
value: "This is a G string"
hostgroups:
- groupname: hg1
members:
- all
exclude_members: [h3]

49
src/configs/limits.yaml Normal file
View file

@ -0,0 +1,49 @@
---
limits:
# cap total slots for ug1 across the whole cluster
- name: ug1_slots
users:
members:
- ug1
resname: slots
value: 10
# per-user slot cap inside ug2 (each member gets up to 4, not a shared pool)
- name: ug2_slots_per_user
users:
members:
- ug2
exclude_members:
- user3
resname: slots
value: 4
per_user: true
# queue-scoped memory cap, percentage of total mem resource
- name: night_mem_pct
queues:
members:
- night
resname: mem
value: 50
percent: true
# host-scoped cap, applied per host in hg1
- name: hg1_slots_per_host
hosts:
members:
- hg1
resname: slots
value: 20
per_host: true
# combined scope: ug1 members on the normal queue
- name: ug1_normal_slots
users:
members:
- ug1
queues:
members:
- normal
resname: slots
value: 8

94
src/configs/queues.yaml Normal file
View file

@ -0,0 +1,94 @@
---
queues:
- name: xce
description: "xce queue"
priority: 30
fairshare:
- user: default
share: 100
hosts:
members:
- all
- name: normal
description: "normal queue"
priority: 40
nice: 10
fairshare:
- user: default
share: 100
hosts:
members:
- all
- name: night
description: "night queue"
priority: 30
nice: 10
res_req: "mem>1000 rusage[mem=1000]"
gpu_req: "num=1:mode=shared:gvendor=NVIDIA:gmodel=A100"
fairshare:
- user: ug1
share: 1000
- user: others
share: 100
cpulimit: 100
memlimit: 1000
runlimit: 20
runtimeest: 30
dispatch_window:
- start_time: "19:00"
end_time: "8:30"
run_window:
- start_time: "5:19:00"
end_time: "1:8:30"
- start_time: "20:00"
end_time: "8:30"
sched_stop:
- resname: tmp
sched: 2000
stop: 1000
- resname: res1
sched: 1.0
stop: 2.5
reservation: 100
preemption: preemptive
backfill: true
exclusive: false
rerunable: true
pre_exec: "/path/to/preexec.sh"
post_exec: "/path/to/postexec.sh"
job_starter: "/path/to/jobstarter.sh"
users:
members:
- ug1
- ug2
exclude_members:
- user3
hosts:
members:
- hgroup1
- hgroup2
exclude_members:
- host3
- name: short
description: "for short jobs"
priority: 45
cpulimit: 15
fairshare:
- user: ug1
share: 1000
- user: default
share: 100
reservation: 10
preemption: preemptable
backfill: true
users:
members:
- ugroup1
hosts:
members:
- hgroup1

199
src/configs/resources.yaml Normal file
View file

@ -0,0 +1,199 @@
---
resources:
# built-in: job slot count (consumed by running jobs, decreases as slots fill)
- name: slots
type: numeric
order: decrease
consumable: true
description: "job slot count (built-in)"
# built-in: dynamic memory load index reported by workers
- name: mem
type: numeric
interval: 60
consumable: false
order: decrease
description: "available memory in MB (built-in dynamic load index)"
# built-in host attributes, registered per-worker by
# controller/worker.c add_hostattrs_resvals()
- name: type
type: string
description: "host OS/arch type (built-in host attribute)"
- name: model
type: string
description: "host hardware model (built-in host attribute)"
- name: cpuf
type: numeric
description: "CPU speed factor (built-in host attribute)"
- name: maxcpus
type: numeric
description: "max CPU count (built-in host attribute)"
- name: maxmem
type: numeric
description: "max memory in MB (built-in host attribute)"
- name: maxswap
type: numeric
description: "max swap in MB (built-in host attribute)"
- name: maxtmp
type: numeric
description: "max tmp disk space in MB (built-in host attribute)"
- name: ndisks
type: numeric
description: "disk count (built-in host attribute)"
# dynamic hostbased resource, external load index
- name: dynres1
type: numeric
interval: 60
order: decrease
consumable: true
release: true
description: "user defined dynamic resource"
resourcemap:
- location:
- default
# dynamic share resource, 2 instances
- name: dynres2
type: numeric
interval: 60
consumable: false
order: decrease
description: "user defined dynamic resource"
resourcemap:
- location:
- host1
- host2
- location:
- host5
- host6
# static share resource, all host share 100 res.
- name: staticres4
type: numeric
interval: 0
consumable: true
order: decrease
resourcemap:
- location:
- all
value: 100
# static share resource, host1 and host2 share the 100 res.
- name: staticres1
type: numeric
interval: 0
consumable: true
order: decrease
resourcemap:
- value: 100
location:
- host1
- host2
# static share resource, each of host1 and host2 shares the 100 res.
- name: staticres2
type: numeric
interval: 0
consumable: false
order: decrease
resourcemap:
- value: 100
location:
- host1
- value: 100
location:
- host2
# static hostbased resource.
- name: staticres3
type: numeric
interval: 0
consumable: true
order: decrease
resourcemap:
- value: 100
location:
- default
- name: res1
type: numeric
interval: 0
consumable: true
order: decrease
resourcemap:
- location:
- all
value: 100
- name: sres1
type: numeric
interval: 0
consumable: true
order: decrease
# boolean resource
- name: bigmem
type: boolean
order: na
resourcemap:
- value: true
location:
- host1
- value: true
location:
- host2
- value: true
location:
- host3
# dynamic string resource
- name: strres1
type: string
interval: 60
consumable: false
order: na
description: "string resources"
resourcemap:
- location:
- default
# static string resource
- name: strres2
type: string
description: "string resources"
resourcemap:
- value: v1
location:
- host1
- value: v2
location:
- host2
- value: v3
location:
- host3
- name: host_quota
description: quota on each host
type: numeric
order: decrease
consumable: true
interval: 0
resourcemap:
- value: 10
location:
- default
- name: gstring
description: string resource
type: string

13
src/configs/scaler.yaml Normal file
View file

@ -0,0 +1,13 @@
---
scalers:
# script-based cloud autoscaler provider
- name: aws-asg
type: script
conf_path: /etc/xce/scalers/aws-asg.conf
script_path: /opt/xce/scalers/aws_asg.sh
script_option: "--region us-east-1"
# minimal provider, no conf file or options
- name: k8s-pool
type: script
script_path: /opt/xce/scalers/k8s_pool.sh

View file

@ -0,0 +1,101 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/schemas/hosts.schema.json",
"title": "Hosts and Hostgroups",
"type": "object",
"additionalProperties": false,
"properties": {
"hosts": {
"type": "array",
"items": { "$ref": "#/$defs/Host" }
},
"hostgroups": {
"type": "array",
"items": { "$ref": "#/$defs/HostGroup" }
}
},
"required": ["hosts"],
"$defs": {
"Host": {
"type": "object",
"additionalProperties": false,
"properties": {
"hostname": { "type": "string", "pattern": "^[A-Za-z0-9._-]+$" },
"type": { "type": "string" },
"model": { "type": "string" },
"max_slot": { "type": "integer", "minimum": -1, "description": "-1 means auto-detect" },
"dispatch_window": { "type": "array", "items": { "$ref": "#/$defs/TimeWindow" } },
"sched_stop": { "type": "array", "items": { "$ref": "#/$defs/SchedStop" } },
"resource": { "type": "array", "items": { "$ref": "#/$defs/HostResource" } },
"description": { "type": "string", "maxLength": 4096 }
},
"required": ["hostname"]
},
"HostResource": {
"type": "object",
"additionalProperties": false,
"properties": {
"name": { "type": "string", "pattern": "^[A-Za-z0-9._-]+$" },
"value": {
"description": "Resource value (string or number)",
"oneOf": [
{ "type": "number" },
{ "type": "string" }
]
}
},
"required": ["name"]
},
"TimeWindow": {
"type": "object",
"additionalProperties": false,
"properties": {
"start_time": {
"type": "string",
"description": "[day:]hour[:minute]; day 0-6, hour 0-23, minute 0-59",
"pattern": "^((([0-6]):)?([01]?\\d|2[0-3])(:[0-5]?\\d)?)$"
},
"end_time": {
"type": "string",
"description": "[day:]hour[:minute]; day 0-6, hour 0-23, minute 0-59",
"pattern": "^((([0-6]):)?([01]?\\d|2[0-3])(:[0-5]?\\d)?)$"
}
},
"required": ["start_time", "end_time"]
},
"SchedStop": {
"type": "object",
"additionalProperties": false,
"properties": {
"resname": { "type": "string" },
"sched": { "type": "number" },
"stop": { "type": "number" }
},
"required": ["resname"],
"anyOf": [
{ "required": ["sched"] },
{ "required": ["stop"] }
]
},
"HostGroup": {
"type": "object",
"additionalProperties": false,
"properties": {
"groupname": { "type": "string", "pattern": "^[A-Za-z0-9._-]+$" },
"members": {
"type": "array",
"minItems": 1,
"items": {
"type": "string",
"pattern": "^(?:all|!|[A-Za-z0-9._*\\[\\]-]+)$"
}
},
"exclude_members": {
"type": "array",
"items": { "type": "string", "pattern": "^(?:all|!|[A-Za-z0-9._*\\[\\]-]+)$" }
}
},
"required": ["groupname", "members"]
}
}
}

View file

@ -0,0 +1,54 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/schemas/limits.schema.json",
"title": "Limits",
"type": "object",
"additionalProperties": false,
"properties": {
"limits": {
"type": "array",
"items": { "$ref": "#/$defs/Limit" }
}
},
"required": ["limits"],
"$defs": {
"Membership": {
"type": "object",
"additionalProperties": false,
"properties": {
"members": {
"type": "array",
"minItems": 1,
"items": {
"type": "string",
"pattern": "^(?:all|[A-Za-z0-9._*\\[\\]-]+)$"
}
},
"exclude_members": {
"type": "array",
"items": { "type": "string", "pattern": "^[A-Za-z0-9._*\\[\\]-]+$" }
}
},
"required": ["members"]
},
"Limit": {
"type": "object",
"additionalProperties": false,
"properties": {
"name": { "type": "string", "pattern": "^[A-Za-z0-9._-]+$" },
"users": { "$ref": "#/$defs/Membership" },
"queues": { "$ref": "#/$defs/Membership" },
"projects": { "$ref": "#/$defs/Membership" },
"hosts": { "$ref": "#/$defs/Membership" },
"resname": { "type": "string" },
"value": { "type": "number" },
"percent": { "type": "boolean" },
"per_user": { "type": "boolean" },
"per_queue": { "type": "boolean" },
"per_project": { "type": "boolean" },
"per_host": { "type": "boolean" }
},
"required": ["name", "resname", "value"]
}
}
}

View file

@ -0,0 +1,120 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/schemas/queues.schema.json",
"title": "Queues",
"type": "object",
"additionalProperties": false,
"properties": {
"queues": {
"type": "array",
"items": { "$ref": "#/$defs/Queue" }
}
},
"required": ["queues"],
"$defs": {
"TimeWindow": {
"type": "object",
"additionalProperties": false,
"properties": {
"start_time": {
"type": "string",
"description": "[day:]HH[:MM] (day 0-6 optional, minute optional)",
"pattern": "^((([0-6]):)?([01]?\\d|2[0-3])(:[0-5]?\\d)?)$"
},
"end_time": {
"type": "string",
"description": "[day:]HH[:MM] (day 0-6 optional, minute optional)",
"pattern": "^((([0-6]):)?([01]?\\d|2[0-3])(:[0-5]?\\d)?)$"
}
},
"required": ["start_time", "end_time"]
},
"SchedStop": {
"type": "object",
"additionalProperties": false,
"properties": {
"resname": { "type": "string" },
"sched": { "type": "number" },
"stop": { "type": "number" }
},
"required": ["resname"],
"anyOf": [
{ "required": ["sched"] },
{ "required": ["stop"] }
]
},
"FairShare": {
"type": "object",
"additionalProperties": false,
"properties": {
"user": { "type": "string" },
"share": { "type": "integer", "minimum": 0 }
},
"required": ["user", "share"]
},
"Membership": {
"type": "object",
"additionalProperties": false,
"properties": {
"members": {
"type": "array",
"minItems": 1,
"items": {
"type": "string", "pattern": "^(?:all|[A-Za-z0-9._*\\[\\]-]+)$"
}
},
"exclude_members": {
"type": "array",
"items": { "type": "string", "pattern": "^[A-Za-z0-9._*\\[\\]-]+$" }
}
},
"required": ["members"]
},
"Queue": {
"type": "object",
"additionalProperties": false,
"properties": {
"name": { "type": "string", "pattern": "^[A-Za-z0-9._-]+$" },
"description": { "type": "string" },
"priority": { "type": "integer" },
"nice": { "type": "integer" },
"res_req": { "type": "string" },
"gpu_req": { "type": "string" },
"fairshare": {
"type": "array",
"items": { "$ref": "#/$defs/FairShare" }
},
"cpulimit": { "type": "number" },
"memlimit": { "type": "number" },
"runlimit": { "type": "number" },
"runtimeest": { "type": "number" },
"dispatch_window": {
"type": "array",
"items": { "$ref": "#/$defs/TimeWindow" }
},
"run_window": {
"type": "array",
"items": { "$ref": "#/$defs/TimeWindow" }
},
"sched_stop": {
"type": "array",
"items": { "$ref": "#/$defs/SchedStop" }
},
"reservation": { "type": "integer", "minimum": 0 },
"preemption": {
"type": "string",
"enum": ["preemptive", "preemptable"]
},
"backfill": { "type": "boolean" },
"exclusive": { "type": "boolean" },
"rerunable": { "type": "boolean" },
"pre_exec": { "type": "string" },
"post_exec": { "type": "string" },
"job_starter": { "type": "string" },
"users": { "$ref": "#/$defs/Membership" },
"hosts": { "$ref": "#/$defs/Membership" }
},
"required": ["name", "priority"]
}
}
}

View file

@ -0,0 +1,41 @@
{
"type": "object",
"properties": {
"resources": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": { "type": "string" },
"type": { "type": "string", "enum": ["numeric", "boolean", "string"] },
"interval": { "type": "integer" },
"order": { "type": "string", "enum": ["increase", "decrease", "na"] },
"consumable": { "type": "boolean" },
"release": { "type": "boolean" },
"description": { "type": "string" },
"resourcemap": {
"type": "array",
"items": {
"type": "object",
"properties": {
"value": {
"oneOf": [
{ "type": "number" },
{ "type": "string" },
{ "type": "boolean" }
]
},
"location": {
"type": "array",
"items": { "type": "string" }
}
}
}
}
},
"required": ["name", "type"]
}
}
},
"required": ["resources"]
}

View file

@ -0,0 +1,28 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/schemas/scaler.schema.json",
"title": "Scalers",
"type": "object",
"additionalProperties": false,
"properties": {
"scalers": {
"type": "array",
"items": { "$ref": "#/$defs/Scaler" }
}
},
"required": ["scalers"],
"$defs": {
"Scaler": {
"type": "object",
"additionalProperties": false,
"properties": {
"name": { "type": "string", "pattern": "^[A-Za-z0-9._-]+$" },
"type": { "type": "string", "description": "provider type, e.g. \"script\"" },
"conf_path": { "type": "string" },
"script_path": { "type": "string" },
"script_option": { "type": "string" }
},
"required": ["name", "type", "script_path"]
}
}
}

View file

@ -0,0 +1,39 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/schemas/users.schema.json",
"title": "Usergroups",
"type": "object",
"additionalProperties": false,
"properties": {
"usergroups": {
"type": "array",
"items": { "$ref": "#/$defs/UserGroup" }
}
},
"required": ["usergroups"],
"$defs": {
"UserGroup": {
"type": "object",
"additionalProperties": false,
"properties": {
"groupname": {
"type": "string",
"pattern": "^[A-Za-z0-9._-]+$"
},
"members": {
"type": "array",
"minItems": 1,
"items": {
"type": "string",
"pattern": "^(?:all|!|[A-Za-z0-9._*\\[\\]-]+)$"
}
},
"exclude_members": {
"type": "array",
"items": { "type": "string", "pattern": "^(?:all|!|[A-Za-z0-9._*\\[\\]-]+)$" }
}
},
"required": ["groupname", "members"]
}
}
}

View file

@ -0,0 +1,35 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/schemas/xce.schema.json",
"title": "XCE Cluster Settings",
"type": "object",
"additionalProperties": false,
"properties": {
"cluster_name": {
"type": "string",
"pattern": "^[A-Za-z0-9._-]+$",
"description": "cluster identity — must be identical on every controller instance"
},
"heartbeat_interval": {
"type": "integer",
"minimum": 1,
"description": "seconds between controller->worker liveness heartbeats"
},
"inactive_heartbeat_count": {
"type": "integer",
"minimum": 1,
"description": "missed heartbeats before a worker is marked unavailable"
},
"fetch_job_interval": {
"type": "integer",
"minimum": 0,
"description": "seconds between scheduler job-fetch passes"
},
"netio_timeout": {
"type": "integer",
"minimum": 0,
"description": "network I/O timeout in seconds"
}
},
"required": ["cluster_name", "heartbeat_interval", "inactive_heartbeat_count"]
}

8
src/configs/users.yaml Normal file
View file

@ -0,0 +1,8 @@
---
#define usergroup share
usergroups:
- groupname: ug1
members:
- user1
- user2

6
src/configs/xce.yaml Normal file
View file

@ -0,0 +1,6 @@
---
cluster_name: eda1
heartbeat_interval: 30
inactive_heartbeat_count: 3
fetch_job_interval: 10
netio_timeout: 30