netbox-custom-scripts について
netbox-custom-scripts は、NetBox 内で動作する Python スクリプトの作成とデバッグを支援するスキルです。変数、ORM の変更、検証、バックグラウンドジョブをバージョン別に案内します。
主な機能
- 01スクリプトクラスとフォーム変数の説明
- 02ORM 検証と変更ログの解説
- 03トランザクション、中断、ジョブ実行の説明
- 04バージョン固有の API と ORM 変更の解説
- 05実装とデバッグのガイダンス
活用シーン
- 01インベントリーを変更する NetBox スクリプトの作成
- 02スクリプト変数や実行エラーの調査
- 03モデル変更を保存する前のデータ検証
- 04NetBox のバージョン変更に合わせたスクリプト調整
インストール
コマンドをコピーしてターミナルで実行します。
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 月時点で、このプラグインはアルファ版(v0.0.1、NetBox 4.7 のみ対応)であり、本番環境での使用は推奨されません。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 以降、アルファ版) | 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')),
)
モジュールレベルでの順序指定
1 つのファイルに複数のスクリプトがある場合の表示順を制御します。
script_order = (ScriptA, ScriptB, ScriptC)
フォーム変数
すべての変数が次の項目に対応しています:label、description、default、required(デフォルトは True)、widget。
| 変数 | 主なオプション | 備考 |
|---|---|---|
StringVar | min_length、max_length、regex | 1 行テキスト |
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'})
UI では、親の選択内容に応じて各ドロップダウンが動的にフィルタリングされます。
ORM アクセスと変更ログ
スクリプトからは Django ORM に完全にアクセスできます。次の 3 つの重要なルールがあります。
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']は引き続き保存時の 生の値 です。変更されたのは 4.7 の REST/GraphQL 表現のみです(選択フィールドは{"value": ..., "label": ...}を返します)。ORM から辞書が返されることは想定しないでください。 CustomField.objects.get_for_model()とobj.custom_fieldsは、4.7 ではリストを返します(有効なフィールドのみ)。4.5/4.6 ではクエリーセットです。これらに.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()の完了後、すべての DB 変更がロールバックされます(ドライランモード)- 例外が発生した場合:
commitの設定にかかわらず、すべての変更がロールバックされます - スクリプトの一部だけをコミットすることはできません — すべてコミットするか、すべてロールバックするかのどちらかです
正常な中断
from utilities.exceptions import AbortScript
if critical_error:
raise AbortScript("Clear error message")
AbortScript はメッセージを失敗として記録し、すべての変更をロールバックします。出力にスタックトレースは表示されません。
log_failure は中断しません
self.log_failure() を呼び出すと self.failed = True が設定されます(UI に警告が表示されます)が、実行は停止しません。実際に処理を停止するには AbortScript を使用してください。
実行モデル
スクリプトは Django-RQ を介してバックグラウンドジョブとして実行されます。
- UI/API からの送信 → ジョブが Redis にキューイングされる → RQ ワーカーが実行
- CLI:
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.models からインポートする | ImportError | from dcim.models import ... を使う |
FileVar が保持されると想定する | ファイルは実行中のみ利用可能 | run() でファイルを処理/コピーする |
CLI で 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(デフォルト:$INSTALL/netbox/scripts/)に置かれた Python ファイルです - UI(Admin > Custom Scripts)からアップロードするか、ファイルを直接配置します
- 各ファイルは
ScriptModuleになり、クラスは自動的に検出されます - ファイルからクラスを削除すると、ジョブ履歴がある場合はその DB レコードがソフト削除されます(
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)
netbox-scriptsプラグインを優先し、コアのカスタムスクリプトは非推奨になりました(4.7 および 4.8 までサポートされ、5.0 で削除されます)。既存のスクリプトは引き続き動作し、移行は自動化される予定です。2026 年 9 月時点で、プラグインはアルファ版です。- REST での実行には書き込み可能なトークンが必要です(それ以外の場合は 403)。4.7.1:REST 入力が宣言済み変数に対して検証され、オブジェクト ID はインスタンスに解決されます。
Job.execution_time(読み取り専用の所要時間)がジョブの REST 表現に追加されました。- 選択/複数選択のカスタムフィールド値は、REST/GraphQL では
{value, label}オブジェクトになります。ORM のobj.custom_field_dataは変更されず(生の値のまま)です。 CustomField.objects.get_for_model()/obj.custom_fieldsは、クエリセットではなく有効なフィールドのリストを返します。デフォルト値を指定したカスタムフィールドの作成と削除は、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)。