Skip to content

NetBox Branching

NetBox is the world's leading source of truth for network infrastructure, featuring an extensive and complex data model. But sometimes it can be challenging to orchestrate changes, especially when working within a large team. This plugin introduces a new paradigm for NetBox to help overcome these challenges: branching.

If you're familiar with git or similar version control systems, the concept of branching should be familiar. Essentially, this plugin allows you to make copies of NetBox's data model and alter them independently. Your changes will be reflected only within the branch you're working on, until you decide to merge your branch into the main data model.

This allows you and your colleagues to stage changes within isolated environments and avoid interfering with one another's work or pushing changes to the network prematurely. Each branch can be synchronized as needed to keep up to date with external changes, and merged when needed.

Features

  • Users can create new branches and switch between them seamlessly while navigating the web UI.

  • Each branch exists in isolation from its peers: Changes made within one branch won't affect any other branches.

  • Standard NetBox permissions are employed to control which users can perform branch operations.

  • Branches can be created, synchronized, merged, reverted, and deleted through the REST API.

  • No external dependencies! This plugin requires only NetBox v4.1 or later and a conventional PostgreSQL database (v12.0 or later).

Terminology

  • Main is shorthand for the primary NetBox state. Any changes made outside the context of a specific branch are made here.

  • The creation, modification, or deletion of an object is a change.

  • A branch is an independent copy of the NetBox data model which diverges from main at a set point in time. Any changes to main after that time will not be reflected in the branch. Likewise, changes made within the branch will not be reflected in main.

  • Branches are provisioned automatically upon creation. The initial state of a branch is identical to the state of main at the time it was provisioned.

  • Changes in main can be synchronized at any time into a branch. Branches are independent of one another: Changes must be synchronized into each branch individually. This ensures complete isolation among branches.

  • Once the work within a branch has been completed, it can be merged into main. Once a branch has been merged, it is generally no longer used.

  • Merged changes can be reverted provided the branch has not yet been deleted. This effectively replays the changes in reverse order to undo the relevant changes.

Workflow

The first step is to create a new branch. Upon creation, a background job is automatically queued to provision a dedicated PostgreSQL schema for the branch. When provisioning is complete, the branch's status is updated to "ready."

Users can now activate the branch and begin making changes within it. These changes will be contained to the branch, and will not impact main. Likewise, any changes to main will not be reflected in the branch until it has been synchronized by a user. A branch may be synchronized repeatedly to keep it up to date with main over time.

Once work in the branch has been completed, it can be merged into main.

sequenceDiagram
    actor User B
    participant Main
    participant Branch
    actor User A
    Main->>Branch: Provision new branch
    User A->>Branch: Make changes
    User B->>Main: Make unrelated changes
    Main->>Branch: Synchronize changes
    User A->>Branch: Make more changes
    Branch->>Main: Merge branch

In the event a branch should not have been merged, it can be reverted. Previously merged changes to main will be unwound and the branch will be restored to its pre-merge state. The branch is again marked as ready for additional changes, if needed, and can be merged again.

sequenceDiagram
    participant Main
    participant Branch
    actor User A
    Main->>Branch: Provision new branch
    User A->>Branch: Make changes
    Branch->>Main: Merge branch
    Note left of Main: Error detected!
    Main->>Branch: Revert changes
    User A->>Branch: Correct error
    Branch->>Main: Merge branch

Getting Started

Database Preparation

Before installing this plugin, ensure that the PostgreSQL user as which NetBox authenticates has permission to create new schemas in the database. This can be achieved by issuing the following command in the PostgreSQL shell (substituting $database and $user with their respective values):

GRANT CREATE ON DATABASE $database TO $user;

Plugin Installation

1. Virtual Environment

The plugin can be installed from PyPI. First, activate the Python virtual environment used by NetBox (which is typically located at /opt/netbox/venv/):

source /opt/netbox/venv/bin/activate

Note

You may need to modify the source command above if your virtual environment has been installed in a different location.

2. Python Package

Use pip to install the Python package:

pip install netboxlabs-netbox-branching

3. Enable Plugin

Add netbox_branching to the end of the PLUGINS list in configuration.py.

PLUGINS = [
    # ...
    'netbox_branching',
]

Warning

netbox_branching must be the last (or only) plugin in the list. Branching support will not be registered for models provided by any plugin appearing later in the list.

Note

If there are no plugins already installed, you might need to create this parameter. If so, be sure to define PLUGINS as a list containing the plugin name as above, rather than just the name.

4. Configuration

This plugin employs dynamic schema resolution, which requires that we override two low-level Django settings. First, we'll wrap NetBox's configured DATABASE parameter with DynamicSchemaDict to support dynamic schemas. Second, we'll employ the plugin's custom database router.

Create a new file named local_settings.py in the same directory as settings.py, and add the content below.

from netbox_branching.utilities import DynamicSchemaDict
from .configuration import DATABASE

# Wrap DATABASES with DynamicSchemaDict for dynamic schema support
DATABASES = DynamicSchemaDict({
    'default': DATABASE,
})

# Employ our custom database router
DATABASE_ROUTERS = [
    'netbox_branching.database.BranchAwareRouter',
]

5. Database Migrations

Run the included database migrations:

cd /opt/netbox/netbox
./manage.py migrate

Known Limitations

There are currently a few limitations to the functionality provided by this plugin that are worth highlighting. We hope to address these in future releases.

  • Branches may not persist across minor version upgrades of NetBox. Users are strongly encouraged to merge or remove all open branches prior to upgrading to a new minor release of NetBox (e.g. from v4.1 to v4.2). This is because database migrations introduced by the upgrade will not be applied to branch schemas, potentially resulting in an invalid state. However, it should be considered safe to upgrade to new patch releases (e.g. v4.1.0 to v4.1.1) with open branches.

  • Open branches will not reflect newly installed plugins. Any branches created before installing a new plugin will not be updated to support its models. Note, however, that installing a new plugin will generally not impede the use of existing branches. Users are encouraged to install all necessary plugins prior to creating branches. (This also applies to database migrations introduced by upgrading a plugin.)