netx/skills/biz-state/netx-biz-state/SKILL.md
oliver 1642a0adec Merge duplicate if-intf checks into Config Interface Intent.
Hide standalone IF VRF from the catalog and point ARP enrich at config_interface so one CLI maps to one check item.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-20 09:48:48 +08:00

163 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
name: netx-biz-state
description: >-
Adds or extends NetX biz_state status metrics: TextFSM rules, vendor parsers
(FSM-first prefer_fsm), ParseProfile FieldDef, multi aux_commands, EnrichJoin
cross-command joins, CollectSession cache. Use when adding show/monitor
commands, ARP/VRF-style enrich, cli_templates, or biz_state parsers/profiles.
---
# netx-biz-state(状态监控采集 / 解析)
在 **netx** 仓库为业务状态监控加一项命令或跨命令补字段时,先读本 skill 再改代码。
**原则:所有命令解析先 TextFSM;无行再手写。** 禁止一上来只写 regex 当主路径(除非确认无法写模板)。
---
## 1. 架构速览
```
ParseProfile (命令 + schema + aux + enrich)
→ CollectSession(同批命令缓存)
→ apply_rules → fsm_tables
→ normalize (prefer_fsm)
→ EnrichJoin(等值拷字段)
→ metric rows / compare
```
| 路径 | 作用 |
|------|------|
| `netx_api/cli_templates/` + `index` | TextFSM 规则(stem = rule key) |
| `netx_api/ntc_parse.py` | `apply_rule` / `apply_rules` / `rules_for_command` |
| `netx_api/biz_state/parsers/<vendor>/` | `RULE_KEYS` + `normalize` + `prefer_fsm` |
| `netx_api/biz_state/profiles.py` | `ParseProfile` / `AuxCommand` / `EnrichJoin` / `FieldDef` |
| `netx_api/biz_state/collect_session.py` | 辅命令解析、缓存、`run_primary_with_bundle` |
| `netx_api/biz_state/enrich.py` | 声明式等值 join |
| `netx_api/biz_state/collect_runner.py` | 任务会话采集落库 |
样板:`zte/interface_brief`(单命令)、`zte.config_interface`(可复用辅表)、`zte.arp`(主+辅+enrich)。
---
## 2. 加「单命令」状态指标(常见)
按序做完,缺一不可(对比 UI 需要 FieldDef):
1. **TextFSM**
- 文件:`cli_templates/<vendor>/<platform>_<stem>.textfsm`
- `cli_templates/index` 一行:`path, .*, <platform>, <cmd-abbrev>`
- stem 例:`zte_zxros_show_arp`
2. **Parser** `parsers/<vendor>/<metric>.py`
```python
RULE_KEYS = ("zte_zxros_show_xxx",)
def normalize_xxx(*, raw_text, fsm_tables=None, vendor="", device_type="", command="", params=None, **_):
tables = dict(fsm_tables or {})
if not any(tables.get(k) for k in RULE_KEYS):
# 可选:本地补跑 apply_rules,便于单测直调
...
return prefer_fsm(tables, RULE_KEYS, _map_fsm, _hand, raw_text=raw_text, ...)
normalize_xxx.RULE_KEYS = RULE_KEYS
```
- `_map_fsm`:FSM 行 → 与 FieldDef 同名的 dict
- `_hand`:仅 FSM 无行时兜底
3. **注册** `parsers/<vendor>/__init__.py` → `PARSERS["xxx"] = normalize_xxx`
- 若 `metric_id` 走通用落库,把 id 加入 `collect_runner._GENERIC_METRICS`
4. **Profile** `profiles.py`
```python
ParseProfile(
profile_id="zte.xxx",
vendor_key="zte",
metric_id="xxx",
parser_id="xxx",
command_template="show ...",
match=r"(?i)^\s*show\s+...\s*$",
textfsm_command="show ...",
fields=[FieldDef(..., is_key=True / role="state"|"meta")],
kind="collect",
)
```
5. **单测**
- FSM:`apply_rule(platform=..., rule_key=stem, text=sample)` 非空
- `run_parser("xxx", ...)` 字段正确
- 有 `test/log` 则截取真实片段
---
## 3. 加「跨命令」补字段(多辅命令)
辅命令也按 **§2 完整状态指标** 开发(可单独采集、可复用)。
主 profile 只声明绑定与 join(**不要**在主 `normalize` 里硬编码辅表拼装,除非 join 表达不了):
```python
aux_commands=[
AuxCommand(key="if_intf", profile_id="zte.config_interface"),
# 多辅:再 append;key 唯一
],
enrich_joins=[
EnrichJoin(from_aux="if_intf", on="interface", take=("vrf",)),
# on= 左右同字段;或 left_on= / right_on=
],
```
- `AuxCommand`:**仅** `key` + `profile_id`(命令 / parser / RULE_KEYS 从辅 profile 读)
- `EnrichJoin`:主表 `normalize` 之后,按键等值拷贝 `take` 字段;未命中且 `fill_missing` 则置 `""`
- 同批相同 concrete CLI 由 `CollectSession` 缓存,不重采不重解析
复杂拼装(多键、聚合、推算)才在 `normalize` 里读 `raws` / `aux_records` / `fsm_tables`。
---
## 4. 禁止 / 注意
- 不要把整任务「先采完再解析」当默认;保持 **一项内主+辅连采 → 立刻 normalize**
- 不要自动从 TextFSM 列生成对比 schema(FieldDef 手写)
- 辅命令不要再抄一份 `command_template`/`rule_keys` 到 `AuxCommand`
- `parser_id` 全局扁平:新指标命名避免跨厂家撞名;注册顺序后写覆盖先写
- TextFSM 字面量 `$`:不要用 `^\$`(模板替换冲突);能靠下一 `interface` 切块则不匹配 `$`
- **比对不要写 metric 硬编码过滤**(如 ARP dynamic):一律进 sheet 的 `row_filters` / `field_rules`,见 `compare_rules.py` + 模板 UI
---
## 5. 比对模板(可视化,禁代码定制)
Sheet(`metrics_json[]`)能力:
| 字段 | 作用 |
|------|------|
| `key_fields` / `iface_fields` / `compare_fields` | 对齐 / 端口映射 / 值比对(空=仅存在性) |
| `display_fields` | 结果列(可含不比对的上下文;缺省=key+compare) |
| `row_filters` | 比对前行过滤(AND;支持 `any`/`all`;op: eq/ne/in/empty/regex/age_timer…) |
| `field_rules` | 每字段 `normalize` / `compare`(eq/numeric/percent/ignore) / `tolerance` |
- ARP 动态过滤预设:`arp_dynamic_row_filters()` → ZTE status 默认 sheet 已带
- 引擎:`compare_engine.compare_rows(..., field_rules=)`;服务:`_run_sheet` 先 `apply_row_filters`
- UI:BizCompare 模板编辑 → 先勾选监控项,再按表编辑行过滤规则(组内且 / 组间或)与字段归一化;无「应用预设」按钮,默认规则随监控项带入后可改
- 单测:`tests.test_biz_state_compare`
---
## 6. 自检清单
- [ ] index + apply_rule 对样本有行
- [ ] prefer_fsm:有 FSM 不走手写;无 FSM 手写仍通
- [ ] Profile match 能命中任务里的 concrete 命令
- [ ] 跨表:辅 profile 可单独 `run_parser`;主 profile enrich 后字段正确
- [ ] `python -m unittest tests.test_fsm_parser_pipeline tests.test_enrich_framework tests.test_multi_command_arp tests.test_biz_state_compare`(及本指标单测)
---
## 7. 参考文件
- Enrich:`biz_state/enrich.py`
- Session:`biz_state/collect_session.py`
- Compare:`biz_state/compare_rules.py`、`compare_engine.py`、`compare_service.py`
- ARP+VRF:`profiles.py` → `zte.arp`;`parsers/zte/arp.py`;辅表 `zte.config_interface`(`zte.if_intf` 已禁用仅兼容)
- 模板:`cli_templates/zte/zte_zxros_show_arp.textfsm`、`..._if_intf.textfsm`