resources: XCE_RESF_BUILTIN 从未被置位 — resources.yaml 加 builtin: true 声明式键
XCE_RESF_BUILTIN (0x01) 在生产链路上从未被任何一层置位:全树只有 types.h 定义、scheduler repl_info.c 显示表、一个测试 fixture 三处引用。worker init_builtin_load_indices()(mem/io/r1m…)、controller add_hostattrs_resvals()/ set_host_slots() 硬编码生产这些资源的值,但定义的 flags 从 YAML→capnp→wire 全程没人打这个位——CLI 的 BUILTIN 列对 mem/io/slots 等恒显 false,与 extlic 等用户资源无法区分。 - resources.schema.json(+xceadmin 打包副本,构建时从 src/configs/schema 同步):加 builtin: boolean 键 - mapping.py:RESOURCE_FLAG_BUILTIN=0x01,_build_resource 映射 builtin:true (与 consumable/release 同模式:缺省/false 不置位) - build.py dump:补渲染 builtin=True;顺手补同样一直缺失的 external=True (此前 EXTERNAL 池 dump 只显示 shared=true) - src/configs/resources.yaml:20 个硬编码生产者资源(slots + 11 load index + 8 host attributes)声明 builtin: true - golden fixture full/expected.bin 相应再生(6696→6856B,diff 恰为 20 行 builtin=True,非 builtin 行 0 差异) 验证:xceadmin 389 测试全绿(dev venv + meson build.dbg 各一遍);t1 实证: 树内 venv 重建 cluster.bin(与 live 逐行比对确认 delta 仅 builtin 20 行)→ config push gen=17 → apiserver 重启拾取(定义 store 只吃 join 时 config push,激活只 re-push scheduler)→ xcecli definition list -l mem/io/slots BUILTIN=true,extlic false、distribution/extlic 值/liccheck 作业 RUN→DONE 全链正常,5 daemon 健康。 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
parent
805288d747
commit
857f987f3c
8 changed files with 122 additions and 9 deletions
|
|
@ -5,6 +5,7 @@ resources:
|
|||
type: numeric
|
||||
order: decrease
|
||||
consumable: true
|
||||
builtin: true
|
||||
description: "job slot count (built-in)"
|
||||
|
||||
# built-in: dynamic memory load index reported by workers
|
||||
|
|
@ -12,6 +13,7 @@ resources:
|
|||
type: numeric
|
||||
interval: 60
|
||||
consumable: true
|
||||
builtin: true
|
||||
order: decrease
|
||||
description: "available memory in bytes (built-in dynamic load index; CLI display converts to a human-readable unit)"
|
||||
|
||||
|
|
@ -21,6 +23,7 @@ resources:
|
|||
type: numeric
|
||||
interval: 60
|
||||
consumable: false
|
||||
builtin: true
|
||||
order: increase
|
||||
description: "1-minute load average (built-in dynamic load index)"
|
||||
|
||||
|
|
@ -28,6 +31,7 @@ resources:
|
|||
type: numeric
|
||||
interval: 60
|
||||
consumable: false
|
||||
builtin: true
|
||||
order: increase
|
||||
description: "15-minute load average (built-in dynamic load index)"
|
||||
|
||||
|
|
@ -35,6 +39,7 @@ resources:
|
|||
type: numeric
|
||||
interval: 60
|
||||
consumable: false
|
||||
builtin: true
|
||||
order: increase
|
||||
description: "15-second load average (built-in dynamic load index)"
|
||||
|
||||
|
|
@ -42,6 +47,7 @@ resources:
|
|||
type: numeric
|
||||
interval: 60
|
||||
consumable: false
|
||||
builtin: true
|
||||
order: increase
|
||||
description: "cpu utilization"
|
||||
|
||||
|
|
@ -49,6 +55,7 @@ resources:
|
|||
type: numeric
|
||||
interval: 60
|
||||
consumable: false
|
||||
builtin: true
|
||||
order: increase
|
||||
description: "page ratio"
|
||||
|
||||
|
|
@ -56,6 +63,7 @@ resources:
|
|||
type: numeric
|
||||
interval: 60
|
||||
consumable: false
|
||||
builtin: true
|
||||
order: increase
|
||||
description: "login count"
|
||||
|
||||
|
|
@ -63,13 +71,15 @@ resources:
|
|||
type: numeric
|
||||
interval: 60
|
||||
consumable: false
|
||||
builtin: true
|
||||
order: increase
|
||||
description: "idle time"
|
||||
|
||||
|
||||
- name: swp
|
||||
type: numeric
|
||||
interval: 60
|
||||
consumable: true
|
||||
builtin: true
|
||||
order: decrease
|
||||
description: "swap size"
|
||||
|
||||
|
|
@ -77,13 +87,15 @@ resources:
|
|||
type: numeric
|
||||
interval: 60
|
||||
consumable: false
|
||||
builtin: true
|
||||
order: increase
|
||||
description: "io rate"
|
||||
|
||||
|
||||
- name: tmp
|
||||
type: numeric
|
||||
interval: 60
|
||||
consumable: true
|
||||
builtin: true
|
||||
order: decrease
|
||||
description: "available tmp disk space in bytes (built-in dynamic load index; CLI display converts to a human-readable unit)"
|
||||
|
||||
|
|
@ -91,34 +103,42 @@ resources:
|
|||
# controller/worker.c add_hostattrs_resvals()
|
||||
- name: type
|
||||
type: string
|
||||
builtin: true
|
||||
description: "host OS/arch type (built-in host attribute)"
|
||||
|
||||
- name: model
|
||||
type: string
|
||||
builtin: true
|
||||
description: "host hardware model (built-in host attribute)"
|
||||
|
||||
- name: cpuf
|
||||
type: numeric
|
||||
builtin: true
|
||||
description: "CPU speed factor (built-in host attribute)"
|
||||
|
||||
- name: maxcpus
|
||||
type: numeric
|
||||
builtin: true
|
||||
description: "max CPU count (built-in host attribute)"
|
||||
|
||||
- name: maxmem
|
||||
type: numeric
|
||||
builtin: true
|
||||
description: "max memory in bytes (built-in host attribute; CLI display converts to a human-readable unit)"
|
||||
|
||||
- name: maxswap
|
||||
type: numeric
|
||||
builtin: true
|
||||
description: "max swap in bytes (built-in host attribute; CLI display converts to a human-readable unit)"
|
||||
|
||||
- name: maxtmp
|
||||
type: numeric
|
||||
builtin: true
|
||||
description: "max tmp disk space in bytes (built-in host attribute; CLI display converts to a human-readable unit)"
|
||||
|
||||
- name: ndisks
|
||||
type: numeric
|
||||
builtin: true
|
||||
description: "disk count (built-in host attribute)"
|
||||
|
||||
# dynamic hostbased resource, external load index
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
"order": { "type": "string", "enum": ["increase", "decrease", "na"] },
|
||||
"consumable": { "type": "boolean" },
|
||||
"release": { "type": "boolean" },
|
||||
"builtin": { "type": "boolean" },
|
||||
"description": { "type": "string" },
|
||||
"resourcemap": {
|
||||
"type": "array",
|
||||
|
|
|
|||
|
|
@ -208,7 +208,9 @@ def _format_resource(r) -> str:
|
|||
ordtype = _RESOURCE_ORDTYPE_NAMES.get(r.ordtype, r.ordtype)
|
||||
consumable = bool(r.flags & mapping.RESOURCE_FLAG_CONSUMABLE)
|
||||
release = bool(r.flags & mapping.RESOURCE_FLAG_RELEASE)
|
||||
builtin = bool(r.flags & mapping.RESOURCE_FLAG_BUILTIN)
|
||||
shared = bool(r.flags & mapping.RESOURCE_FLAG_SHARED)
|
||||
external = bool(r.flags & mapping.RESOURCE_FLAG_EXTERNAL)
|
||||
parts = [f"name={r.name}", f"type={valtype}", f"order={ordtype}"]
|
||||
if r.desc:
|
||||
parts.append(f"desc={r.desc!r}")
|
||||
|
|
@ -216,6 +218,13 @@ def _format_resource(r) -> str:
|
|||
parts.append(f"interval={r.interval}")
|
||||
parts.append(f"consumable={consumable}")
|
||||
parts.append(f"release={release}")
|
||||
# builtin/external change the resource's semantics (whose values count,
|
||||
# where they come from) -- always visible when set, never shown bare
|
||||
# false: absent means the plain user-resource default.
|
||||
if builtin:
|
||||
parts.append("builtin=True")
|
||||
if external:
|
||||
parts.append("external=True")
|
||||
if shared:
|
||||
parts.append("shared=true")
|
||||
if len(r.distributions) > 0:
|
||||
|
|
|
|||
|
|
@ -64,12 +64,12 @@ reason -- never a silent drop.
|
|||
|
||||
``Resource`` (``resources.yaml``)
|
||||
``valtype``/``ordtype``/``flags`` ARE mapped from YAML
|
||||
``type``/``order``/``consumable``/``release`` -- the encoding is
|
||||
documented, just not in ``controller.capnp``/design.md: it lives in
|
||||
``type``/``order``/``consumable``/``release``/``builtin`` -- the encoding
|
||||
is documented, just not in ``controller.capnp``/design.md: it lives in
|
||||
``include/xce/types.h`` (``XCE_VAL_*``, ``xce_order_t``,
|
||||
``XCE_RESF_CONSUMABLE``/``XCE_RESF_RELEASE``) and is actively read at
|
||||
runtime (e.g. ``scheduler.c`` copies these three fields straight out
|
||||
of the parsed resource). See :data:`RESOURCE_VALTYPE`,
|
||||
``XCE_RESF_CONSUMABLE``/``XCE_RESF_RELEASE``/``XCE_RESF_BUILTIN``) and
|
||||
is actively read at runtime (e.g. ``scheduler.c`` copies these three
|
||||
fields straight out of the parsed resource). See :data:`RESOURCE_VALTYPE`,
|
||||
:data:`RESOURCE_ORDTYPE`, and the flag constants below for the exact
|
||||
values (also reused by ``xceadmin.build.dump_cluster_config`` to render
|
||||
these int-encoded fields back to their YAML names). ``idx`` and
|
||||
|
|
@ -205,10 +205,16 @@ _XCE_SCALAR_FIELDS: tuple[tuple[str, str], ...] = (
|
|||
RESOURCE_VALTYPE: dict[str, int] = {"string": 0, "numeric": 1, "boolean": 2}
|
||||
RESOURCE_ORDTYPE: dict[str, int] = {"increase": 0, "decrease": 1, "na": 2}
|
||||
|
||||
# resources.yaml ``consumable``/``release`` -> Resource.flags bits, mirroring
|
||||
# include/xce/types.h's XCE_RESF_CONSUMABLE/XCE_RESF_RELEASE.
|
||||
# resources.yaml ``consumable``/``release``/``builtin`` -> Resource.flags
|
||||
# bits, mirroring include/xce/types.h's XCE_RESF_CONSUMABLE/
|
||||
# XCE_RESF_RELEASE/XCE_RESF_BUILTIN.
|
||||
RESOURCE_FLAG_CONSUMABLE = 0x80
|
||||
RESOURCE_FLAG_RELEASE = 0x20
|
||||
# XCE_RESF_BUILTIN: the resource's values are produced by hardcoded worker/
|
||||
# controller reporters (resource_sync.c's load indices, worker.c's host
|
||||
# attributes and slots) -- display/accounting code uses it to tell those
|
||||
# from user-declared resources of the same name space.
|
||||
RESOURCE_FLAG_BUILTIN = 0x01
|
||||
|
||||
# Set together on a resourcemap item whose location is exactly ["all"]
|
||||
# (cluster-wide static shared pool): XCE_RESF_SHARED | XCE_RESF_GLOBAL in
|
||||
|
|
@ -421,6 +427,8 @@ def _build_resource(item: dict, target: "capnp._DynamicStructBuilder") -> None:
|
|||
flags |= RESOURCE_FLAG_CONSUMABLE
|
||||
if item.get("release"):
|
||||
flags |= RESOURCE_FLAG_RELEASE
|
||||
if item.get("builtin"):
|
||||
flags |= RESOURCE_FLAG_BUILTIN
|
||||
resourcemap = item.get("resourcemap") or []
|
||||
if resourcemap:
|
||||
distributions = target.init("distributions", len(resourcemap))
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ resources:
|
|||
type: numeric
|
||||
interval: 60
|
||||
consumable: false
|
||||
builtin: true
|
||||
order: decrease
|
||||
description: "available memory in MB (built-in dynamic load index)"
|
||||
|
||||
|
|
@ -197,3 +198,16 @@ resources:
|
|||
- name: gstring
|
||||
description: string resource
|
||||
type: string
|
||||
|
||||
# external-license-resources: cluster-wide license pool reported by
|
||||
# the extres daemon (xceesd) -- config declares only the name/shape,
|
||||
# the free counts come from the daemon's pushes
|
||||
- name: extlic
|
||||
description: externally reported license pool
|
||||
type: numeric
|
||||
interval: 0
|
||||
consumable: true
|
||||
order: decrease
|
||||
resourcemap:
|
||||
- location:
|
||||
- external
|
||||
|
|
|
|||
BIN
src/xceadmin/tests/fixtures/full/expected.bin
vendored
BIN
src/xceadmin/tests/fixtures/full/expected.bin
vendored
Binary file not shown.
|
|
@ -49,6 +49,32 @@ def test_dump_lists_each_resource_with_decoded_type_and_order(full_cluster_bin):
|
|||
assert "consumable=False" in text
|
||||
|
||||
|
||||
def test_dump_renders_builtin_and_external_flags(full_cluster_bin):
|
||||
"""The flag bits that change a resource's semantics must be visible in a
|
||||
dump: builtin (worker/controller hardcode the values -- the repo fixture's
|
||||
load indices and host attributes) and external (values come only from the
|
||||
extres daemon's pushes -- the extlic pool shape). Both were silently
|
||||
invisible before: every builtin resource dumped as a plain user resource,
|
||||
and an external pool dumped as bare shared=true.
|
||||
"""
|
||||
text = build.dump_cluster_config(str(full_cluster_bin))
|
||||
mem_line = next(l for l in text.splitlines() if "name=mem type=numeric" in l)
|
||||
assert "builtin=True" in mem_line
|
||||
# a user-declared resource must not show a bare "builtin=" at all
|
||||
assert "builtin=False" not in text
|
||||
|
||||
import yaml as _yaml
|
||||
configs = str(FIXTURES_DIR / "full" / "configs")
|
||||
with open(configs + "/resources.yaml", encoding="utf-8") as fh:
|
||||
names = [r["name"] for r in _yaml.safe_load(fh)["resources"]
|
||||
if (r.get("resourcemap") or []) and
|
||||
(r["resourcemap"][0].get("location") or []) == ["external"]]
|
||||
assert names, "fixture must contain an external resource for this test"
|
||||
for name in names:
|
||||
line = next(l for l in text.splitlines() if f"name={name} " in l)
|
||||
assert "external=True" in line, line
|
||||
|
||||
|
||||
def test_dump_renders_mxj_auto_detect_sentinel_not_raw_uint32(full_cluster_bin):
|
||||
"""The exact bug that motivated this feature: hosts.yaml's `default`
|
||||
host has `max_slot: -1`, which wire-encodes as HOSTCONFIG_MXJ_AUTO_DETECT
|
||||
|
|
|
|||
|
|
@ -176,6 +176,29 @@ def test_map_list_section_resources_populates_mapped_fields_only(cluster_config)
|
|||
assert r0.lastReport == 0
|
||||
|
||||
|
||||
def test_map_list_section_resources_builtin_flag(cluster_config):
|
||||
"""``builtin: true`` (resources.yaml) -> XCE_RESF_BUILTIN (0x01) OR'd
|
||||
on top of the other bits: the load indices (mem/io/r1m...) and host
|
||||
attributes (type/model/maxmem...) are produced by hardcoded worker/
|
||||
controller reporters, and the flag tells them apart from user-declared
|
||||
resources (the CLI's BUILTIN column renders it). Absent/false must
|
||||
stay 0 -- the same convention as consumable/release.
|
||||
"""
|
||||
items = [
|
||||
{"name": "mem", "type": "numeric", "interval": 60,
|
||||
"consumable": True, "builtin": True, "description": "memory"},
|
||||
{"name": "mylic", "type": "numeric", "consumable": True,
|
||||
"builtin": False, "description": "user pool"},
|
||||
{"name": "plain", "type": "numeric"}, # builtin absent
|
||||
]
|
||||
mapping.map_list_section(_spec_for("resources"), items, cluster_config)
|
||||
|
||||
r0, r1, r2 = cluster_config.resources
|
||||
assert r0.flags == 0x80 | 0x01 # CONSUMABLE | BUILTIN
|
||||
assert r1.flags == 0x80 # explicit false -> no BUILTIN bit
|
||||
assert r2.flags == 0 # absent -> no BUILTIN bit
|
||||
|
||||
|
||||
def test_map_list_section_noop_on_empty_or_missing_items(cluster_config):
|
||||
mapping.map_list_section(_spec_for("resources"), [], cluster_config)
|
||||
mapping.map_list_section(_spec_for("resources"), None, cluster_config)
|
||||
|
|
@ -307,6 +330,18 @@ def test_map_list_section_resourcemap_real_repo_configs(cluster_config):
|
|||
for name in ("staticres1", "staticres2", "staticres3", "host_quota"):
|
||||
assert by_name[name].flags & (0x08 | 0x04) == 0
|
||||
|
||||
# builtin:true resources (the worker load indices + host attributes the
|
||||
# repo config declares) carry XCE_RESF_BUILTIN; user-declared shapes
|
||||
# (staticres*, extlic) do not.
|
||||
for name in ("slots", "mem", "r1m", "r15m", "r15s", "ut", "pg", "ls", "it",
|
||||
"swp", "io", "tmp", "type", "model", "cpuf", "maxcpus",
|
||||
"maxmem", "maxswap", "maxtmp", "ndisks"):
|
||||
assert by_name[name].flags & 0x01 == 0x01, f"{name} missing BUILTIN"
|
||||
for name in ("dynres1", "dynres2", "staticres1", "staticres2", "staticres3",
|
||||
"staticres4", "res1", "sres1", "bigmem", "strres1", "strres2",
|
||||
"host_quota", "gstring", "extlic"):
|
||||
assert by_name[name].flags & 0x01 == 0, f"{name} unexpectedly BUILTIN"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# map_list_section: users.yaml/hosts.yaml -> ugroups/hgroups (named groups)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue