Integrate NetBox UI with plugins catalog
NetBox version
v3.7.0
Feature type
New functionality
Proposed functionality
Provide an interface for users to compare their installed plugins against the public database, and potentially to discover new plugins
Use case
Providing a single interface where NetBox administrators can compare the installed version of each plugin to the latest available greatly simplifies the task of keeping plugins up-to-date.
Database changes
This can probably be done without introducing any new models, as plugin details live in memory. Some caching may be needed.
External dependencies
TBD, but no new dependencies are expected.
Blocked by #14728. We'll also likely need to build out a full API for the plugins database to support this.
Could this be done on client side? In our case (and I'm sure it's the same for many others), the Netbox infrastructure does not have access to internet...
For this to work properly (and for plugin authors to know how to make sure the information in the plugin database is correct) it would really be helpful to have some documentation on how the database is created. Additionally, the database needs to be kept in a more current state if it's supposed to be useful.
Example: In the case of NetBox DNS (netbox-plugin-dns), the last version according to the database is 0.18.2, which is almost 9 months old, and the development status is "unknown". The current version is 0.22.1, which was released last week, and the development status is, as far as I'm concerned, "supported" ... on the other hand, the old netbox-dns is still in there, whose status is definitely "abandoned" (the last commit is almost a year old, and there will be no new ones, as the project was forked by me due to the sudden demise of its former maintainers).
Details of the public plugin catalog endpoint
URL: https://api.netbox.oss.netboxlabs.com//v1/plugins
Allowed methods: GET
Allowed query parameters:
page: Page number to fetchper_page: Number of entries per page
At the time of this comment, it appears the endpoint expects an authentication token, which I assume will be removed soon.
Here is a sample of the endpoint's output.
{
"metadata": {
"duration": 1,
"pagination": {
"current_page": 1,
"first_page": true,
"last_page": true,
"page_size": 50,
"total_items": 2,
"total_pages": 1
}
},
"data": [
{
"id": "77525ee1-838e-4b99-9d0a-ff3d891a9049",
"status": "active",
"title_short": "Netbox plugin for BGP related objects documentation.",
"title_long": "Model & manage BGP related resources like communities, sessions, routing policies, and IP prefix lists",
"tag_line": "BGP related objects",
"description_short": "Netbox plugin for BGP related objects documentation.",
"slug": "netbox-bgp",
"author": {
"name": "Nikolay Yuzefovich",
"org_id": "org-00000000",
"url": "https://github.com/netbox-community/netbox-bgp"
},
"created_at": "2023-10-24T22:24:59.244147Z",
"updated_at": "2024-04-17T17:55:16.837377Z",
"license_type": "Apache 2.0",
"homepage_url": "https://github.com/netbox-community/netbox-bgp",
"package_name_pypi": "netbox-bgp",
"config_name": "netbox_bgp",
"is_certified": false,
"release_count": 2,
"release_latest": {
"date": "2024-02-26T19:46:31.962084Z",
"version": "0.12.1",
"netbox_min_version": "3.7.3",
"netbox_max_version": "3.7.8",
"has_model": true,
"is_certified": true,
"is_feature": false,
"is_integration": false,
"is_netboxlabs_supported": true
},
"release_recent_history": [
{
"date": "2024-02-26T19:46:31.962084Z",
"version": "0.12.1",
"netbox_min_version": "3.7.3",
"netbox_max_version": "3.7.8",
"has_model": true,
"is_certified": true,
"is_feature": false,
"is_integration": false,
"is_netboxlabs_supported": true
},
{
"date": "2024-01-18T15:15:03.870601Z",
"version": "0.12.0",
"netbox_min_version": "3.7.0",
"netbox_max_version": "3.7.2",
"has_model": true,
"is_certified": false,
"is_feature": false,
"is_integration": false,
"is_netboxlabs_supported": true
}
]
},
{
"id": "4c107c65-59d8-4197-9a26-9eb2a29a742a",
"status": "active",
"title_short": "Automatically discover your network with Slurp’it",
"title_long": "Automatically discover your network with Slurp’it",
"tag_line": "Automatically discover your network with Slurp’it",
"description_short": "Automatically discover your network with Slurp’it",
"slug": "slurpit_netbox",
"author": {
"name": "Pieter van Os",
"org_id": "org-00000000",
"url": "https://gitlab.com/slurpit.io/slurpit-netbox"
},
"created_at": "2024-01-31T15:31:37.247368Z",
"updated_at": "2024-04-19T14:23:33.598421Z",
"license_type": "MIT",
"homepage_url": "https://gitlab.com/slurpit.io/slurpit-netbox",
"package_name_pypi": "slurpit_netbox",
"config_name": "slurpit_netbox",
"is_certified": true,
"release_count": 1,
"release_latest": {
"date": "2024-04-18T12:15:52.308399Z",
"version": "0.8.117",
"netbox_min_version": "3.7.5",
"netbox_max_version": "3.7.8",
"has_model": true,
"is_certified": true,
"is_feature": false,
"is_integration": true,
"is_netboxlabs_supported": false
},
"release_recent_history": [
{
"date": "2024-04-18T12:15:52.308399Z",
"version": "0.8.117",
"netbox_min_version": "3.7.5",
"netbox_max_version": "3.7.8",
"has_model": true,
"is_certified": true,
"is_feature": false,
"is_integration": true,
"is_netboxlabs_supported": false
}
]
}
]
}
~~Roughed-out~~ Proper pydantic-generated json-schema description for this endpoint:
{
"$defs": {
"Author": {
"description": "Author model.",
"properties": {
"name": {
"description": "Name",
"title": "Name",
"type": "string"
},
"org_id": {
"description": "Organization ID",
"title": "Org Id",
"type": "string"
},
"url": {
"description": "Author URL",
"format": "uri",
"minLength": 1,
"title": "Url",
"type": "string"
}
},
"required": [
"name",
"org_id",
"url"
],
"title": "Author",
"type": "object"
},
"LicenseType": {
"description": "License type model.",
"enum": [
"No license",
"MIT",
"Apache 2.0",
"BSD-3-Clause",
"BSD-2-Clause",
"CDDL-1.0",
"LGPLv3",
"GPLv3",
"MPL-2.0",
"EPL-2.0"
],
"title": "LicenseType",
"type": "string"
},
"Metadata": {
"description": "Metadata model.",
"properties": {
"duration": {
"default": 0,
"description": "Duration",
"title": "Duration",
"type": "integer"
},
"pagination": {
"allOf": [
{
"$ref": "#/$defs/Pagination"
}
],
"description": "Pagination object"
}
},
"required": [
"pagination"
],
"title": "Metadata",
"type": "object"
},
"Pagination": {
"description": "Pagination model.",
"properties": {
"current_page": {
"default": 1,
"description": "Current page",
"title": "Current Page",
"type": "integer"
},
"first_page": {
"description": "First page",
"title": "First Page",
"type": "boolean"
},
"last_page": {
"description": "Last page",
"title": "Last Page",
"type": "boolean"
},
"page_size": {
"description": "Page size",
"title": "Page Size",
"type": "integer"
},
"total_items": {
"default": 0,
"description": "Total items",
"title": "Total Items",
"type": "integer"
},
"total_pages": {
"default": 0,
"description": "Total pages",
"title": "Total Pages",
"type": "integer"
}
},
"required": [
"first_page",
"last_page",
"page_size"
],
"title": "Pagination",
"type": "object"
},
"Plugin": {
"description": "Plugin model.",
"properties": {
"id": {
"description": "Plugin ID",
"format": "uuid4",
"title": "Id",
"type": "string"
},
"status": {
"allOf": [
{
"$ref": "#/$defs/PluginStatus"
}
],
"description": "Plugin status"
},
"title_short": {
"description": "Short title",
"title": "Title Short",
"type": "string"
},
"title_long": {
"description": "Long title",
"title": "Title Long",
"type": "string"
},
"tag_line": {
"description": "Tag line",
"title": "Tag Line",
"type": "string"
},
"description_short": {
"description": "Short description",
"title": "Description Short",
"type": "string"
},
"slug": {
"description": "Slug",
"title": "Slug",
"type": "string"
},
"author": {
"allOf": [
{
"$ref": "#/$defs/Author"
}
],
"description": "Author"
},
"created_at": {
"description": "Created at",
"format": "date-time",
"title": "Created At",
"type": "string"
},
"updated_at": {
"description": "Updated at",
"format": "date-time",
"title": "Updated At",
"type": "string"
},
"license_type": {
"allOf": [
{
"$ref": "#/$defs/LicenseType"
}
],
"description": "License type"
},
"homepage_url": {
"description": "Homepage URL",
"format": "uri",
"minLength": 1,
"title": "Homepage Url",
"type": "string"
},
"package_name_pypi": {
"description": "Package name (PyPI)",
"title": "Package Name Pypi",
"type": "string"
},
"config_name": {
"description": "Configuration name",
"title": "Config Name",
"type": "string"
},
"is_certified": {
"description": "Certified",
"title": "Is Certified",
"type": "boolean"
},
"release_count": {
"default": 0,
"description": "Release count",
"title": "Release Count",
"type": "integer"
},
"release_latest": {
"allOf": [
{
"$ref": "#/$defs/Release"
}
],
"default": null,
"description": "Latest release"
},
"release_recent_history": {
"default": [],
"description": "Recent releases",
"items": {
"$ref": "#/$defs/Release"
},
"title": "Release Recent History",
"type": "array"
}
},
"required": [
"id",
"status",
"title_short",
"title_long",
"tag_line",
"description_short",
"slug",
"author",
"created_at",
"updated_at",
"license_type",
"homepage_url",
"package_name_pypi",
"config_name",
"is_certified"
],
"title": "Plugin",
"type": "object"
},
"PluginStatus": {
"const": "active",
"description": "Plugin status model.",
"enum": [
"active"
],
"title": "PluginStatus",
"type": "string"
},
"Release": {
"description": "Release model.",
"properties": {
"id": {
"description": "Release ID",
"format": "uuid4",
"title": "Id",
"type": "string"
},
"date": {
"description": "Date",
"format": "date-time",
"title": "Date",
"type": "string"
},
"version": {
"description": "Version",
"title": "Version",
"type": "string"
},
"netbox_min_version": {
"description": "NetBox minimum version",
"title": "Netbox Min Version",
"type": "string"
},
"netbox_max_version": {
"description": "NetBox maximum version",
"title": "Netbox Max Version",
"type": "string"
},
"has_model": {
"description": "Has model",
"title": "Has Model",
"type": "boolean"
},
"is_certified": {
"description": "Is certified",
"title": "Is Certified",
"type": "boolean"
},
"is_feature": {
"description": "Is feature",
"title": "Is Feature",
"type": "boolean"
},
"is_integration": {
"description": "Is integration",
"title": "Is Integration",
"type": "boolean"
},
"is_netboxlabs_supported": {
"description": "Is NetBox Labs supported",
"title": "Is Netboxlabs Supported",
"type": "boolean"
}
},
"required": [
"id",
"date",
"version",
"netbox_min_version",
"netbox_max_version",
"has_model",
"is_certified",
"is_feature",
"is_integration",
"is_netboxlabs_supported"
],
"title": "Release",
"type": "object"
}
},
"description": "List response model.",
"properties": {
"metadata": {
"allOf": [
{
"$ref": "#/$defs/Metadata"
}
],
"description": "Metadata object"
},
"data": {
"description": "Plugins",
"items": {
"$ref": "#/$defs/Plugin"
},
"title": "Data",
"type": "array"
}
},
"required": [
"metadata",
"data"
],
"title": "PluginListResponse",
"type": "object"
}
Prototype of list view:
(edit: Added a made up locally-installed plugin Contrail Sync not listed in the catalog)
Additional prototypes for per-plugin detail view. I'm having trouble getting clickable prototype sharing to work so I'm just dropping static images here for the time being.
ACLs plugin – certified, not installed
Contrail Sync plugin – made up, installed locally, not in catalog
DNS plugin – certified, installed, new version available (pretend version history tab shows this)