关于 cmdb-patterns
面向 ServiceNow CMDB 管理的技能,介绍 CI 类别、关系、影响分析和数据质量检查。重点说明通过 IRE 识别并更新 CI,以减少重复记录并保留数据来源。
主要功能
- 01介绍 CI 类别与关键表
- 02创建 CI 并维护关系
- 03分析 CI 的上下游影响
- 04检测孤立与过期 CI
- 05通过 IRE 识别和协调 CI
适用场景
- 01提交发现的服务器及其应用关系
- 02评估 CI 故障影响的业务服务
- 03检查 CMDB 中孤立或过期的 CI
MCP 相关
文档列出用于搜索、创建和查询 CI、建立关系、分析影响及触发发现的 MCP 工具。它强调使用 snow_cmdb_identify_reconcile 通过 IRE 写入,避免重复并记录数据来源。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ serac-labs/ serac --skill cmdb-patterns安装后在 Claude Code 中输入/cmdb-patterns确认已加载。
详细介绍
ServiceNow 的 CMDB 模式
配置管理数据库(CMDB)是 ServiceNow ITSM 的基础,用于跟踪所有配置项(CI)及其关系。
CMDB 架构
CI 类层次结构
cmdb (Base)
└── cmdb_ci (Configuration Item)
├── cmdb_ci_computer
│ ├── cmdb_ci_server
│ │ ├── cmdb_ci_linux_server
│ │ ├── cmdb_ci_win_server
│ │ └── cmdb_ci_unix_server
│ └── cmdb_ci_pc_hardware
├── cmdb_ci_service
│ ├── cmdb_ci_service_business
│ ├── cmdb_ci_service_technical
│ └── cmdb_ci_service_auto
│ └── cmdb_ci_service_discovered
├── cmdb_ci_appl
│ ├── cmdb_ci_app_server
│ └── cmdb_ci_db_instance
└── cmdb_ci_network_gear
├── cmdb_ci_netgear
└── cmdb_ci_lb
关键 CI 表
| 表 | 用途 | 关键字段 |
|---|---|---|
cmdb_ci | CI 基础表 | name, sys_class_name, operational_status |
cmdb_ci_server | 服务器 | ip_address, os, cpu_count, ram |
cmdb_ci_service | 服务(基类 — 业务服务为 cmdb_ci_service_business) | service_classification, busines_criticality |
cmdb_ci_appl | 应用程序 | version, install_directory |
cmdb_rel_ci | CI 关系 | parent, child, type |
创建配置项
创建基本 CI(ES5)
// Create a new server CI
var ci = new GlideRecord("cmdb_ci_server")
ci.initialize()
ci.setValue("name", "PROD-WEB-001")
ci.setValue("ip_address", "10.0.1.100")
ci.setValue("os", "Linux Red Hat")
ci.setValue("os_version", "8.5")
ci.setValue("cpu_count", 8)
ci.setValue("ram", 32768)
ci.setValue("operational_status", 1) // Operational
ci.setValue("install_status", 1) // Installed
ci.setValue("used_for", "Production")
ci.setValue("owned_by", "sys_id_of_owner")
ci.setValue("support_group", "sys_id_of_group")
var sysId = ci.insert()
带有发现来源的 CI
// CI from Discovery
var ci = new GlideRecord("cmdb_ci_linux_server")
ci.initialize()
ci.setValue("name", "discovered-server-001")
ci.setValue("discovery_source", "ServiceNow")
ci.setValue("first_discovered", new GlideDateTime())
ci.setValue("last_discovered", new GlideDateTime())
ci.setValue("ip_address", "10.0.2.50")
// Set classification
ci.setValue("classification", "Production")
ci.setValue("environment", "Production")
ci.insert()
CI 关系
关系类型
| 类型 | 父项 → 子项 | 示例 |
|---|---|---|
Runs on::Runs | 应用 → 服务器 | ERP 运行于 PROD-DB-01 |
Depends on::Used by | 服务 → 应用 | HR 服务依赖 SAP |
Contains::Contained by | 集群 → 服务器 | 集群包含 Node1 |
Hosted on::Hosts | VM → 虚拟机监控程序 | VM01 托管于 ESX01 |
Members::Member of | CI → 组 | 服务器是池的成员 |
创建关系(ES5)
// Create relationship between CIs
function createCIRelationship(parentSysId, childSysId, relationType) {
// Find relationship type
var relType = new GlideRecord("cmdb_rel_type")
relType.addQuery("name", relationType)
relType.query()
if (!relType.next()) {
gs.error("Relationship type not found: " + relationType)
return null
}
// Check if relationship already exists
var existing = new GlideRecord("cmdb_rel_ci")
existing.addQuery("parent", parentSysId)
existing.addQuery("child", childSysId)
existing.addQuery("type", relType.getUniqueValue())
existing.query()
if (existing.next()) {
gs.info("Relationship already exists")
return existing.getUniqueValue()
}
// Create new relationship
var rel = new GlideRecord("cmdb_rel_ci")
rel.initialize()
rel.setValue("parent", parentSysId)
rel.setValue("child", childSysId)
rel.setValue("type", relType.getUniqueValue())
return rel.insert()
}
// Usage
createCIRelationship(appSysId, serverSysId, "Runs on::Runs")
查询关系
// Find all servers an application runs on
function getAppServers(appSysId) {
var servers = []
var rel = new GlideRecord("cmdb_rel_ci")
rel.addQuery("parent", appSysId)
rel.addQuery("type.name", "Runs on::Runs")
rel.query()
while (rel.next()) {
var server = rel.child.getRefRecord()
servers.push({
sys_id: server.getUniqueValue(),
name: server.getValue("name"),
ip_address: server.getValue("ip_address"),
})
}
return servers
}
// Find all dependencies of a service
function getServiceDependencies(serviceSysId) {
var deps = []
var rel = new GlideRecord("cmdb_rel_ci")
rel.addQuery("parent", serviceSysId)
rel.addQuery("type.name", "Depends on::Used by")
rel.query()
while (rel.next()) {
deps.push({
sys_id: rel.child.getUniqueValue(),
name: rel.child.getDisplayValue(),
class: rel.child.sys_class_name.toString(),
})
}
return deps
}
影响分析
上游/下游分析
// Get all CIs affected by a CI outage (downstream impact)
function getDownstreamImpact(ciSysId, depth) {
if (typeof depth === "undefined") depth = 3
var impacted = []
var processed = {}
function traverse(sysId, currentDepth) {
if (currentDepth > depth || processed[sysId]) return
processed[sysId] = true
var rel = new GlideRecord("cmdb_rel_ci")
rel.addQuery("child", sysId)
rel.query()
while (rel.next()) {
var parentId = rel.parent.toString()
if (!processed[parentId]) {
impacted.push({
sys_id: parentId,
name: rel.parent.getDisplayValue(),
depth: currentDepth,
})
traverse(parentId, currentDepth + 1)
}
}
}
traverse(ciSysId, 1)
return impacted
}
// Get all CIs this CI depends on (upstream dependencies)
function getUpstreamDependencies(ciSysId, depth) {
if (typeof depth === "undefined") depth = 3
var dependencies = []
var processed = {}
function traverse(sysId, currentDepth) {
if (currentDepth > depth || processed[sysId]) return
processed[sysId] = true
var rel = new GlideRecord("cmdb_rel_ci")
rel.addQuery("parent", sysId)
rel.query()
while (rel.next()) {
var childId = rel.child.toString()
if (!processed[childId]) {
dependencies.push({
sys_id: childId,
name: rel.child.getDisplayValue(),
depth: currentDepth,
})
traverse(childId, currentDepth + 1)
}
}
}
traverse(ciSysId, 1)
return dependencies
}
业务服务影响
// Find all business services impacted by a CI
function getImpactedServices(ciSysId) {
var services = []
var processed = {}
function findServices(sysId) {
if (processed[sysId]) return
processed[sysId] = true
// Check if this CI is a service
var ci = new GlideRecord("cmdb_ci")
if (ci.get(sysId)) {
if (ci.sys_class_name.toString().indexOf("cmdb_ci_service") === 0) {
services.push({
sys_id: sysId,
name: ci.getValue("name"),
criticality: ci.getValue("busines_criticality"),
})
}
}
// Traverse upstream
var rel = new GlideRecord("cmdb_rel_ci")
rel.addQuery("child", sysId)
rel.query()
while (rel.next()) {
findServices(rel.parent.toString())
}
}
findServices(ciSysId)
return services
}
CMDB 健康状况与数据质量
检测孤立 CI
// Find CIs without relationships
function findOrphanCIs(ciClass) {
var orphans = []
var ci = new GlideRecord(ciClass || "cmdb_ci")
ci.addQuery("operational_status", 1) // Operational only
ci.query()
while (ci.next()) {
var sysId = ci.getUniqueValue()
// Check for any relationships
var rel = new GlideRecord("cmdb_rel_ci")
rel.addQuery("parent", sysId).addOrCondition("child", sysId)
rel.setLimit(1)
rel.query()
if (!rel.hasNext()) {
orphans.push({
sys_id: sysId,
name: ci.getValue("name"),
class: ci.getValue("sys_class_name"),
})
}
}
return orphans
}
检测过期 CI
// Find CIs not updated by discovery
function findStaleCIs(daysOld) {
if (typeof daysOld === "undefined") daysOld = 30
var stale = []
var cutoff = new GlideDateTime()
cutoff.addDaysLocalTime(-daysOld)
var ci = new GlideRecord("cmdb_ci")
ci.addQuery("operational_status", 1)
ci.addQuery("last_discovered", "<", cutoff)
ci.addNotNullQuery("last_discovered")
ci.query()
while (ci.next()) {
stale.push({
sys_id: ci.getUniqueValue(),
name: ci.getValue("name"),
last_discovered: ci.getValue("last_discovered"),
})
}
return stale
}
MCP 工具集成
可用的 CMDB 工具
| 工具 | 用途 |
|---|---|
snow_cmdb_identify_reconcile | 通过 IRE 插入/更新 CI — 写入前先进行识别 |
snow_create_ci | 使用正确的类创建新 CI(直接写入类表) |
snow_cmdb_search | 使用筛选条件搜索 CI |
snow_create_ci_relationship | 创建 CI 关系 |
snow_impact_analysis | 分析 CI 影响 |
snow_get_ci_details | 获取完整的 CI 信息 |
snow_run_discovery | 触发发现 |
通过 IRE 写入 CI,不要先搜索
先搜索 CI,搜索不到再创建,这种模式会让 CMDB 充斥重复项:只要名称、IP 或序列号有任何差异,就无法匹配;而第二次写入也没有记录数据来源。标识与协调引擎正是为正确匹配而设计的。snow_cmdb_identify_reconcile 会将负载提交到 /api/now/identifyreconcile,因此每个类的标识规则都会针对负载运行,已有的 CI 会得到更新而非重复创建,协调规则会决定此数据源能否覆盖各个属性,并将写入记录到来源 [sys_object_source]。
snow_create_ci 和 snow_update_ci 会直接写入类表,跳过上述所有流程。仅当你确定 CMDB 中不存在该 CI,且无需注明其来源时,才使用它们。不要理会名称容易引起误解的 snow_reconcile_ci —— 它会将你的字段 PUT 到基础 cmdb_ci,并原样返回 reconciliation_rule 参数;不会运行任何规则。
一个负载还可以通过 relations 描述其中各项之间的关系,其中 type 是 CI Relationship Type [cmdb_rel_type] 中的名称字段值,parent/child 是 items 中的索引。这样一来,发现主机及其上运行的应用程序的数据源就可以在一次调用中同时提交两者。
dry_run 不会提交任何更改,但权限按工具而非参数控制,因此它仍被视为写入操作:在生产实例上,试运行和实际调用一样,都需要 __confirmProd。
示例工作流
// 1. Dry run first: IRE reports what it would do and commits nothing. On a
// 400-CI payload this is the only way to see the verdict before taking it.
await snow_cmdb_identify_reconcile({
data_source: "ServiceNow", // must be a choice on cmdb_ci.discovery_source
dry_run: true,
items: [
{
className: "cmdb_ci_linux_server",
values: { name: "PROD-WEB-002", ip_address: "10.0.1.101", serial_number: "VMW-42-8A" },
},
],
})
// Read items[0].operation — INSERT, UPDATE, NO_CHANGE, UPDATE_WITH_UPGRADE,
// UPDATE_WITH_DOWNGRADE, UPDATE_WITH_SWITCH or DELETE — and
// items[0].identificationAttempts for the identifier rule that matched.
// 2. Same payload without dry_run commits it. IRE answers HTTP 200 even when
// individual items fail, so check data.items[i].errors, which the tool
// surfaces as a failure rather than a success.
const ire = await snow_cmdb_identify_reconcile({
data_source: "ServiceNow",
items: [
{
className: "cmdb_ci_linux_server",
values: { name: "PROD-WEB-002", ip_address: "10.0.1.101", serial_number: "VMW-42-8A" },
},
],
})
// sys_id of the CI IRE inserted or matched: ire.data.items[0].sysId
// 3. Create relationship (relationship_type is a cmdb_rel_type sys_id,
// look it up via snow_query_table on cmdb_rel_type)
await snow_create_ci_relationship({
parent_ci: appSysId,
child_ci: serverSysId,
relationship_type: runsOnRelTypeSysId, // sys_id of "Runs on::Runs"
})
// 4. Impact analysis (the CI argument is ci_id; there is no direction parameter).
// include_services is left off on purpose: the business_services key it fills
// is not impact — see the csdm-modeling skill.
await snow_impact_analysis({
ci_id: serverSysId,
depth: 3,
include_services: false,
})
最佳实践
- 使用正确的 CI 类 - 始终使用最具体的类(
cmdb_ci_linux_server,而不是cmdb_ci) - 维护关系 - 没有关联关系的 CI 价值有限
- 与发现模式保持一致 - 使手动创建的 CI 与发现模式保持一致
- 运维状态 - 保持状态最新(Operational、Retired 等)
- 唯一标识符 - 使用
serial_number、asset_tag确保唯一性 - 服务映射 - 将 CI 关联到业务服务
- 定期清理 - 归档已退役的 CI,移除孤立项