关于 netbox-custom-scripts
netbox-custom-scripts 是用于编写和调试 NetBox 内部 Python 脚本的技能,提供有关变量、ORM 变更、数据校验和后台作业的版本适配指引。
主要功能
- 01介绍脚本类和表单变量
- 02说明 ORM 校验与变更日志
- 03讲解事务、终止操作和作业执行
- 04描述特定版本的 API 与 ORM 变化
- 05提供脚本编写和调试指引
适用场景
- 01编写用于变更库存数据的 NetBox 脚本
- 02排查脚本变量或执行故障
- 03保存模型变更前校验数据
- 04根据 NetBox 版本调整脚本
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ netboxlabs/ skills --skill netbox-custom-scripts安装后在 Claude Code 中输入/netbox-custom-scripts确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/execution-model.mdorm-patterns.mdscript-variables.md详细介绍
NetBox 自定义脚本
关于 NetBox 自定义脚本的知识可能已经过时。 Script 类 API、变量类型和作业框架会随着 NetBox 版本的发布而演变。请优先检索信息,而不是依赖预训练知识。
NetBox 4.7+:核心自定义脚本已弃用——目前无需采取行动。 内置实现将在 4.7 和 4.8 版本周期内继续提供并受支持,计划在 NetBox 5.0 中移除。替代方案是开源插件
netbox-scripts(脚本按项目组织、脚本间共享 Python 资源、每个脚本均可进行版本控制);迁移预计大部分可自动完成,无需重写脚本。现有脚本仍可照常运行。截至 2026 年 9 月,该插件仍处于 alpha 阶段(v0.0.1,仅支持 NetBox 4.7),不建议用于生产环境——在 NetBox 4.5–4.7 上,请继续按照此技能编写核心脚本。参见版本说明。
检索来源
| 来源 | URL / 方法 | 用途 |
|---|---|---|
| 自定义脚本文档 | https://netboxlabs.com/docs/netbox/customization/custom-scripts/ | Script 类、变量、调度 |
| NetBox 仓库 | https://github.com/netbox-community/netbox | 脚本框架源代码 |
| netbox-scripts 插件 (4.7+,alpha) | https://github.com/netbox-community/netbox-scripts | 核心脚本的后继方案;迁移工作流 |
| NetBox MCP 服务 | 如果已配置——在脚本执行前后验证对象状态 | 测试脚本输出 |
首先:验证环境
自定义脚本在 NetBox 内运行。请确认 NetBox 实例正在运行,并且你可以访问脚本端点:
curl -s -H "Authorization: Bearer $NETBOX_TOKEN" "$NETBOX_URL/api/extras/scripts/" | python -m json.tool
你应该会看到已安装脚本的列表。脚本从 SCRIPTS_ROOT 加载(默认值:/opt/netbox/netbox/scripts/)。
编写或调试在 NetBox 内运行的自定义脚本时,请使用此技能。有关插件开发(新模型、视图、API),请参阅 netbox-plugin-development。
快速参考
最简脚本
from extras.scripts import Script, StringVar, ObjectVar
from dcim.models import Site
class HelloWorld(Script):
class Meta:
name = "Hello World"
description = "A minimal example script"
commit_default = False
site = ObjectVar(model=Site)
def run(self, data, commit):
self.log_success(f"Selected site: {data['site']}", data['site'])
return "Done"
日志方法
| 方法 | 级别 | 副作用 |
|---|---|---|
self.log_debug(msg, obj) | DEBUG | — |
self.log_info(msg, obj) | INFO | — |
self.log_success(msg, obj) | SUCCESS | — |
self.log_warning(msg, obj) | WARNING | — |
self.log_failure(msg, obj) | FAILURE | 将 self.failed 设为 True |
msg 和 obj 均为可选参数。消息支持 Markdown。如果 obj 具有 get_absolute_url(),它将在 UI 中显示为可点击链接。
常用导入
from extras.scripts import Script, AbortScript
from extras.scripts import (
StringVar, TextVar, IntegerVar, BooleanVar, ChoiceVar,
MultiChoiceVar, ObjectVar, MultiObjectVar, FileVar,
IPAddressVar, IPAddressWithMaskVar, IPNetworkVar,
DateVar, DateTimeVar, DecimalVar,
)
from dcim.models import Device, Site, Interface, Rack, Region
from ipam.models import IPAddress, Prefix, VLAN, VRF
from tenancy.models import Tenant
from circuits.models import Circuit
脚本结构
元属性
| 属性 | 默认值 | 用途 |
|---|---|---|
name | 类名 | UI 中显示的名称 |
description | '' | UI 中显示的描述 |
commit_default | True | 提交复选框的默认状态 |
scheduling_enabled | True | 允许按计划或定期执行 |
notifications_default (4.6+) | 'always' | 作业的初始通知策略:always、on_failure 或 never。从事件规则或 runscript 运行时也会使用此策略 |
job_timeout | RQ_DEFAULT_TIMEOUT | 以秒为单位的最长运行时间 |
field_order | 声明顺序 | 字段名称元组 |
fieldsets | None | 分组字段布局(优先于 field_order) |
字段集
在 UI 中将相关字段分组:
class Meta:
fieldsets = (
('Source', ('region', 'site')),
('Configuration', ('vlan_id', 'description')),
)
模块级排序
控制文件包含多个脚本时的显示顺序:
script_order = (ScriptA, ScriptB, ScriptC)
表单变量
所有变量都支持:label、description、default、required(默认值为 True)、widget。
| 变量 | 关键选项 | 说明 |
|---|---|---|
StringVar | min_length、max_length、regex | 单行文本 |
TextVar | — | 多行文本区域 |
IntegerVar | min_value、max_value | |
BooleanVar | — | 内部始终为可选项 |
ChoiceVar | choices(由 (value, label) 组成的列表) | 自动添加空白选项 |
MultiChoiceVar | choices | 多选 |
ObjectVar | model、query_params、null_option、selector、quick_add | 由 API 动态提供数据的下拉菜单。selector(4.5+)会显示高级对象选择器;quick_add(4.6.2+)会添加内联的“新建”按钮 |
MultiObjectVar | 与 ObjectVar 相同 | 多选对象 |
FileVar | — | 文件上传(仅在执行期间可用) |
IPAddressVar | — | IPv4/IPv6 地址,不含掩码 |
IPAddressWithMaskVar | — | 带前缀的 IP 地址(例如,192.168.1.1/24) |
IPNetworkVar | min_prefix_length、max_prefix_length | 网络前缀 |
DateVar / DateTimeVar | — | 日期/时间选择器 |
DecimalVar | min_value、max_value、max_digits、decimal_places |
完整详情请参阅 references/script-variables.md。
使用 ObjectVar 动态筛选
在 query_params 中使用 $ 前缀引用其他表单字段:
region = ObjectVar(model=Region)
site = ObjectVar(model=Site, query_params={'region_id': '$region'})
rack = ObjectVar(model=Rack, query_params={'site_id': '$site'})
界面会根据父级选项动态筛选每个下拉菜单。
ORM 访问与变更日志记录
脚本拥有完整的 Django ORM 访问权限。请遵循以下三条关键规则:
1. 在调用 save() 前始终调用 full_clean()
device = Device(name='new-device', site=site, role=role, device_type=dtype)
device.full_clean() # Validates model constraints — skipping risks data corruption
device.save()
2. 修改现有对象前调用 snapshot()
device = Device.objects.get(name='existing')
device.snapshot() # Required for change log to show a diff
device.status = 'active'
device.full_clean()
device.save()
3. 使用 _changelog_message 提供上下文(可选)
device._changelog_message = 'Bulk status update via maintenance script'
device.save()
会影响脚本的 NetBox 4.7 ORM 变更
- 自定义字段数据:在所有版本中,脚本内的
obj.custom_field_data['x']仍然是原始存储值。只有 REST/GraphQL 表示形式在 4.7 中发生了变化(选择字段会返回{"value": ..., "label": ...})——不要期望从 ORM 获取字典。 CustomField.objects.get_for_model()和obj.custom_fields在 4.7 中返回列表(仅包含启用的字段),而在 4.5/4.6 中返回 queryset。不要对它们链式调用.filter()/.exclude();请使用列表推导式。- 分层模型在 4.7 中使用
ltree(Region、SiteGroup、Location、DeviceRole、Platform、TenantGroup 等):get_ancestors()、get_descendants()、get_children()仍可用;level仅是 Python 属性(不能使用.filter(level=…)/.order_by('level'));get_root()、get_family()、is_leaf_node()、move_to()、insert_at()已移除。 - 4.7 中,全局搜索索引更新会延迟到后台作业执行——脚本刚创建的对象可能会在短时间内无法通过全局搜索找到(ORM 查询不受影响)。
有关查询优化、批量操作模式以及上述内容的版本条件示例,请参阅 references/orm-patterns.md。
事务处理
**所有脚本执行都由 transaction.atomic() 包裹。**这意味着:
commit=True:脚本在无错误完成时,变更会持久保存commit=False:run()完成后,所有数据库变更都会回滚(试运行模式)- 任何异常:无论
commit设置如何,都会回滚所有变更 - 脚本无法部分提交——要么全部提交,要么全部回滚
干净地中止
from utilities.exceptions import AbortScript
if critical_error:
raise AbortScript("Clear error message")
AbortScript 会将消息记录为失败并回滚所有变更——输出中不会显示堆栈跟踪。
log_failure 不会中止
调用 self.log_failure() 会设置 self.failed = True(在界面中显示警告),但不会停止执行。要真正停止执行,请使用 AbortScript。
执行模型
脚本通过 Django-RQ 作为后台作业运行:
- UI/API 提交 → 作业进入 Redis 队列 → RQ 工作进程执行
- 命令行:
python manage.py runscript --commit --user admin module.ClassName - 调度:设置
schedule_at以供未来执行,设置interval以便重复执行 - 超时:由
Meta.job_timeout或全局RQ_DEFAULT_TIMEOUT控制
NetBox 4.7+ — REST 执行:
POST /api/extras/scripts/<id>/需要启用了写入权限的令牌;只读令牌会在脚本解析之前收到 HTTP 403。自 4.7.1 起,data载荷会根据脚本声明的变量进行验证(缺失/无效 → HTTP 400,未声明的键会被丢弃),并且ObjectVarID 会在run()前解析为模型实例——请传入 ID,例如{"data": {"site": 1}}。包含FileVar的脚本必须通过multipart/form-data运行。已完成的作业会记录execution_time(4.7)。
NetBox 4.6+ — 通知:每次运行都有一个
notifications执行参数(always/on_failure/never;UI 表单字段、REST 请求体键,默认值由Meta.notifications_default指定)。不要依赖 UI 通知来判断已调度的运行是否结束——请轮询作业。NetBox 4.6.2 还会防止重复的已调度后台作业(重新提交已排队的重复作业不再会堆积重复项)。
有关作业生命周期的详细信息,请参阅 references/execution-model.md。
作业状态
PENDING → RUNNING → COMPLETED | ERRORED | FAILED
或 SCHEDULED → PENDING → ...
验证报告(测试方法)
名为 test_* 的方法会被自动检测并作为验证检查运行:
class CablingAudit(Script):
class Meta:
name = "Cabling Audit"
def test_console_connections(self):
for device in Device.objects.filter(status='active'):
ports = ConsolePort.objects.filter(device=device)
for port in ports:
if port.connected_endpoints:
self.log_success(None, port) # Count success without message
else:
self.log_failure(f"No console connection: {port}", device)
如果未重写 run(),NetBox 会依次执行 pre_run() → 所有 test_* 方法 → post_run()。
NetBox 4.0+:报告已合并到脚本中。旧版报告日志签名
(obj, message)仍可使用,但已弃用——请使用(message, obj)。
反模式
| 错误 | 后果 | 修正方法 |
|---|---|---|
跳过 full_clean() | 数据完整性违规 | 保存前始终进行验证 |
跳过 snapshot() | 更改日志不显示差异 | 修改现有对象前调用 |
以为 log_failure 会停止执行 | 脚本会继续运行 | 使用 AbortScript 停止 |
| 脚本运行时间过长 | 因超时被终止 | 设置 Meta.job_timeout |
导入 from netbox.dcim.models | ImportError | 使用 from dcim.models import ... |
认为 FileVar 会持久化 | 文件仅在执行期间可用 | 在 run() 中处理/复制文件 |
在命令行中依赖 self.request.user | 获取到 NetBoxFakeRequest | 妥善处理 |
| 模块名称冲突 | 导入冲突 | 避免将脚本命名为已安装软件包的名称 |
| 使用只读令牌通过 REST 运行 (4.7) | HTTP 403 | 使用启用了写入权限的令牌 |
对 obj.custom_fields 调用 .filter() (4.7) | AttributeError — 它是一个列表 | 使用列表推导式 |
使用 Region.objects.filter(level=0) / get_root() (4.7) | FieldError / AttributeError — ltree | 使用 parent__isnull=True;get_ancestors() |
| 期望全局搜索能找到刚创建的对象 (4.7) | 索引由后台作业更新 | 查询 ORM,而不是搜索索引 |
脚本文件管理
- 脚本是位于
SCRIPTS_ROOT中的 Python 文件(默认路径:$INSTALL/netbox/scripts/) - 可通过 UI(Admin > Custom Scripts)上传,也可直接放置文件
- 每个文件都会成为一个
ScriptModule;系统会自动发现其中的类 - 如果从文件中移除某个类,且存在作业历史记录,则其数据库记录会被软删除(
is_executable=False) - 存储后端可通过 Django
STORAGES配置(支持 S3) - 脚本也可以从远程 DataSource(git/S3)同步;在 NetBox 4.6.2+ 中,远程来源脚本会在同步时进行验证,因此格式错误的脚本会在同步时被发现,而不是等到首次执行时才发现
- 通过 REST 上传:
POST /api/extras/scripts/upload/(multipart/form-data,[email protected]);在 4.6.5+ 中,可通过PUT/PATCH向/api/extras/scripts/upload/<module>.py/原位替换现有模块
版本说明
NetBox 4.7(2026-09-02)
- 核心自定义脚本已弃用(在 4.7 和 4.8 中继续支持,5.0 中移除),改用
netbox-scripts插件;现有脚本仍可正常运行,迁移计划自动完成。截至 2026 年 9 月,插件处于 alpha 阶段。 - 通过 REST 执行需要具有写入权限的令牌(否则返回 403)。4.7.1:REST 输入会根据声明的变量进行验证;对象 ID 会解析为实例。
- 作业 REST 表示中新增
Job.execution_time(只读时长)。 - REST/GraphQL 中,选择/多选自定义字段的值为
{value, label}对象;ORM 中的obj.custom_field_data保持不变(原始值)。 CustomField.objects.get_for_model()/obj.custom_fields返回有效字段列表,而不是 queryset。当批量创建带默认值的自定义字段或删除自定义字段的数量超过BULK_UPDATE_CHUNK_SIZE时,操作会推迟到后台作业执行;此类字段的status为provisioning/deleting,完成前不会出现在get_for_model()的结果中。- 分层模型从 MPTT 迁移到 PostgreSQL
ltree:level不再可用于筛选或排序;get_root()、get_family()、is_leaf_node()、move_to()、insert_at()已移除。 - 全局搜索索引更新改为由后台作业执行(创建/更新后会有短暂延迟)。
- 升级前提:PostgreSQL 15+、Redis 6.0+、
ltree扩展。建议使用 4.7.3+(4.7.0/4.7.1 在作业结果中记录了通过后台批量请求创建的明文 API 令牌;4.7.3 增加了可安全处理分支的后台写入,以及 config-context omit 优化)。
NetBox 4.6(2026-05-05)
- 新增
notifications执行参数(always/on_failure/never)和Meta.notifications_default;4.6.2 可防止重复创建计划的后台作业。 - 新增
ObjectVar(quick_add=True)(4.6.2);远程来源脚本会在同步时进行验证(4.6.2);可通过 RESTPUT/PATCH替换已上传的脚本模块(4.6.5)。