Agent Skillsnetdata/netdata › collectors-snmp-trap-profiles

collectors-snmp-trap-profiles

GitHub

用于编写或审查 Netdata SNMP Trap 配置文件、指标规则及生成器,确保符合 MIB 和分类契约。

.agents/skills/collectors-snmp-trap-profiles/SKILL.md netdata/netdata

Trigger Scenarios

修改 SNMP Trap 配置文件或指标规则 审查 SNMP Trap 相关代码变更 更新 SNMP Trap 分类或严重程度

Install

npx skills add netdata/netdata --skill collectors-snmp-trap-profiles -g -y
More Options

Non-standard path

npx skills add https://github.com/netdata/netdata/tree/master/.agents/skills/collectors-snmp-trap-profiles -g -y

Use without installing

npx skills use netdata/netdata@collectors-snmp-trap-profiles

指定 Agent (Claude Code)

npx skills add netdata/netdata --skill collectors-snmp-trap-profiles -a claude-code -g -y

安装 repo 全部 skill

npx skills add netdata/netdata --all -g -y

预览 repo 内 skill

npx skills add netdata/netdata --list

SKILL.md

Frontmatter
{
    "name": "collectors-snmp-trap-profiles",
    "description": "Author or review Netdata SNMP trap profiles, metrics\/charts rules, loader schema, snmp-trap-profile-gen, stock catalogue\/compression and category\/severity changes. Preserve MIB, varbind, taxonomy and cardinality contracts. Trap-log queries and SNMP polling profiles use separate skills."
}

SNMP Trap Profile Authoring

Use this skill for authoring or reviewing the affected contracts under:

  • src/go/plugin/go.d/config/go.d/snmp.trap-profiles/ (stock pack default/, catalogue.json, profile-format.md)
  • src/go/cmd/snmptrapprofilegen/ (the generator; installed as snmp-trap-profile-gen)
  • src/go/plugin/go.d/collector/snmp_traps/internal/catalog/ when the change is about what a profile may contain

Select The Task

Apply AGENTS.md#skill-selection. Review checks affected rules against source and existing validation evidence; implementation procedures below do not require a reviewer to create artifacts, regenerate the pack, call a classifier or install profiles. Select additional sections when the changed behavior reaches them.

Task Read
Profile contents, templates or metric rules Profile checks and the profile-format owner
Generator or loader behavior Applicable generator/profile checks and their source owners; follow emitted-schema effects
Stock pack regeneration or compression Regeneration, file-size/compression and catalogue contracts; inspect affected generator changes
Category or severity set changes Taxonomy section and every coupled surface it lists
Trap log investigation or polling profiles query-snmp-traps or collectors-snmp-profiles

Authoritative sources

This skill holds only what the documents below lack. Point at them; do not restate them here.

  • src/go/plugin/go.d/config/go.d/snmp.trap-profiles/profile-format.md: the profile format (file layout, the varbinds table, trap entries, .0. tolerance, description templates, metrics:/charts: rules and their validation list, categories, severities, cardinality, operator overrides, generated stock profiles). It ships with the pack under usr/lib/netdata/conf.d/go.d/snmp.trap-profiles/, so it is operator-facing and must stay true to the loader.
  • src/go/plugin/go.d/collector/snmp_traps/ARCHITECTURE.md: how the collector works (life of a trap, journal field contract, package map, "Where To Change Things", and the validation commands; the sections a rule below relies on are cited as src/go/plugin/go.d/collector/snmp_traps/ARCHITECTURE.md#where-to-change-things and src/go/plugin/go.d/collector/snmp_traps/ARCHITECTURE.md#validation). Collector code changes start there, not here.
  • docs/npm/snmp-traps/ (published operator docs): trap-profiles.md (override versus new profile), configuration.md (every job option), field-reference.md (every TRAP_* field), metrics.md (built-in charts and dimensions).
  • Sibling skills: collectors-snmp-profiles for polling profiles; query-snmp-traps for reading trap logs.
  • Design history (rejected alternatives, the comparison with other trap systems, the phase plans) is not maintained in the tree; the pre-implementation design records that used to live in this directory are in git history.

Required checks before changing a profile

  1. Trap name: is MIB-qualified <MIB-MODULE>::<symbol> (IF-MIB::linkDown). Bare symbols are reused across vendor MIB modules and are not unique; the qualified form is what snmptranslate produces and what lands in TRAP_NAME. If the OID changes, the name changes. Different OIDs must have different names (profile-format.md, "Trap entries").

  2. Check MAX-ACCESS of the source MIB object for every varbind. A not-accessible index object still belongs in the varbinds table, so that the rare device that does include it gets it named in TRAP_JSON and, when non-sensitive and non-redundant, as an indexed TRAP_VAR_* field. Never use it as a description: template variable on its own: an SNMP entity normally does not send it in the trap PDU, so the placeholder renders empty. No in-tree artifact can check this; it needs the MIB.

  3. Every varbind reference resolves. A name in a trap's varbinds: list must exist in the file-scoped varbinds: table or be an inline {name, oid, type} dict on that trap. A dangling name renders empty in the description and produces a misleading journal message. Table entries need both oid and type; an empty {} entry fails profile load. The generator drops extractor records with an empty name, OID, or type from the table and from every trap's reference list (buildProfile, pinned by TestBuildProfileDropsUnresolvedVarbinds); do the same by hand.

  4. Keep the metadata the loader validates. Trap status: takes only current, deprecated, mandatory, obsolete, optional (validTrapStatuses in the generator, validStatuses in internal/catalog/profile.go). Varbind enum: is what renders {{value}} symbolically and what equals/in predicates match; constraints: documents the range. File-scope vendor:, mib_count:, trap_count: are emitted on every stock file. Do not strip any of them when editing.

  5. Categories: closed set of 8. state_change, config_change, security, auth, license, mobility, diagnostic, unknown. Cross-cutting concerns (compliance scope, tenant, datacenter, change window) go in labels:, not new slugs. Every profile entry declares category and severity (the loader rejects a missing or unknown value); only a trap with no profile match is logged with unknown/notice at runtime, and operators reclassify it through the job's overrides:. There is no "custom" category. Changing the set is taxonomy work.

  6. Severities: closed set of 8 full syslog names mapped to PRIORITY=0..7: emerg, alert, crit, err, warning, notice, info, debug (never warn). emerg is for true vendor catastrophe; routine events are warning/notice/info; debug only when the MIB itself marks the notification debug-level.

  7. Label cardinality. Label templates reference bounded varbinds only. Reject labels built from MAC addresses, source IPs, usernames, packet contents, RAID slot IDs, or any per-event identifier; the loader rejects unbounded label templates at profile load. High-cardinality content belongs in description: (rendered into MESSAGE), the indexed TRAP_VAR_* fields, and TRAP_JSON, none of which propagate to metrics.

  8. Label keys match [a-z][a-z0-9_]* and emit as TRAP_TAG_<KEY_UPPERCASE>, whether they come from a profile's labels: or from a listener job's per-OID overrides[].labels (there is no job-wide labels: option). The dedicated prefix makes collision with the plugin-owned TRAP_* fields impossible, so the key syntax is the only check. See docs/npm/snmp-traps/field-reference.md for the shipped TRAP_* set (it is not closed at the profile level; new fields arrive with collector releases).

  9. Trap OID form and .0. tolerance. Use the OID form the source MIB tooling produces. Lookup is exact-match first, then retries the alternate spelling with one .0. segment added or removed before the final arc (model.AlternateTrapOID, called from Epoch.lookupLoaded). A catalogue that defines both spellings of one trap fails to load ("alternate form already defined"). The tolerance is trap-OID only: never normalize or alternate-match varbind OIDs.

  10. Stock versus operator. Files under default/ are generated and overwritten on regeneration (their header comment says so); never hand-edit them for site concerns. Operator profiles live in the user config directory go.d/snmp.trap-profiles/ (catalog_paths.go) in one of three forms: a complete same-identity replacement of a stock file, an independent different-identity addition, or a metric-only profile whose rules reference stock traps. Partial inheritance does not exist; an extends: key is rejected as unknown at profile load. Per-OID category, severity, and label overrides belong in the listener job's overrides: (docs/npm/snmp-traps/trap-profiles.md has the decision table).

  11. No hand-authored journal fields. There is no journal_fields: key: TRAP_VAR_* fields are derived from the received non-sensitive, non-redundant varbinds and TRAP_JSON keeps the audit copy. display_hint is documented as a future varbind field but is not part of the loaded schema: varbinds: entries accept unknown keys (profileYAMLSpec in internal/catalog/load.go), so a hand-added display_hint is silently ignored and then overwritten on regeneration. When the renderer starts consuming DISPLAY-HINT metadata, the extractor, profile-format.md, and model.TrapEntry change in the same cycle.

  12. Profile metrics only through the validated schema. profile-format.md, "Optional metrics: rules and charts:", owns the syntax, defaults, numeric source types, and the load-time rejection list; a listener job enables rules explicitly with profile_metrics.include (rule names, never trap names or filenames). Checks the loader enforces (internal/catalog/metric_validate.go) that authors most often get wrong:

    • Chart IDs and contexts must not reuse the six built-in charts events, severity, errors, dedup_suppressed, pipeline, profile_metric_diagnostics or their snmp.trap.* contexts; a chart context defaults to snmp.trap.<chart id> and must start with snmp.trap.; metric names must not start with a reserved prefix (builtInProfileMetricChartIDs, reservedProfileMetricPrefixes).
    • Every where: predicate selects exactly one string-valued source: varbind, or field from the closed set category, severity, trap_name, trap_oid; predicates AND; use absent, not not plus exists.
    • identity.resource.key_from_varbind must be an integer-like bounded varbind (INTEGER, Integer32, Unsigned32, Gauge32); Counter32, Counter64, TimeTicks are sample values, not identity keys.
    • missing: is one of drop, error, zero, unknown_dimension (zero is invalid for counter and state; unknown_dimension needs resource identity). Authoring rules the loader does NOT check, so review them by hand:
    • Every rule sharing a chart has the same label shape: do not mix resource and non-resource rules, or several resource classes, in one chart.
    • Declare lifecycle explicitly on every chart that creates per-source or per-resource instances instead of relying on the loader's defaults; expired instances are removed and a returning identity starts a fresh series.
    • On a chart that carries a state rule, lifecycle.expire_after_cycles must outlast state.ttl: the TTL publishes the clear value once and only then expires the series, so a chart that expires first leaves the problem state as the last value. The loader validates the two fields independently and never compares them. A rule's where: is evaluated before its state.set_when/clear_when predicates.
    • Never use the community varbind or another sensitive varbind (model.IsSensitiveVarbind) as a predicate or sample source; redaction happens downstream, not in rule validation.
    • Profile rules describe vendor or site semantics, never receiver health. Profile metrics update only after the trap is committed to the configured backend; dedup-suppressed and write-failed traps do not count.
    • No stock profile ships metrics: today (0 of 803). Stock rules would be a curation layer that the generator must preserve through a tested read-modify-write path from a reviewable, committed source recording rule name, trap, varbinds, type, chart, and cardinality evidence, and must validate against the varbinds: and traps: it just emitted before writing the file. That path does not exist; build it before adding stock rules, and check pack size and lazy-load memory when you do.

Required checks when editing the generator (src/go/cmd/snmptrapprofilegen/)

  1. One Go binary, no runtime dependencies. CMake target snmp_trap_profile_gen builds snmp-trap-profile-gen and installs it under usr/libexec/netdata/plugins.d/ in the plugin-go component; the pack build runs it with CGO_ENABLED=0. Do not add Python, CGO, SQLite, or a runtime MIB compiler to the shipped path.

  2. Subcommands are extract, classify, emit, generate, and compress-zstd (usage). generate is extract plus optional classify plus emit; the three stages exist separately for reruns on saved artifacts.

  3. Extraction stays incremental and memory-bounded. The corpus is too large for one MIB universe: keep batch-based gomib loading (--batch-size, default 32), deterministic source priority, and the review artifacts under --out-dir: traps.jsonl, extraction-report.json, conflicts.json (duplicate trap OIDs), dot0-conflicts.json (both .0. spellings present), source-conflicts.json (one module name in several files). --baseline-profiles-dir adds a stock-overlap report. If source discovery changes, rerun a representative multi-vendor corpus before touching the stock pack.

  4. Classification cache stays reviewable JSONL. One Classification record per trap keyed by hashTrap, with schema_version and prompt_version; a record whose versions differ from defaultSchemaVer or defaultPromptVer is rejected, so bump defaultPromptVer whenever the prompt or the taxonomy changes or the cache silently replays stale answers. --force-llm ignores the cache. The cache path is derived from the default out-dir unless --cache is passed explicitly, even when --out-dir differs. Never switch to SQLite or another opaque store.

  5. LLM output validation is mandatory. Every response passes the classifier response JSON Schema (classifierResponseSchemaJSON, checked by validateClassifierResponseSchema), the template check (validateDescriptionTemplate: only the helpers in classifierTemplateFuncMap, references checked against the trap record), and the style check (validateDescriptionStyle: ends with on {{hostname}}., {{hostname}} exactly once). Off-taxonomy categories are remapped first by repairInvalidCategory. Up to maxLLMAttempts (5) tries, then mechanicalClassification, or a hard failure under --require-llm. MIB text reaches the model wrapped as untrusted input (sanitizePromptText); keep that wrapping.

  6. Emission is deterministic and produces the file-scoped table (buildProfile, writeProfileYAML):

    • trap name: is MIB-qualified; varbind table names are bare symbols;
    • one table entry per varbind name; a name that recurs with a different OID or type falls back to an inline {name, oid, type} dict on that trap (intended; do not "fix" it, and do not regress to inline everywhere);
    • records with an empty name, OID, or type are dropped from table and references; no {} entries;
    • traps sort by OID (compareOIDString) and the table keys and rule names are sorted, so regenerations diff cleanly;
    • the three-line header comment is part of the file and of its digest;
    • the vendor slug (vendorForOID: standard, ieee-lldp, ieee-802, the PEN slug or enterprise-<pen>, else oid-<first arc> or unknown) is the output filename and therefore the identity an operator override replaces.
  7. catalogue.json stays in sync. Each entry (profileCatalogueEntry) records file, mib_count, mibs, sample_traps, trap_count, trap_oids, varbind_count, sha256, and metric_rule_names when the profile has rules (omitted otherwise, which is every stock file today; emitting stock rules also needs the curation path described in profile check 12). sha256 is 64 lowercase hex over the exact bytes written, comments and final newline included (format pinned by TestStockProfileCatalogueRequiresValidSHA256); lazy hydration verifies it (TestStockProfileEpochBindsLazyHydrationToManifestContent). Catalog tests load all shipped profiles and require the manifest and the files to agree in both directions (TestStockProfileCatalogueMatchesDefaultFiles, TestStockCatalogueReconcilesPhysicalInventory, TestStockProfileDefaultFilesParse): regenerating profiles without the catalogue fails tests.

  8. PEN registry. The default is the bundled snapshot (defaultPENFilePath; installed at usr/lib/netdata/conf.d/go.d/snmp.profiles/metadata/iana-enterprise-numbers.txt). With --refresh-pen, or when the file is missing or empty, loadPENs fetches --pen-url and a failed fetch aborts the run (TestLoadPENsRefreshFailureIsFatal). An air-gapped run needs the snapshot present.

Regenerating the stock pack

  • Operation: this command overwrites stock profiles and the catalogue. Run it only for authorized pack regeneration; review or schema inspection does not require it.
  • Classifier: --classify sends MIB-derived text to the configured endpoint. Establish the source scope and endpoint/model within the task's authorization before running; the default local endpoint is not an offline parser.
  • Artifacts: from the repository root, create a fresh private run below.
  • Cache: select a compatible earlier cache explicitly in --cache for an intentional rerun. Reuse can append and compact that file; copy it into the fresh run first when preserving the original matters.
(
set -e
umask 077
PROFILE_AUDIT_DIR="$PWD/.local/audits/snmp-trap-profiles"
mkdir -p "$PROFILE_AUDIT_DIR"
PROFILE_RUN_DIR="$(mktemp -d "$PROFILE_AUDIT_DIR/generate.XXXXXX")"
cd src/go
go run ./cmd/snmptrapprofilegen generate \
  --source-dir /path/to/mibs \
  --all \
  --classify \
  --require-llm \
  --concurrency 20 \
  --out-dir "$PROFILE_RUN_DIR" \
  --cache "$PROFILE_RUN_DIR/classification-cache.jsonl" \
  --profiles-out-dir ./plugin/go.d/config/go.d/snmp.trap-profiles/default \
  --catalogue ./plugin/go.d/config/go.d/snmp.trap-profiles/catalogue.json
)
  • Classification talks to an OpenAI-compatible endpoint (--base-url, default a local server; --model); without --classify every trap keeps the extractor defaults: category unknown, severity notice, description <qualified name> on {{hostname}}..
  • Review the diff: ordering is deterministic, so unexpected churn means an extractor or prompt change.
  • Counts quoted in docs (ARCHITECTURE.md, docs/npm/snmp-traps/trap-profiles.md) describe the pack; recompute them from catalogue.json after a regeneration rather than carrying old numbers.

The installed operator form converts site MIBs offline; the output under snmp-trap-profile-gen-output/profiles/ is copied into the operator profile directory (profile-format.md, "Generated stock profiles").

Changing categories or severities (taxonomy work)

The sets are duplicated in code, alerts, and docs. Only the telemetry series are test-pinned (TestJobCollectsExactRetainedMetricSet in internal/telemetry/job_test.go enumerates the per-category and per-severity counters); nothing pins the generator's or the loader's sets. A change must touch every site:

  1. Generator main.go: validCategories, validSeverities, severityPriority, repairInvalidCategory, mechanicalClassification (the no-LLM fallback hard-codes both sets), the classifier response JSON Schema (classifierResponseSchemaJSON), and the prompt text; bump defaultPromptVer.
  2. Collector internal/catalog/profile.go: validCategories, validSeverities, categoryList, severityList (the loader rejects profiles the generator would otherwise emit).
  3. Per-slug surfaces in the collector: the overrides[].category and overrides[].severity enums in config_schema.json, the internal/telemetry counters and their test, the events and severity chart dimensions in charts.yaml and metadata.yaml, the OTLP severity mapping (otlpSeverity in internal/output/otlp), and the PRIORITY mapping in internal/output/journal (grep -rn the slug across snmp_traps/).
  4. Health: src/health/health.d/snmp_traps.conf has severity-rate alert templates for emerg, alert, crit, err, and warning (notice, info, debug deliberately do not alert), mirrored in the alerts: list of metadata.yaml; a renamed or removed slug silently breaks them. grep -rn the slug across src/health/health.d/ too.
  5. Docs: profile-format.md category and severity tables, and every operator page that enumerates the sets (grep -rln the slug across docs/npm/snmp-traps/: today README.md, trap-profiles.md, configuration.md, field-reference.md, metrics.md, alerts.md, usage-and-output.md, journal-and-querying.md, investigation-playbooks.md).
  6. Re-run classification for the full corpus: existing cache records were produced under the old taxonomy.

Add tests that pin the generator's and the loader's sets when you touch them.

File size and compression

  • Stock profile YAMLs stay raw in the repository so git diff reviews them.
  • The pack build (CMake) runs the generator's compress-zstd --rm subcommand via go run on a copy of the pack and installs *.yaml.zst plus catalogue.json.zst; the installed pack must stay compressed. The loader accepts profiles as .yaml.zst, .yml.zst, .yaml, or .yml (internal/catalog/load.go) and the manifest as catalogue.json or catalogue.json.zst, never gzip (internal/catalog/stock.go).
  • Operator profiles stay uncompressed .yaml for editability.
  • If one vendor file passes about 10 MB in the repository, cut description verbosity rather than hide generated bloat behind compression.

Validation

Use the suites for the affected generator/profile contract, including consumers when their behavior changes. Review checks relevant existing results and can run isolated local tests; it does not execute the stock-regeneration recipe.

cd src/go
go test -count=1 ./cmd/snmptrapprofilegen/
go test -count=1 ./plugin/go.d/collector/snmp_traps/internal/catalog/...

The catalog tests load all shipped profiles and verify the manifest. For collector code changes run the full snmp_traps suite with -race as src/go/plugin/go.d/collector/snmp_traps/ARCHITECTURE.md#validation describes.

Version History

  • 1e97a0f Current 2026-09-23 11:23

Same Skill Collection

.agents/skills/collectors-authoring/SKILL.md
.agents/skills/collectors-go-design/SKILL.md
.agents/skills/collectors-go-framework-v2/SKILL.md
.agents/skills/collectors-metadata-yaml/SKILL.md
.agents/skills/collectors-prometheus-profiles/SKILL.md
.agents/skills/collectors-snmp-profiles/SKILL.md
.agents/skills/coverity-audit/SKILL.md
.agents/skills/docs-learn-pr-preview/SKILL.md
.agents/skills/docs-learn-site-structure/SKILL.md
.agents/skills/graphql-audit/SKILL.md
.agents/skills/health-alert-authoring/SKILL.md
.agents/skills/integrations-lifecycle/SKILL.md
.agents/skills/learn-pr-preview/SKILL.md
.agents/skills/packaging-static-installer/SKILL.md
.agents/skills/project-create-topology/SKILL.md
.agents/skills/project-health-alert-authoring/SKILL.md
.agents/skills/project-prometheus-profiles/SKILL.md
.agents/skills/project-query-corpus/SKILL.md
.agents/skills/project-snmp-profiles-authoring/SKILL.md
.agents/skills/project-snmp-trap-profiles-authoring/SKILL.md
.agents/skills/project-writing-go-modules-framework-v2/SKILL.md
.agents/skills/repo-mirror-sources/SKILL.md
.agents/skills/repo-pr-reviews/SKILL.md
.agents/skills/repo-skill-authoring/SKILL.md
.agents/skills/sonarqube-audit/SKILL.md
.agents/skills/tests-query-corpus/SKILL.md
.agents/skills/topology-authoring/SKILL.md
.agents/skills/triage-agent-events/SKILL.md
.agents/skills/triage-codacy/SKILL.md
.agents/skills/triage-codeql/SKILL.md
.agents/skills/triage-coverity/SKILL.md
.agents/skills/triage-snmp-diagnostics/SKILL.md
.agents/skills/triage-sonarqube/SKILL.md
docs/netdata-ai/skills/query-netdata-agents/SKILL.md
docs/netdata-ai/skills/query-netdata-cloud/SKILL.md
docs/netdata-ai/skills/query-snmp-traps/SKILL.md
.agents/skills/codacy-audit/SKILL.md
.agents/skills/learn-site-structure/SKILL.md
.agents/skills/mirror-netdata-repos/SKILL.md
.agents/skills/pr-reviews/SKILL.md
.agents/skills/project-build-static-binary/SKILL.md
.agents/skills/project-writing-collectors/SKILL.md
.agents/skills/query-agent-events/SKILL.md
.agents/skills/triage-support-bundle/SKILL.md

Metadata

Files
0
Version
1e97a0f
Hash
9f7e7f76
Indexed
2026-09-23 11:23

trang chủ - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-09-23 12:50
浙ICP备14020137号-1