mcpField
N
netbox-custom-scripts

netbox-custom-scripts

github.com/netboxlabs开发2151 次安装2 周前更新
代码仓库

关于 netbox-custom-scripts

netbox-custom-scripts 是用于编写和调试 NetBox 内部 Python 脚本的技能,提供有关变量、ORM 变更、数据校验和后台作业的版本适配指引。

主要功能

  1. 01介绍脚本类和表单变量
  2. 02说明 ORM 校验与变更日志
  3. 03讲解事务、终止操作和作业执行
  4. 04描述特定版本的 API 与 ORM 变化
  5. 05提供脚本编写和调试指引

适用场景

  1. 01编写用于变更库存数据的 NetBox 脚本
  2. 02排查脚本变量或执行故障
  3. 03保存模型变更前校验数据
  4. 04根据 NetBox 版本调整脚本

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/netboxlabs/skills --skill netbox-custom-scripts

安装后在 Claude Code 中输入/netbox-custom-scripts确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
execution-model.md
orm-patterns.md
script-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_defaultTrue提交复选框的默认状态
scheduling_enabledTrue允许按计划或定期执行
notifications_default (4.6+)'always'作业的初始通知策略:always、on_failure 或 never。从事件规则或 runscript 运行时也会使用此策略
job_timeoutRQ_DEFAULT_TIMEOUT以秒为单位的最长运行时间
field_order声明顺序字段名称元组
fieldsetsNone分组字段布局(优先于 field_order)
字段集

在 UI 中将相关字段分组:

class Meta:
    fieldsets = (
        ('Source', ('region', 'site')),
        ('Configuration', ('vlan_id', 'description')),
    )
模块级排序

控制文件包含多个脚本时的显示顺序:

script_order = (ScriptA, ScriptB, ScriptC)

表单变量

所有变量都支持:label、description、default、required(默认值为 True)、widget。

变量关键选项说明
StringVarmin_length、max_length、regex单行文本
TextVar—多行文本区域
IntegerVarmin_value、max_value
BooleanVar—内部始终为可选项
ChoiceVarchoices(由 (value, label) 组成的列表)自动添加空白选项
MultiChoiceVarchoices多选
ObjectVarmodel、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)
IPNetworkVarmin_prefix_length、max_prefix_length网络前缀
DateVar / DateTimeVar—日期/时间选择器
DecimalVarmin_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,未声明的键会被丢弃),并且 ObjectVar ID 会在 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.modelsImportError使用 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);可通过 REST PUT/PATCH 替换已上传的脚本模块(4.6.5)。

评论

支持 Markdown