From c49f0e815f230715dc530c144b329e14e29d9d26 Mon Sep 17 00:00:00 2001 From: Shawn Williams Date: Tue, 6 Oct 2026 15:26:13 -0400 Subject: [PATCH 1/7] Add NetBox plugin and dashboards Introduces a new NetBox v1 data source with authentication, indexed object types, and a wide set of REST/GraphQL-backed data streams for sites, racks, devices, VMs, prefixes, circuits, power, and change history. This also adds default dashboards, docs, config validation, custom types, and pre-request token handling for NetBox v1/v2 APIs. --- plugins/NetBox/v1/configValidation.json | 18 + plugins/NetBox/v1/custom_types.json | 44 + plugins/NetBox/v1/dataStreams/circuits.json | 150 +++ .../v1/dataStreams/deviceInterfaces.json | 205 +++ .../NetBox/v1/dataStreams/deviceRoles.json | 63 + plugins/NetBox/v1/dataStreams/devices.json | 283 ++++ .../NetBox/v1/dataStreams/graphqlQuery.json | 46 + .../NetBox/v1/dataStreams/ipAddresses.json | 276 ++++ .../NetBox/v1/dataStreams/objectChanges.json | 209 +++ plugins/NetBox/v1/dataStreams/powerFeeds.json | 222 ++++ .../v1/dataStreams/prefixUtilization.json | 133 ++ plugins/NetBox/v1/dataStreams/prefixes.json | 175 +++ .../NetBox/v1/dataStreams/rackElevation.json | 171 +++ .../v1/dataStreams/rackUtilization.json | 150 +++ plugins/NetBox/v1/dataStreams/racks.json | 181 +++ .../v1/dataStreams/scripts/deviceRoles.js | 4 + .../scripts/errorHandling/graphqlQuery.js | 5 + .../v1/dataStreams/scripts/graphqlQuery.js | 81 ++ .../v1/dataStreams/scripts/powerFeeds.js | 6 + .../dataStreams/scripts/prefixUtilization.js | 95 ++ .../v1/dataStreams/scripts/rackElevation.js | 123 ++ .../v1/dataStreams/scripts/rackUtilization.js | 60 + plugins/NetBox/v1/dataStreams/sites.json | 168 +++ plugins/NetBox/v1/dataStreams/status.json | 60 + .../v1/dataStreams/virtualMachines.json | 181 +++ plugins/NetBox/v1/dataStreams/vlanGroups.json | 147 +++ .../defaultContent/changeActivity.dash.json | 677 ++++++++++ .../v1/defaultContent/circuit.dash.json | 179 +++ .../v1/defaultContent/dataCenters.dash.json | 771 +++++++++++ .../v1/defaultContent/dataQuality.dash.json | 1101 ++++++++++++++++ .../NetBox/v1/defaultContent/device.dash.json | 486 +++++++ .../defaultContent/deviceInventory.dash.json | 457 +++++++ .../v1/defaultContent/ipamCapacity.dash.json | 680 ++++++++++ .../lifecycleAndCircuits.dash.json | 972 ++++++++++++++ .../NetBox/v1/defaultContent/manifest.json | 64 + .../v1/defaultContent/overview.dash.json | 897 +++++++++++++ .../NetBox/v1/defaultContent/power.dash.json | 606 +++++++++ .../NetBox/v1/defaultContent/prefix.dash.json | 471 +++++++ .../NetBox/v1/defaultContent/rack.dash.json | 652 ++++++++++ .../v1/defaultContent/rackCapacity.dash.json | 569 +++++++++ plugins/NetBox/v1/defaultContent/scopes.json | 104 ++ .../NetBox/v1/defaultContent/site.dash.json | 1135 +++++++++++++++++ .../defaultContent/virtualMachine.dash.json | 313 +++++ plugins/NetBox/v1/docs/README.md | 45 + plugins/NetBox/v1/icon.svg | 23 + .../NetBox/v1/indexDefinitions/default.json | 265 ++++ plugins/NetBox/v1/metadata.json | 85 ++ plugins/NetBox/v1/preRequest.js | 16 + plugins/NetBox/v1/ui.json | 72 ++ 49 files changed, 13896 insertions(+) create mode 100644 plugins/NetBox/v1/configValidation.json create mode 100644 plugins/NetBox/v1/custom_types.json create mode 100644 plugins/NetBox/v1/dataStreams/circuits.json create mode 100644 plugins/NetBox/v1/dataStreams/deviceInterfaces.json create mode 100644 plugins/NetBox/v1/dataStreams/deviceRoles.json create mode 100644 plugins/NetBox/v1/dataStreams/devices.json create mode 100644 plugins/NetBox/v1/dataStreams/graphqlQuery.json create mode 100644 plugins/NetBox/v1/dataStreams/ipAddresses.json create mode 100644 plugins/NetBox/v1/dataStreams/objectChanges.json create mode 100644 plugins/NetBox/v1/dataStreams/powerFeeds.json create mode 100644 plugins/NetBox/v1/dataStreams/prefixUtilization.json create mode 100644 plugins/NetBox/v1/dataStreams/prefixes.json create mode 100644 plugins/NetBox/v1/dataStreams/rackElevation.json create mode 100644 plugins/NetBox/v1/dataStreams/rackUtilization.json create mode 100644 plugins/NetBox/v1/dataStreams/racks.json create mode 100644 plugins/NetBox/v1/dataStreams/scripts/deviceRoles.js create mode 100644 plugins/NetBox/v1/dataStreams/scripts/errorHandling/graphqlQuery.js create mode 100644 plugins/NetBox/v1/dataStreams/scripts/graphqlQuery.js create mode 100644 plugins/NetBox/v1/dataStreams/scripts/powerFeeds.js create mode 100644 plugins/NetBox/v1/dataStreams/scripts/prefixUtilization.js create mode 100644 plugins/NetBox/v1/dataStreams/scripts/rackElevation.js create mode 100644 plugins/NetBox/v1/dataStreams/scripts/rackUtilization.js create mode 100644 plugins/NetBox/v1/dataStreams/sites.json create mode 100644 plugins/NetBox/v1/dataStreams/status.json create mode 100644 plugins/NetBox/v1/dataStreams/virtualMachines.json create mode 100644 plugins/NetBox/v1/dataStreams/vlanGroups.json create mode 100644 plugins/NetBox/v1/defaultContent/changeActivity.dash.json create mode 100644 plugins/NetBox/v1/defaultContent/circuit.dash.json create mode 100644 plugins/NetBox/v1/defaultContent/dataCenters.dash.json create mode 100644 plugins/NetBox/v1/defaultContent/dataQuality.dash.json create mode 100644 plugins/NetBox/v1/defaultContent/device.dash.json create mode 100644 plugins/NetBox/v1/defaultContent/deviceInventory.dash.json create mode 100644 plugins/NetBox/v1/defaultContent/ipamCapacity.dash.json create mode 100644 plugins/NetBox/v1/defaultContent/lifecycleAndCircuits.dash.json create mode 100644 plugins/NetBox/v1/defaultContent/manifest.json create mode 100644 plugins/NetBox/v1/defaultContent/overview.dash.json create mode 100644 plugins/NetBox/v1/defaultContent/power.dash.json create mode 100644 plugins/NetBox/v1/defaultContent/prefix.dash.json create mode 100644 plugins/NetBox/v1/defaultContent/rack.dash.json create mode 100644 plugins/NetBox/v1/defaultContent/rackCapacity.dash.json create mode 100644 plugins/NetBox/v1/defaultContent/scopes.json create mode 100644 plugins/NetBox/v1/defaultContent/site.dash.json create mode 100644 plugins/NetBox/v1/defaultContent/virtualMachine.dash.json create mode 100644 plugins/NetBox/v1/docs/README.md create mode 100644 plugins/NetBox/v1/icon.svg create mode 100644 plugins/NetBox/v1/indexDefinitions/default.json create mode 100644 plugins/NetBox/v1/metadata.json create mode 100644 plugins/NetBox/v1/preRequest.js create mode 100644 plugins/NetBox/v1/ui.json diff --git a/plugins/NetBox/v1/configValidation.json b/plugins/NetBox/v1/configValidation.json new file mode 100644 index 00000000..9371c98c --- /dev/null +++ b/plugins/NetBox/v1/configValidation.json @@ -0,0 +1,18 @@ +{ + "steps": [ + { + "displayName": "Authenticate", + "dataStream": { "name": "status" }, + "required": true, + "error": "Could not connect to NetBox. Check the NetBox URL is reachable, the API token is valid and enabled, and that any allowed IP restriction on the token permits SquaredUp.", + "success": "Connected to NetBox successfully." + }, + { + "displayName": "Check data access", + "dataStream": { "name": "sites" }, + "required": false, + "error": "Connected, but no sites were returned. Check the token's user has view permission on DCIM objects.", + "success": "Sites are accessible." + } + ] +} diff --git a/plugins/NetBox/v1/custom_types.json b/plugins/NetBox/v1/custom_types.json new file mode 100644 index 00000000..7c3da58f --- /dev/null +++ b/plugins/NetBox/v1/custom_types.json @@ -0,0 +1,44 @@ +[ + { + "name": "Site", + "sourceType": "Site", + "icon": "building", + "singular": "Site", + "plural": "Sites" + }, + { + "name": "Rack", + "sourceType": "Rack", + "icon": "server", + "singular": "Rack", + "plural": "Racks" + }, + { + "name": "Device", + "sourceType": "Device", + "icon": "network-wired", + "singular": "Device", + "plural": "Devices" + }, + { + "name": "Virtual Machine", + "sourceType": "Virtual Machine", + "icon": "cube", + "singular": "Virtual Machine", + "plural": "Virtual Machines" + }, + { + "name": "Prefix", + "sourceType": "Prefix", + "icon": "sitemap", + "singular": "Prefix", + "plural": "Prefixes" + }, + { + "name": "Circuit", + "sourceType": "Circuit", + "icon": "route", + "singular": "Circuit", + "plural": "Circuits" + } +] diff --git a/plugins/NetBox/v1/dataStreams/circuits.json b/plugins/NetBox/v1/dataStreams/circuits.json new file mode 100644 index 00000000..5e4cce84 --- /dev/null +++ b/plugins/NetBox/v1/dataStreams/circuits.json @@ -0,0 +1,150 @@ +{ + "name": "circuits", + "displayName": "Circuits", + "description": "All circuits with provider, type, status, commit rate and contract dates", + "tags": [ + "Network", + "Inventory" + ], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "api/circuits/circuits/", + "getArgs": [ + { + "key": "fields", + "value": "id,cid,display,display_url,provider,provider_account,type,status,tenant,install_date,termination_date,commit_rate,description,termination_a,termination_z,created,last_updated,custom_fields" + } + ], + "paging": { + "mode": "offset", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "250" + }, + "offset": { + "mode": "row", + "rowCountIn": { + "realm": "payloadArraySize", + "path": "results" + }, + "base": 0 + }, + "out": { + "realm": "queryArg", + "path": "offset" + } + }, + "pathToData": "results", + "expandInnerObjects": true + }, + "matches": "none", + "metadata": [ + { + "name": "id", + "visible": false + }, + { + "name": "cid", + "displayName": "Circuit ID", + "shape": "string", + "role": "label" + }, + { + "name": "provider.name", + "displayName": "Provider", + "shape": "string" + }, + { + "name": "provider_account.name", + "displayName": "Provider Account", + "shape": "string" + }, + { + "name": "type.name", + "displayName": "Type", + "shape": "string" + }, + { + "name": "status.label", + "displayName": "Status", + "shape": "string" + }, + { + "name": "status.value", + "visible": false + }, + { + "name": "tenant.name", + "displayName": "Tenant", + "shape": "string" + }, + { + "name": "commit_rate", + "displayName": "Commit Rate", + "shape": "kilobitspersecond" + }, + { + "name": "termination_a.termination.display", + "displayName": "Termination A", + "shape": "string" + }, + { + "name": "termination_z.termination.display", + "displayName": "Termination Z", + "shape": "string" + }, + { + "name": "install_date", + "displayName": "Install Date", + "shape": "date" + }, + { + "name": "termination_date", + "displayName": "Termination Date", + "shape": "date" + }, + { + "name": "daysUntilTermination", + "displayName": "Days Until Termination", + "shape": "number", + "computed": true, + "valueExpression": "{{ typeof $['termination_date'] === 'string' && $['termination_date'] ? Math.ceil((new Date($['termination_date']).getTime() - Date.now()) / 86400000) : null }}" + }, + { + "name": "description", + "displayName": "Description", + "shape": "string" + }, + { + "name": "created", + "displayName": "Created", + "shape": "date" + }, + { + "name": "last_updated", + "displayName": "Last Updated", + "shape": "date" + }, + { + "name": "display_url", + "displayName": "Link", + "shape": [ + "url", + { + "label": "Open in NetBox" + } + ] + }, + { + "sourceId": "id", + "sourceType": "Circuit", + "name": "cid" + }, + { + "pattern": "^custom_fields[.].*" + } + ], + "timeframes": false +} diff --git a/plugins/NetBox/v1/dataStreams/deviceInterfaces.json b/plugins/NetBox/v1/dataStreams/deviceInterfaces.json new file mode 100644 index 00000000..2b89e30c --- /dev/null +++ b/plugins/NetBox/v1/dataStreams/deviceInterfaces.json @@ -0,0 +1,205 @@ +{ + "name": "deviceInterfaces", + "displayName": "Device Interfaces", + "description": "Interfaces on a device with type, state, speed, VLAN mode, cabling and connection", + "tags": [ + "Network", + "Inventory" + ], + "baseDataSourceName": "httpRequestScoped", + "config": { + "httpMethod": "get", + "endpointPath": "api/dcim/interfaces/", + "getArgs": [ + { + "key": "device_id", + "value": "{{ objects.map(o => o.rawId) }}" + }, + { + "key": "fields", + "value": "id,display,display_url,device,name,label,type,enabled,mgmt_only,mtu,speed,duplex,mode,mac_address,primary_mac_address,lag,parent,untagged_vlan,tagged_vlans,vrf,cable,mark_connected,link_peers,connected_endpoints,connected_endpoints_reachable,count_ipaddresses,description,last_updated" + } + ], + "paging": { + "mode": "offset", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "250" + }, + "offset": { + "mode": "row", + "base": 0, + "rowCountIn": { + "realm": "payloadArraySize", + "path": "results" + } + }, + "out": { + "realm": "queryArg", + "path": "offset" + } + }, + "pathToData": "results", + "expandInnerObjects": true + }, + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Device" + ] + } + }, + "metadata": [ + { + "name": "id", + "visible": false + }, + { + "pattern": "^(link_peers|connected_endpoints|tagged_vlans)[.].*", + "visible": false + }, + { + "name": "display", + "visible": false + }, + { + "name": "device.name", + "displayName": "Device", + "shape": "string" + }, + { + "name": "name", + "displayName": "Name", + "shape": "string", + "role": "label" + }, + { + "name": "label", + "displayName": "Label", + "shape": "string" + }, + { + "name": "type.label", + "displayName": "Type", + "shape": "string" + }, + { + "name": "enabled", + "displayName": "Enabled", + "shape": "boolean" + }, + { + "name": "mgmt_only", + "displayName": "Management Only", + "shape": "boolean" + }, + { + "name": "speed", + "displayName": "Speed", + "shape": "kilobitspersecond" + }, + { + "name": "mtu", + "displayName": "MTU", + "shape": "number" + }, + { + "name": "duplex.label", + "displayName": "Duplex", + "shape": "string" + }, + { + "name": "mode.label", + "displayName": "VLAN Mode", + "shape": "string" + }, + { + "name": "primary_mac_address.mac_address", + "visible": false + }, + { + "name": "mac_address", + "displayName": "MAC Address", + "shape": "string", + "valueExpression": "{{ typeof $['primary_mac_address.mac_address'] === 'string' && $['primary_mac_address.mac_address'] ? $['primary_mac_address.mac_address'] : (typeof $['mac_address'] === 'string' && $['mac_address'] ? $['mac_address'] : null) }}" + }, + { + "name": "lag.name", + "displayName": "LAG", + "shape": "string" + }, + { + "name": "parent.name", + "displayName": "Parent Interface", + "shape": "string" + }, + { + "name": "untagged_vlan.display", + "displayName": "Untagged VLAN", + "shape": "string" + }, + { + "name": "taggedVlanCount", + "displayName": "Tagged VLANs", + "shape": "number", + "computed": true, + "valueExpression": "{{ Object.keys($).filter(k => k.startsWith('tagged_vlans.') && k.endsWith('.id') && k.split('.').length === 3 && typeof $[k] === 'number').length }}" + }, + { + "name": "vrf.name", + "displayName": "VRF", + "shape": "string" + }, + { + "name": "cable.id", + "visible": false + }, + { + "name": "cabled", + "displayName": "Cabled", + "shape": "boolean", + "computed": true, + "valueExpression": "{{ typeof $['cable.id'] === 'number' }}" + }, + { + "name": "connectedTo", + "displayName": "Connected To", + "shape": "string", + "computed": true, + "valueExpression": "{{ (() => { for (const p of ['connected_endpoints', 'link_peers']) { const out = []; for (let i = 0; i < 20; i++) { const n = $[p + '.' + i + '.name']; const d = $[p + '.' + i + '.display']; const label = typeof n === 'string' && n ? n : (typeof d === 'string' && d ? d : null); if (!label) continue; const dev = $[p + '.' + i + '.device.name']; out.push(typeof dev === 'string' && dev ? dev + ' ' + label : label); } if (out.length) return out.join(', '); } return null; })() }}" + }, + { + "name": "connected_endpoints_reachable", + "displayName": "Reachable", + "shape": "boolean" + }, + { + "name": "count_ipaddresses", + "displayName": "IP Addresses", + "shape": "number" + }, + { + "name": "description", + "displayName": "Description", + "shape": "string" + }, + { + "name": "last_updated", + "displayName": "Last Updated", + "shape": "date" + }, + { + "name": "display_url", + "displayName": "Link", + "shape": [ + "url", + { + "label": "Open in NetBox" + } + ] + } + ], + "timeframes": false +} diff --git a/plugins/NetBox/v1/dataStreams/deviceRoles.json b/plugins/NetBox/v1/dataStreams/deviceRoles.json new file mode 100644 index 00000000..85a51781 --- /dev/null +++ b/plugins/NetBox/v1/dataStreams/deviceRoles.json @@ -0,0 +1,63 @@ +{ + "name": "deviceRoles", + "displayName": "Device Roles", + "description": "Device roles as label and slug pairs", + "tags": [ + "Inventory" + ], + "visibility": { + "type": "hidden" + }, + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "api/dcim/device-roles/", + "getArgs": [ + { + "key": "fields", + "value": "id,name,slug" + }, + { + "key": "ordering", + "value": "name" + } + ], + "paging": { + "mode": "offset", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "250" + }, + "offset": { + "mode": "row", + "base": 0, + "rowCountIn": { + "realm": "payloadArraySize", + "path": "results" + } + }, + "out": { + "realm": "queryArg", + "path": "offset" + } + }, + "postRequestScript": "deviceRoles.js" + }, + "matches": "none", + "metadata": [ + { + "name": "label", + "displayName": "Label", + "shape": "string", + "role": "label" + }, + { + "name": "value", + "displayName": "Value", + "shape": "string", + "role": "value" + } + ], + "timeframes": false +} diff --git a/plugins/NetBox/v1/dataStreams/devices.json b/plugins/NetBox/v1/dataStreams/devices.json new file mode 100644 index 00000000..b51378cb --- /dev/null +++ b/plugins/NetBox/v1/dataStreams/devices.json @@ -0,0 +1,283 @@ +{ + "name": "devices", + "displayName": "Devices", + "description": "All devices with status, role, hardware, location, ownership and data quality flags", + "tags": [ + "Inventory", + "Network" + ], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "api/dcim/devices/", + "getArgs": [ + { + "key": "fields", + "value": "id,display,display_url,status,role,device_type,platform,site,location,rack,position,face,serial,asset_tag,tenant,primary_ip,oob_ip,cluster,parent_device,description,created,last_updated,custom_fields" + }, + { + "key": "site_id", + "value": "{{ (typeof site !== 'undefined' && site && site.length) ? site.map(s => [].concat(s.rawId)[0]) : null }}" + }, + { + "key": "rack_id", + "value": "{{ (typeof rack !== 'undefined' && rack && rack.length) ? rack.map(o => [].concat(o.rawId)[0]) : null }}" + }, + { + "key": "role", + "value": "{{ (typeof role !== 'undefined' && role && role.length) ? [].concat(role).flatMap(r => (r && typeof r === 'object') ? [r.value] : String(r).split(',')).map(r => String(r).trim()).filter(Boolean) : null }}" + } + ], + "paging": { + "mode": "offset", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "250" + }, + "offset": { + "mode": "row", + "base": 0, + "rowCountIn": { + "realm": "payloadArraySize", + "path": "results" + } + }, + "out": { + "realm": "queryArg", + "path": "offset" + } + }, + "pathToData": "results", + "expandInnerObjects": true + }, + "matches": "none", + "ui": [ + { + "type": "objects", + "name": "site", + "label": "Site (optional)", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Site" + ] + } + } + }, + { + "type": "objects", + "name": "rack", + "label": "Rack (optional)", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Rack" + ] + } + } + }, + { + "type": "autocomplete", + "name": "role", + "label": "Role(s) (optional)", + "isMulti": true, + "allowCustomValues": true, + "data": { + "source": "dataStream", + "dataStreamName": "deviceRoles", + "dataSourceConfig": {} + } + } + ], + "metadata": [ + { + "name": "id", + "visible": false + }, + { + "name": "display", + "displayName": "Name", + "shape": "string", + "role": "label" + }, + { + "name": "status.label", + "displayName": "Status", + "shape": "string" + }, + { + "name": "status.value", + "visible": false + }, + { + "name": "role.name", + "displayName": "Role", + "shape": "string" + }, + { + "name": "device_type.manufacturer.name", + "displayName": "Manufacturer", + "shape": "string" + }, + { + "name": "device_type.model", + "displayName": "Device Type", + "shape": "string" + }, + { + "name": "platform.name", + "displayName": "Platform", + "shape": "string" + }, + { + "name": "site.name", + "displayName": "Site", + "shape": "string" + }, + { + "name": "site.id", + "visible": false + }, + { + "name": "site.url", + "visible": false, + "valueExpression": "{{ typeof $['site.id'] === 'number' ? String($['site.id']) : null }}" + }, + { + "name": "location.name", + "displayName": "Location", + "shape": "string" + }, + { + "name": "rack.name", + "displayName": "Rack", + "shape": "string" + }, + { + "name": "rack.id", + "visible": false + }, + { + "name": "rack.url", + "visible": false, + "valueExpression": "{{ typeof $['rack.id'] === 'number' ? String($['rack.id']) : null }}" + }, + { + "name": "position", + "displayName": "Position (U)", + "shape": "number" + }, + { + "name": "tenant.name", + "displayName": "Tenant", + "shape": "string" + }, + { + "name": "serial", + "displayName": "Serial Number", + "shape": "string" + }, + { + "name": "asset_tag", + "displayName": "Asset Tag", + "shape": "string" + }, + { + "name": "primary_ip.address", + "visible": false + }, + { + "name": "primary_ip.display", + "displayName": "Primary IP", + "shape": "string", + "valueExpression": "{{ typeof $['primary_ip.address'] === 'string' && $['primary_ip.address'] ? $['primary_ip.address'].split('/')[0] : null }}" + }, + { + "name": "oob_ip.address", + "displayName": "OOB IP", + "shape": "string" + }, + { + "name": "cluster.name", + "displayName": "Cluster", + "shape": "string" + }, + { + "name": "parent_device.name", + "displayName": "Parent Device", + "shape": "string" + }, + { + "name": "description", + "displayName": "Description", + "shape": "string" + }, + { + "name": "created", + "displayName": "Created", + "shape": "date" + }, + { + "name": "last_updated", + "displayName": "Last Updated", + "shape": "date" + }, + { + "name": "missingSerial", + "displayName": "Missing Serial", + "shape": "boolean", + "computed": true, + "valueExpression": "{{ !(typeof $['serial'] === 'string' && $['serial']) }}" + }, + { + "name": "missingPrimaryIp", + "displayName": "Missing Primary IP", + "shape": "boolean", + "computed": true, + "valueExpression": "{{ !(typeof $['primary_ip.address'] === 'string' && $['primary_ip.address']) }}" + }, + { + "name": "missingTenant", + "displayName": "Missing Tenant", + "shape": "boolean", + "computed": true, + "valueExpression": "{{ !(typeof $['tenant.name'] === 'string' && $['tenant.name']) }}" + }, + { + "name": "missingPlatform", + "displayName": "Missing Platform", + "shape": "boolean", + "computed": true, + "valueExpression": "{{ !(typeof $['platform.name'] === 'string' && $['platform.name']) }}" + }, + { + "name": "daysSinceUpdate", + "displayName": "Days Since Update", + "shape": "number", + "computed": true, + "valueExpression": "{{ typeof $['last_updated'] === 'string' && $['last_updated'] ? Math.floor((Date.now() - new Date($['last_updated']).getTime()) / 86400000) : null }}" + }, + { + "name": "display_url", + "displayName": "Link", + "shape": [ + "url", + { + "label": "Open in NetBox" + } + ] + }, + { + "sourceId": "id", + "sourceType": "Device", + "name": "display" + }, + { + "pattern": "^custom_fields[.].*" + } + ], + "timeframes": false +} diff --git a/plugins/NetBox/v1/dataStreams/graphqlQuery.json b/plugins/NetBox/v1/dataStreams/graphqlQuery.json new file mode 100644 index 00000000..39cbbe31 --- /dev/null +++ b/plugins/NetBox/v1/dataStreams/graphqlQuery.json @@ -0,0 +1,46 @@ +{ + "name": "graphqlQuery", + "displayName": "GraphQL Query", + "description": "Results of a NetBox GraphQL query, one row per item in the first list returned", + "tags": ["Query"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "post", + "endpointPath": "graphql/", + "headers": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "postBody": { + "query": "{{query}}" + }, + "postRequestScript": "graphqlQuery.js", + "errorHandling": { + "type": "script", + "script": "errorHandling/graphqlQuery.js" + } + }, + "matches": "none", + "manualConfigApply": true, + "ui": [ + { + "type": "code", + "name": "query", + "label": "GraphQL query", + "language": "graphql", + "defaultValue": "{\n power_feed_list {\n name\n status\n rack {\n name\n site {\n name\n }\n }\n custom_field_data\n }\n}", + "validation": { + "required": true + }, + "help": "A NetBox GraphQL query. One row is returned per item in the first list in the response, with nested fields flattened into columns such as `rack.site.name`. Use the GraphiQL explorer at `/graphql/` on your NetBox instance to build and test queries." + } + ], + "metadata": [ + { + "pattern": ".*" + } + ], + "timeframes": false +} diff --git a/plugins/NetBox/v1/dataStreams/ipAddresses.json b/plugins/NetBox/v1/dataStreams/ipAddresses.json new file mode 100644 index 00000000..58e6779f --- /dev/null +++ b/plugins/NetBox/v1/dataStreams/ipAddresses.json @@ -0,0 +1,276 @@ +{ + "name": "ipAddresses", + "displayName": "IP Addresses", + "description": "IP addresses with VRF, status, role, DNS name and assignment", + "tags": [ + "IPAM", + "Network" + ], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "api/ipam/ip-addresses/", + "getArgs": [ + { + "key": "fields", + "value": "id,address,display,display_url,family,vrf,tenant,status,role,assigned_object_id,assigned_object,dns_name,description,created,last_updated,custom_fields" + }, + { + "key": "assigned", + "value": "{{assignment || null}}" + }, + { + "key": "status", + "value": "{{status || null}}" + }, + { + "key": "parent", + "value": "{{ (typeof prefix !== 'undefined' && prefix && prefix.length) ? [].concat(prefix[0].name)[0] : null }}" + }, + { + "key": "vrf_id", + "value": "{{ (typeof prefix !== 'undefined' && prefix && prefix.length) ? (typeof [].concat(prefix[0].vrfId)[0] !== 'undefined' && [].concat(prefix[0].vrfId)[0] !== null ? [].concat(prefix[0].vrfId)[0] : ([].concat(prefix[0].status)[0] === 'container' ? null : 'null')) : null }}" + }, + { + "key": "device_id", + "value": "{{ (typeof device !== 'undefined' && device && device.length) ? device.map(o => [].concat(o.rawId)[0]) : null }}" + }, + { + "key": "virtual_machine_id", + "value": "{{ (typeof virtualMachine !== 'undefined' && virtualMachine && virtualMachine.length) ? virtualMachine.map(o => [].concat(o.rawId)[0]) : null }}" + } + ], + "paging": { + "mode": "offset", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "250" + }, + "offset": { + "mode": "row", + "base": 0, + "rowCountIn": { + "realm": "payloadArraySize", + "path": "results" + } + }, + "out": { + "realm": "queryArg", + "path": "offset" + } + }, + "pathToData": "results", + "expandInnerObjects": true + }, + "matches": "none", + "ui": [ + { + "type": "objects", + "name": "prefix", + "label": "Prefix (optional)", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Prefix" + ] + } + } + }, + { + "type": "objects", + "name": "device", + "label": "Device (optional)", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Device" + ] + } + } + }, + { + "type": "objects", + "name": "virtualMachine", + "label": "Virtual machine (optional)", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Virtual Machine" + ] + } + } + }, + { + "type": "choiceChips", + "name": "assignment", + "label": "Assignment", + "options": [ + { + "value": "true", + "label": "Assigned" + }, + { + "value": "false", + "label": "Unassigned" + } + ] + }, + { + "type": "choiceChips", + "name": "status", + "label": "Status", + "isMulti": true, + "options": [ + { + "value": "active", + "label": "Active" + }, + { + "value": "reserved", + "label": "Reserved" + }, + { + "value": "deprecated", + "label": "Deprecated" + }, + { + "value": "dhcp", + "label": "DHCP" + }, + { + "value": "slaac", + "label": "SLAAC" + } + ] + } + ], + "metadata": [ + { + "name": "id", + "visible": false + }, + { + "name": "address", + "displayName": "Address", + "shape": "string", + "role": "label" + }, + { + "name": "display", + "displayName": "IP", + "shape": "string", + "valueExpression": "{{ typeof $[\"address\"] === \"string\" ? $[\"address\"].split(\"/\")[0] : null }}" + }, + { + "name": "family.label", + "displayName": "Family", + "shape": "string" + }, + { + "name": "family.value", + "visible": false + }, + { + "name": "vrf.name", + "visible": false + }, + { + "name": "vrfName", + "displayName": "VRF", + "shape": "string", + "computed": true, + "valueExpression": "{{ typeof $[\"vrf.name\"] === \"string\" && $[\"vrf.name\"] ? $[\"vrf.name\"] : \"Global\" }}" + }, + { + "name": "status.label", + "displayName": "Status", + "shape": "string" + }, + { + "name": "status.value", + "visible": false + }, + { + "name": "role.label", + "displayName": "Role", + "shape": "string" + }, + { + "name": "tenant.name", + "displayName": "Tenant", + "shape": "string" + }, + { + "name": "dns_name", + "displayName": "DNS Name", + "shape": "string" + }, + { + "name": "assigned_object_id", + "visible": false + }, + { + "name": "assigned_object.name", + "visible": false + }, + { + "name": "assigned_object.display", + "visible": false + }, + { + "name": "assigned_object.device.name", + "visible": false + }, + { + "name": "assigned_object.virtual_machine.name", + "visible": false + }, + { + "name": "assignedTo", + "displayName": "Assigned To", + "shape": "string", + "computed": true, + "valueExpression": "{{ (() => { const s = (v) => typeof v === \"string\" && v ? v : null; const parent = s($[\"assigned_object.device.name\"]) || s($[\"assigned_object.virtual_machine.name\"]); const iface = s($[\"assigned_object.name\"]) || s($[\"assigned_object.display\"]); return parent && iface ? parent + \" / \" + iface : (parent || iface); })() }}" + }, + { + "name": "assigned", + "displayName": "Assigned", + "shape": "boolean", + "computed": true, + "valueExpression": "{{ typeof $[\"assigned_object_id\"] === \"number\" }}" + }, + { + "name": "description", + "displayName": "Description", + "shape": "string" + }, + { + "name": "created", + "displayName": "Created", + "shape": "date" + }, + { + "name": "last_updated", + "displayName": "Last Updated", + "shape": "date" + }, + { + "name": "display_url", + "displayName": "Link", + "shape": [ + "url", + { + "label": "Open in NetBox" + } + ] + }, + { + "pattern": "^custom_fields[.].*" + } + ], + "timeframes": false +} diff --git a/plugins/NetBox/v1/dataStreams/objectChanges.json b/plugins/NetBox/v1/dataStreams/objectChanges.json new file mode 100644 index 00000000..f86446eb --- /dev/null +++ b/plugins/NetBox/v1/dataStreams/objectChanges.json @@ -0,0 +1,209 @@ +{ + "name": "objectChanges", + "displayName": "Change Log", + "description": "Changes recorded in the NetBox change log with user, action and object", + "tags": [ + "Audit", + "Changes" + ], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "api/core/object-changes/", + "getArgs": [ + { + "key": "fields", + "value": "id,time,user_name,request_id,action,changed_object_type,changed_object_id,object_repr,message" + }, + { + "key": "time_after", + "value": "{{timeframe.start}}" + }, + { + "key": "time_before", + "value": "{{timeframe.end}}" + }, + { + "key": "action", + "value": "{{typeof action !== 'undefined' && action ? action : null}}" + }, + { + "key": "changed_object_type", + "value": "{{(typeof object !== 'undefined' && object && object[0]) ? ({ 'Site': 'dcim.site', 'Rack': 'dcim.rack', 'Device': 'dcim.device', 'Virtual Machine': 'virtualization.virtualmachine', 'Prefix': 'ipam.prefix', 'Circuit': 'circuits.circuit' })[[].concat(object[0].sourceType)[0]] || null : (typeof objectType !== 'undefined' && objectType ? objectType : null)}}" + }, + { + "key": "changed_object_id", + "value": "{{(typeof object !== 'undefined' && object && object[0]) ? [].concat(object[0].rawId)[0] : null}}" + } + ], + "paging": { + "mode": "offset", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "250" + }, + "offset": { + "mode": "row", + "base": 0, + "rowCountIn": { + "realm": "payloadArraySize", + "path": "results" + } + }, + "out": { + "realm": "queryArg", + "path": "offset" + } + }, + "pathToData": "results", + "expandInnerObjects": true + }, + "matches": "none", + "ui": [ + { + "type": "objects", + "name": "object", + "label": "Object (optional)", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Site", + "Rack", + "Device", + "Virtual Machine", + "Prefix", + "Circuit" + ] + } + } + }, + { + "type": "choiceChips", + "name": "action", + "label": "Action", + "options": [ + { + "value": "create", + "label": "Created" + }, + { + "value": "update", + "label": "Updated" + }, + { + "value": "delete", + "label": "Deleted" + } + ] + }, + { + "type": "choiceChips", + "name": "objectType", + "label": "Object type", + "options": [ + { + "value": "dcim.site", + "label": "Site" + }, + { + "value": "dcim.rack", + "label": "Rack" + }, + { + "value": "dcim.device", + "label": "Device" + }, + { + "value": "virtualization.virtualmachine", + "label": "Virtual Machine" + }, + { + "value": "ipam.prefix", + "label": "Prefix" + }, + { + "value": "circuits.circuit", + "label": "Circuit" + } + ] + } + ], + "metadata": [ + { + "name": "id", + "visible": false + }, + { + "name": "time", + "displayName": "Time", + "shape": "date", + "role": "timestamp" + }, + { + "name": "user_name", + "displayName": "User", + "shape": "string" + }, + { + "name": "action.label", + "displayName": "Action", + "shape": "string" + }, + { + "name": "action.value", + "visible": false + }, + { + "name": "changed_object_type", + "displayName": "Object Type", + "shape": "string", + "formatExpression": "{{ ({ 'dcim.site': 'Site', 'dcim.rack': 'Rack', 'dcim.device': 'Device', 'virtualization.virtualmachine': 'Virtual Machine', 'ipam.prefix': 'Prefix', 'circuits.circuit': 'Circuit' })[$['changed_object_type']] || $['changed_object_type'] }}" + }, + { + "name": "objectTypeName", + "displayName": "Object Type Name", + "shape": "string", + "computed": true, + "visible": false, + "valueExpression": "{{ (() => { const t = $['changed_object_type']; if (typeof t !== 'string') { return null; } const known = { 'dcim.site': 'Site', 'dcim.rack': 'Rack', 'dcim.device': 'Device', 'virtualization.virtualmachine': 'Virtual Machine', 'ipam.prefix': 'Prefix', 'circuits.circuit': 'Circuit' }; return known[t] || t.split('.').pop().replace(/^./, (c) => c.toUpperCase()); })() }}" + }, + { + "name": "object_repr", + "displayName": "Object", + "shape": "string", + "role": "label" + }, + { + "name": "message", + "displayName": "Message", + "shape": "string" + }, + { + "name": "changed_object_id", + "visible": false + }, + { + "name": "request_id", + "visible": false + } + ], + "defaultShaping": { + "sort": { + "by": [ + [ + "time", + "desc" + ] + ] + } + }, + "timeframes": [ + "last1hour", + "last12hours", + "last24hours", + "last7days", + "last30days" + ] +} diff --git a/plugins/NetBox/v1/dataStreams/powerFeeds.json b/plugins/NetBox/v1/dataStreams/powerFeeds.json new file mode 100644 index 00000000..6f3bf107 --- /dev/null +++ b/plugins/NetBox/v1/dataStreams/powerFeeds.json @@ -0,0 +1,222 @@ +{ + "name": "powerFeeds", + "displayName": "Power Feeds", + "description": "Power feeds with their rack, site, panel, electrical rating and available power", + "tags": [ + "Power", + "Capacity" + ], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "post", + "endpointPath": "graphql/", + "headers": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "postBody": { + "query": "query PowerFeeds($offset: Int = 0) { power_feed_list(pagination: { offset: $offset, limit: 500 }{{ (typeof site !== 'undefined' && site && site.length) ? ', filters: { power_panel: { site: { id: { in_list: [' + site.map(s => Number([].concat(s.rawId)[0])).join(', ') + '] } } } }' : '' }}) { id name description status type supply phase voltage amperage max_utilization available_power mark_connected cable { id } tenant { name } rack { id name } power_panel { id name site { id name } } created last_updated custom_field_data } }" + }, + "paging": { + "mode": "offset", + "pageSize": { + "realm": "none" + }, + "offset": { + "mode": "row", + "base": 0, + "rowCountIn": { + "realm": "payloadArraySize", + "path": "data.power_feed_list" + } + }, + "out": { + "realm": "body", + "path": "variables.offset" + } + }, + "postRequestScript": "powerFeeds.js", + "expandInnerObjects": true + }, + "matches": "none", + "ui": [ + { + "type": "objects", + "name": "site", + "label": "Site (optional)", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Site" + ] + } + } + } + ], + "metadata": [ + { + "name": "id", + "visible": false + }, + { + "name": "name", + "displayName": "Name", + "shape": "string", + "role": "label" + }, + { + "name": "power_panel.site.name", + "displayName": "Site", + "shape": "string" + }, + { + "name": "rack.name", + "displayName": "Rack", + "shape": "string" + }, + { + "name": "power_panel.name", + "displayName": "Power Panel", + "shape": "string" + }, + { + "name": "status", + "visible": false + }, + { + "name": "statusLabel", + "displayName": "Status", + "shape": "string", + "computed": true, + "valueExpression": "{{ typeof $['status'] === 'string' ? $['status'].charAt(0).toUpperCase() + $['status'].slice(1).replace(/-/g, ' ') : null }}" + }, + { + "name": "type", + "visible": false + }, + { + "name": "typeLabel", + "displayName": "Type", + "shape": "string", + "computed": true, + "valueExpression": "{{ typeof $['type'] === 'string' ? $['type'].charAt(0).toUpperCase() + $['type'].slice(1).replace(/-/g, ' ') : null }}" + }, + { + "name": "supply", + "visible": false + }, + { + "name": "supplyLabel", + "displayName": "Supply", + "shape": "string", + "computed": true, + "valueExpression": "{{ typeof $['supply'] === 'string' ? $['supply'].toUpperCase() : null }}" + }, + { + "name": "phase", + "visible": false + }, + { + "name": "phaseLabel", + "displayName": "Phase", + "shape": "string", + "computed": true, + "valueExpression": "{{ typeof $['phase'] === 'string' ? $['phase'].charAt(0).toUpperCase() + $['phase'].slice(1).replace(/-/g, ' ') : null }}" + }, + { + "name": "voltage", + "displayName": "Voltage (V)", + "shape": "number" + }, + { + "name": "amperage", + "displayName": "Amperage (A)", + "shape": "number" + }, + { + "name": "max_utilization", + "displayName": "Max Utilization", + "shape": "percent" + }, + { + "name": "available_power", + "visible": false + }, + { + "name": "availableKva", + "displayName": "Available Power (kVA)", + "shape": [ + "number", + { + "decimalPlaces": 2 + } + ], + "computed": true, + "valueExpression": "{{ typeof $['available_power'] === 'number' ? Math.round($['available_power'] / 10) / 100 : null }}" + }, + { + "name": "mark_connected", + "visible": false + }, + { + "name": "cable.id", + "visible": false + }, + { + "name": "connected", + "displayName": "Connected", + "shape": "boolean", + "computed": true, + "valueExpression": "{{ $['mark_connected'] === true || typeof $['cable.id'] === 'string' }}" + }, + { + "name": "tenant.name", + "displayName": "Tenant", + "shape": "string" + }, + { + "name": "description", + "displayName": "Description", + "shape": "string" + }, + { + "name": "created", + "displayName": "Created", + "shape": "date" + }, + { + "name": "last_updated", + "displayName": "Last Updated", + "shape": "date" + }, + { + "name": "rack.id", + "visible": false + }, + { + "name": "power_panel.id", + "visible": false + }, + { + "name": "power_panel.site.id", + "visible": false + }, + { + "sourceId": "rack.id", + "sourceType": "Rack", + "name": "rack.name" + }, + { + "sourceId": "power_panel.site.id", + "sourceType": "Site", + "name": "power_panel.site.name" + }, + { + "pattern": "^custom_field_data[.].*" + } + ], + "timeframes": false +} diff --git a/plugins/NetBox/v1/dataStreams/prefixUtilization.json b/plugins/NetBox/v1/dataStreams/prefixUtilization.json new file mode 100644 index 00000000..90687a97 --- /dev/null +++ b/plugins/NetBox/v1/dataStreams/prefixUtilization.json @@ -0,0 +1,133 @@ +{ + "name": "prefixUtilization", + "displayName": "Prefix Utilization", + "description": "IP space utilization per prefix, calculated the same way as NetBox", + "tags": [ + "IPAM", + "Capacity" + ], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "{{ object.status === 'container' ? 'api/ipam/prefixes/' : 'api/ipam/ip-addresses/' }}", + "getArgs": [ + { + "key": "within", + "value": "{{ object.status === 'container' ? object.name : null }}" + }, + { + "key": "parent", + "value": "{{ object.status === 'container' ? null : object.name }}" + }, + { + "key": "vrf_id", + "value": "{{ object.status === 'container' ? (typeof object.vrfId === 'undefined' ? null : object.vrfId) : (typeof object.vrfId === 'undefined' ? 'null' : object.vrfId) }}" + }, + { + "key": "fields", + "value": "{{ object.status === 'container' ? 'prefix' : null }}" + }, + { + "key": "brief", + "value": "{{ object.status === 'container' ? null : 'true' }}" + }, + { + "key": "limit", + "value": "{{ object.status === 'container' ? '1000' : '1' }}" + } + ], + "paging": { + "mode": "none" + }, + "postRequestScript": "prefixUtilization.js" + }, + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Prefix" + ] + } + }, + "metadata": [ + { + "name": "prefix", + "displayName": "Prefix", + "shape": "string", + "role": "label" + }, + { + "name": "vrf", + "displayName": "VRF", + "shape": "string" + }, + { + "name": "status", + "displayName": "Status", + "shape": "string" + }, + { + "name": "size", + "displayName": "Size", + "shape": [ + "number", + { + "decimalPlaces": 0, + "thousandsSeparator": true + } + ] + }, + { + "name": "used", + "displayName": "Used", + "shape": [ + "number", + { + "decimalPlaces": 0, + "thousandsSeparator": true + } + ] + }, + { + "name": "utilization", + "displayName": "Utilization", + "shape": [ + "percent", + { + "decimalPlaces": 1 + } + ], + "role": "value" + }, + { + "name": "prefixId", + "visible": false + }, + { + "name": "netboxUrl", + "displayName": "Link", + "shape": [ + "url", + { + "label": "Open in NetBox" + } + ] + }, + { + "sourceId": "prefixId", + "sourceType": "Prefix", + "name": "prefix" + } + ], + "defaultShaping": { + "sort": { + "by": [ + [ + "utilization", + "desc" + ] + ] + } + }, + "timeframes": false +} diff --git a/plugins/NetBox/v1/dataStreams/prefixes.json b/plugins/NetBox/v1/dataStreams/prefixes.json new file mode 100644 index 00000000..ef588814 --- /dev/null +++ b/plugins/NetBox/v1/dataStreams/prefixes.json @@ -0,0 +1,175 @@ +{ + "name": "prefixes", + "displayName": "Prefixes", + "description": "All IP prefixes with VRF, scope, status, role and pool settings", + "tags": [ + "IPAM", + "Network" + ], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "api/ipam/prefixes/", + "getArgs": [ + { + "key": "fields", + "value": "id,display,display_url,prefix,family,vrf,scope_type,scope,status,role,tenant,vlan,is_pool,mark_utilized,_depth,children,description,created,last_updated,custom_fields" + }, + { + "key": "site_id", + "value": "{{ (typeof site !== 'undefined' && site && site.length) ? site.map(s => [].concat(s.rawId)[0]) : null }}" + } + ], + "paging": { + "mode": "offset", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "250" + }, + "offset": { + "mode": "row", + "base": 0, + "rowCountIn": { + "realm": "payloadArraySize", + "path": "results" + } + }, + "out": { + "realm": "queryArg", + "path": "offset" + } + }, + "pathToData": "results", + "expandInnerObjects": true + }, + "matches": "none", + "ui": [ + { + "type": "objects", + "name": "site", + "label": "Site (optional)", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Site" + ] + } + } + } + ], + "metadata": [ + { + "name": "id", + "visible": false + }, + { + "name": "prefix", + "displayName": "Prefix", + "shape": "string", + "role": "label" + }, + { + "name": "family.label", + "displayName": "Family", + "shape": "string" + }, + { + "name": "vrf.name", + "displayName": "VRF", + "shape": "string" + }, + { + "name": "vrf.id", + "visible": false + }, + { + "name": "status.label", + "displayName": "Status", + "shape": "string" + }, + { + "name": "status.value", + "visible": false + }, + { + "name": "role.name", + "displayName": "Role", + "shape": "string" + }, + { + "name": "tenant.name", + "displayName": "Tenant", + "shape": "string" + }, + { + "name": "scope.name", + "displayName": "Scope", + "shape": "string" + }, + { + "name": "scope_type", + "visible": false + }, + { + "name": "vlan.display", + "displayName": "VLAN", + "shape": "string" + }, + { + "name": "is_pool", + "displayName": "Pool", + "shape": "boolean" + }, + { + "name": "mark_utilized", + "displayName": "Marked Utilized", + "shape": "boolean" + }, + { + "name": "_depth", + "displayName": "Depth", + "shape": "number" + }, + { + "name": "children", + "displayName": "Child Prefixes", + "shape": "number" + }, + { + "name": "description", + "displayName": "Description", + "shape": "string" + }, + { + "name": "created", + "displayName": "Created", + "shape": "date" + }, + { + "name": "last_updated", + "displayName": "Last Updated", + "shape": "date" + }, + { + "name": "display_url", + "displayName": "Link", + "shape": [ + "url", + { + "label": "Open in NetBox" + } + ] + }, + { + "sourceId": "id", + "sourceType": "Prefix", + "name": "prefix" + }, + { + "pattern": "^custom_fields[.].*" + } + ], + "timeframes": false +} diff --git a/plugins/NetBox/v1/dataStreams/rackElevation.json b/plugins/NetBox/v1/dataStreams/rackElevation.json new file mode 100644 index 00000000..6b70acf0 --- /dev/null +++ b/plugins/NetBox/v1/dataStreams/rackElevation.json @@ -0,0 +1,171 @@ +{ + "name": "rackElevation", + "displayName": "Rack Elevation", + "description": "Rack units from top to bottom with the device, role, type and manufacturer at each unit", + "tags": [ + "Inventory", + "Capacity" + ], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "post", + "endpointPath": "graphql/", + "headers": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "postBody": { + "query": "query($offset: Int) { rack_list(pagination: { limit: 100, offset: $offset }{{ (() => { const ids = (v) => ((typeof v !== 'undefined' && v) || []).map((o) => String([].concat(o.rawId)[0]).replace(/[^0-9]/g, '')).filter(Boolean); const s = ids(typeof site !== 'undefined' ? site : null); const r = ids(typeof rack !== 'undefined' ? rack : null); const f = []; if (s.length) f.push('site: { id: { in_list: [' + s.join(', ') + '] } }'); if (r.length) f.push('id: { in_list: [' + r.join(', ') + '] }'); return f.length ? ', filters: { ' + f.join(', ') + ' }' : ''; })() }}) { id name u_height starting_unit desc_units site { name } devices { id name position face role { name slug } device_type { model u_height is_full_depth manufacturer { name } } parent_bay { id } } } }", + "variables": { + "offset": 0 + } + }, + "postRequestScript": "rackElevation.js", + "paging": { + "mode": "offset", + "offset": { + "mode": "row", + "base": 0, + "rowCountIn": { + "realm": "payloadArraySize", + "path": "data.rack_list" + } + }, + "out": { + "realm": "body", + "path": "variables.offset" + } + } + }, + "matches": "none", + "ui": [ + { + "type": "objects", + "name": "site", + "label": "Site (optional)", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Site" + ] + } + } + }, + { + "type": "objects", + "name": "rack", + "label": "Rack (optional)", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Rack" + ] + } + } + }, + { + "type": "autocomplete", + "name": "role", + "label": "Role (optional)", + "isMulti": true, + "allowCustomValues": true, + "data": { + "source": "dataStream", + "dataStreamName": "deviceRoles", + "dataSourceConfig": {} + } + } + ], + "metadata": [ + { + "name": "rack", + "displayName": "Rack", + "shape": "string", + "role": "label" + }, + { + "name": "site", + "displayName": "Site", + "shape": "string" + }, + { + "name": "unit", + "displayName": "Unit", + "shape": "number" + }, + { + "name": "unitsLabel", + "displayName": "Device Units", + "shape": "string" + }, + { + "name": "device", + "displayName": "Device", + "shape": "string" + }, + { + "name": "role", + "displayName": "Role", + "shape": "string" + }, + { + "name": "deviceType", + "displayName": "Device Type", + "shape": "string" + }, + { + "name": "manufacturer", + "displayName": "Manufacturer", + "shape": "string" + }, + { + "name": "face", + "displayName": "Face", + "shape": "string" + }, + { + "name": "deviceHeight", + "displayName": "Height (U)", + "shape": "number" + }, + { + "name": "isTopUnit", + "displayName": "Top Unit", + "shape": "boolean" + }, + { + "name": "occupied", + "displayName": "Occupied", + "shape": "boolean" + }, + { + "name": "order", + "displayName": "Row Order", + "shape": "number", + "visible": false + }, + { + "name": "rackId", + "visible": false + }, + { + "name": "deviceId", + "visible": false + }, + { + "sourceId": "rackId", + "sourceType": "Rack", + "name": "rack" + }, + { + "sourceId": "deviceId", + "sourceType": "Device", + "name": "device" + } + ], + "timeframes": false +} diff --git a/plugins/NetBox/v1/dataStreams/rackUtilization.json b/plugins/NetBox/v1/dataStreams/rackUtilization.json new file mode 100644 index 00000000..bf407a3a --- /dev/null +++ b/plugins/NetBox/v1/dataStreams/rackUtilization.json @@ -0,0 +1,150 @@ +{ + "name": "rackUtilization", + "displayName": "Rack Utilization", + "description": "Rack unit space used and available per rack, counting both faces", + "tags": [ + "Capacity", + "Inventory" + ], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "post", + "endpointPath": "graphql/", + "headers": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "postBody": { + "query": "query($offset: Int) { rack_list(pagination: { limit: 500, offset: $offset }{{ (() => { const ids = (v) => ((typeof v !== 'undefined' && v) || []).map((o) => String([].concat(o.rawId)[0]).replace(/[^0-9]/g, '')).filter(Boolean); const s = ids(typeof site !== 'undefined' ? site : null); const r = ids(typeof rack !== 'undefined' ? rack : null); const f = []; if (s.length) f.push('site: { id: { in_list: [' + s.join(', ') + '] } }'); if (r.length) f.push('id: { in_list: [' + r.join(', ') + '] }'); return f.length ? ', filters: { ' + f.join(', ') + ' }' : ''; })() }}) { id name u_height starting_unit site { id name } location { name } devices { id position face device_type { u_height is_full_depth } } } }", + "variables": { + "offset": 0 + } + }, + "paging": { + "mode": "offset", + "offset": { + "mode": "row", + "base": 0, + "rowCountIn": { + "realm": "payloadArraySize", + "path": "data.rack_list" + } + }, + "out": { + "realm": "body", + "path": "variables.offset" + } + }, + "postRequestScript": "rackUtilization.js" + }, + "matches": "none", + "ui": [ + { + "type": "objects", + "name": "site", + "label": "Site (optional)", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Site" + ] + } + } + }, + { + "type": "objects", + "name": "rack", + "label": "Rack (optional)", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Rack" + ] + } + } + } + ], + "metadata": [ + { + "name": "rack", + "displayName": "Rack", + "shape": "string", + "role": "label" + }, + { + "name": "site", + "displayName": "Site", + "shape": "string" + }, + { + "name": "location", + "displayName": "Location", + "shape": "string" + }, + { + "name": "uHeight", + "displayName": "Height (U)", + "shape": "number" + }, + { + "name": "usedUnits", + "displayName": "Used (U)", + "shape": "number" + }, + { + "name": "availableUnits", + "displayName": "Available (U)", + "shape": "number" + }, + { + "name": "utilization", + "displayName": "Utilization", + "shape": [ + "percent", + { + "decimalPlaces": 1 + } + ], + "role": "value" + }, + { + "name": "frontUsed", + "displayName": "Front Used (U)", + "shape": "number" + }, + { + "name": "rearUsed", + "displayName": "Rear Used (U)", + "shape": "number" + }, + { + "name": "deviceCount", + "displayName": "Devices", + "shape": "number" + }, + { + "name": "rackId", + "visible": false + }, + { + "sourceId": "rackId", + "sourceType": "Rack", + "name": "rack" + } + ], + "defaultShaping": { + "sort": { + "by": [ + [ + "utilization", + "desc" + ] + ] + } + }, + "timeframes": false +} diff --git a/plugins/NetBox/v1/dataStreams/racks.json b/plugins/NetBox/v1/dataStreams/racks.json new file mode 100644 index 00000000..35556efd --- /dev/null +++ b/plugins/NetBox/v1/dataStreams/racks.json @@ -0,0 +1,181 @@ +{ + "name": "racks", + "displayName": "Racks", + "description": "All racks with site, location, status, height and device count", + "tags": [ + "Inventory", + "Network" + ], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "api/dcim/racks/", + "getArgs": [ + { + "key": "fields", + "value": "id,display,display_url,site,location,status,role,tenant,facility_id,serial,asset_tag,u_height,starting_unit,form_factor,device_count,powerfeed_count,description,created,last_updated,custom_fields" + }, + { + "key": "site_id", + "value": "{{ (typeof site !== 'undefined' && site && site.length) ? site.map(s => [].concat(s.rawId)[0]) : null }}" + } + ], + "paging": { + "mode": "offset", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "250" + }, + "offset": { + "mode": "row", + "base": 0, + "rowCountIn": { + "realm": "payloadArraySize", + "path": "results" + } + }, + "out": { + "realm": "queryArg", + "path": "offset" + } + }, + "pathToData": "results", + "expandInnerObjects": true + }, + "matches": "none", + "ui": [ + { + "type": "objects", + "name": "site", + "label": "Site (optional)", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Site" + ] + } + } + } + ], + "metadata": [ + { + "name": "id", + "visible": false + }, + { + "name": "display", + "displayName": "Name", + "shape": "string", + "role": "label" + }, + { + "name": "status.label", + "displayName": "Status", + "shape": "string" + }, + { + "name": "status.value", + "visible": false + }, + { + "name": "site.name", + "displayName": "Site", + "shape": "string" + }, + { + "name": "site.id", + "visible": false + }, + { + "name": "site.url", + "visible": false, + "valueExpression": "{{ typeof $['site.id'] === 'number' ? String($['site.id']) : null }}" + }, + { + "name": "location.name", + "displayName": "Location", + "shape": "string" + }, + { + "name": "role.name", + "displayName": "Role", + "shape": "string" + }, + { + "name": "tenant.name", + "displayName": "Tenant", + "shape": "string" + }, + { + "name": "facility_id", + "displayName": "Facility ID", + "shape": "string" + }, + { + "name": "form_factor.label", + "displayName": "Form Factor", + "shape": "string" + }, + { + "name": "u_height", + "displayName": "Height (U)", + "shape": "number" + }, + { + "name": "device_count", + "displayName": "Devices", + "shape": "number" + }, + { + "name": "powerfeed_count", + "displayName": "Power Feeds", + "shape": "number" + }, + { + "name": "serial", + "displayName": "Serial Number", + "shape": "string" + }, + { + "name": "asset_tag", + "displayName": "Asset Tag", + "shape": "string" + }, + { + "name": "description", + "displayName": "Description", + "shape": "string" + }, + { + "name": "created", + "displayName": "Created", + "shape": "date" + }, + { + "name": "last_updated", + "displayName": "Last Updated", + "shape": "date" + }, + { + "name": "display_url", + "displayName": "Link", + "shape": [ + "url", + { + "label": "Open in NetBox" + } + ] + }, + { + "sourceId": "id", + "sourceType": "Rack", + "name": "display" + }, + { + "pattern": "^custom_fields[.].*" + } + ], + "timeframes": false +} diff --git a/plugins/NetBox/v1/dataStreams/scripts/deviceRoles.js b/plugins/NetBox/v1/dataStreams/scripts/deviceRoles.js new file mode 100644 index 00000000..73dec329 --- /dev/null +++ b/plugins/NetBox/v1/dataStreams/scripts/deviceRoles.js @@ -0,0 +1,4 @@ +result = ((data && data.results) || []).map((r) => ({ + label: r.name, + value: r.slug, +})); diff --git a/plugins/NetBox/v1/dataStreams/scripts/errorHandling/graphqlQuery.js b/plugins/NetBox/v1/dataStreams/scripts/errorHandling/graphqlQuery.js new file mode 100644 index 00000000..84d45c8b --- /dev/null +++ b/plugins/NetBox/v1/dataStreams/scripts/errorHandling/graphqlQuery.js @@ -0,0 +1,5 @@ +// GraphQL errors in a 200 response are raised by the post-request script; this covers failed HTTP requests (bad auth, malformed request) +const body = typeof response.body === "string" ? response.body.slice(0, 300) : ""; +const apiMsg = (data && (data.detail || data.message)) || body; + +result = apiMsg ? `HTTP ${response.status}: ${apiMsg}` : `Request failed with HTTP ${response.status}`; diff --git a/plugins/NetBox/v1/dataStreams/scripts/graphqlQuery.js b/plugins/NetBox/v1/dataStreams/scripts/graphqlQuery.js new file mode 100644 index 00000000..97534aef --- /dev/null +++ b/plugins/NetBox/v1/dataStreams/scripts/graphqlQuery.js @@ -0,0 +1,81 @@ +// NetBox GraphQL returns { data: { : [ ...items ] } } for list queries +// (e.g. site_list) or { data: { : { ... } } } for single-object queries. +// The query is user-supplied, so the columns are unknown in advance and must be built dynamically here. +// +// Row source: the first array found in `data.data` (in key order). If a top-level value is an object +// that wraps a list under a well-known name (results, items, nodes, edges, entries), that list is used. +// If there is no list at all, the single object returned becomes one row. +// +// Flattening: nested objects become dot-notation columns (rack.site.name). +// - arrays of primitives are joined with ", " +// - arrays of objects become ".count" plus "", the joined display/name of each item (if any have one) +const WRAPPER_KEYS = ["results", "items", "nodes", "edges", "entries"]; +const isObject =(v) => v !== null && typeof v === "object" && !Array.isArray(v); +const isPrimitive = (v) => v === null || typeof v !== "object"; + +const flatten = (value, path, out) => { + if (isObject(value)) { + Object.keys(value).forEach((key) => { + flatten(value[key], path ? `${path}.${key}` : key, out); + }); + } else if (Array.isArray(value)) { + if (value.every(isPrimitive)) { + out[path] = value.join(", "); + } else { + out[`${path}.count`] = value.length; + out[path] = value + .map((item) => isObject(item) && (item.display || item.name)) + .filter(Boolean) + .join(", "); + } + } else { + out[path] = value; + } + return out; +}; + +// GraphQL reports query errors with HTTP 200 and an `errors` array, which errorHandling never sees (it only runs on failed HTTP requests) +if (data && Array.isArray(data.errors) && data.errors.length) { + throw new Error(data.errors.map((e) => e.message).join("; ")); +} + +const root = (data && data.data) || {}; + +let items = null; +for (const key of Object.keys(root)) { + const value = root[key]; + if (Array.isArray(value)) { + items = value; + break; + } + if (isObject(value)) { + // Only well-known wrapper names are unwrapped, otherwise a single object with an array field + // (e.g. site(id: 1) { name tags { name } }) would be mistaken for a list of its tags + const inner = Object.keys(value).find((k) => WRAPPER_KEYS.includes(k) && Array.isArray(value[k])); + if (inner) { + items = value[inner]; + break; + } + } +} + +if (items) { + const rows = items.map((item) => (isObject(item) ? flatten(item, "", {}) : { value: item })); + // An empty array can't be told apart from an empty primitive list, so it emits no ".count" column. + // Default that column to 0 on rows that lack it, otherwise the count is blank rather than 0 for empty lists. + const countCols = new Set(); + rows.forEach((row) => Object.keys(row).forEach((k) => k.endsWith(".count") && countCols.add(k))); + rows.forEach((row) => countCols.forEach((k) => row[k] === undefined && (row[k] = 0))); + // A null nested object (e.g. region: null) is emitted as a "region" column, which would sit beside "region.name" + // from the rows where it is populated. Drop it so only the dotted columns remain (blank where the object is null). + const allKeys = new Set(); + rows.forEach((row) => Object.keys(row).forEach((k) => allKeys.add(k))); + const parents = [...allKeys].filter((k) => [...allKeys].some((o) => o.startsWith(`${k}.`))); + rows.forEach((row) => parents.forEach((k) => row[k] === null && delete row[k])); + result = rows; +} else { + const keys = Object.keys(root); + // A single object field (e.g. site(id: 1) { ... }) is flattened without its field name as a prefix + const single = keys.length === 1 && isObject(root[keys[0]]) ? root[keys[0]] : root; + result = keys.length ? [flatten(single, "", {})] : []; +} diff --git a/plugins/NetBox/v1/dataStreams/scripts/powerFeeds.js b/plugins/NetBox/v1/dataStreams/scripts/powerFeeds.js new file mode 100644 index 00000000..8edb8b77 --- /dev/null +++ b/plugins/NetBox/v1/dataStreams/scripts/powerFeeds.js @@ -0,0 +1,6 @@ +// GraphQL reports errors as HTTP 200 with an `errors` array, so errorHandling never fires; throw to surface the message. +if (data && data.errors && data.errors.length) { + throw new Error(data.errors.map((e) => e.message).join("; ")); +} + +result = (data && data.data && data.data.power_feed_list) || []; diff --git a/plugins/NetBox/v1/dataStreams/scripts/prefixUtilization.js b/plugins/NetBox/v1/dataStreams/scripts/prefixUtilization.js new file mode 100644 index 00000000..27e7dd59 --- /dev/null +++ b/plugins/NetBox/v1/dataStreams/scripts/prefixUtilization.js @@ -0,0 +1,95 @@ +// Mirrors NetBox Prefix.get_utilization() (ipam/models/ip.py), minus IP ranges marked populated. +const unwrap = (v) => (Array.isArray(v) ? v[0] : v); +const obj = (context.objects && context.objects[0]) || {}; +const cidr = String(unwrap(obj.name)); +const status = unwrap(obj.status); +const isPool = unwrap(obj.isPool) === true || unwrap(obj.isPool) === 'true'; +const markUtilized = unwrap(obj.markUtilized) === true || unwrap(obj.markUtilized) === 'true'; +const vrfId = unwrap(obj.vrfId); +const vrfName = unwrap(obj.vrf); + +const [addr, lenStr] = cidr.split('/'); +const prefixLen = parseInt(lenStr, 10); +const isV6 = addr.includes(':'); +const totalBits = isV6 ? 128 : 32; + +// Parse an address into a BigInt (IPv4 dotted quad or IPv6, including '::' compression and embedded IPv4). +const toBigInt = (a) => { + if (!a.includes(':')) { + return a.split('.').reduce((acc, o) => (acc << 8n) + BigInt(parseInt(o, 10)), 0n); + } + let s = a; + if (s.includes('.')) { + const idx = s.lastIndexOf(':'); + const v4 = s.slice(idx + 1).split('.').map((o) => parseInt(o, 10)); + s = s.slice(0, idx + 1) + ((v4[0] << 8) | v4[1]).toString(16) + ':' + ((v4[2] << 8) | v4[3]).toString(16); + } + const [head, tail] = s.split('::'); + const h = head ? head.split(':') : []; + const t = tail !== undefined && tail ? tail.split(':') : []; + const missing = tail === undefined ? 0 : 8 - h.length - t.length; + const groups = [...h, ...Array(missing).fill('0'), ...t]; + return groups.reduce((acc, g) => (acc << 16n) + BigInt(parseInt(g || '0', 16)), 0n); +}; + +const parseCidr = (c) => { + const [a, l] = c.split('/'); + const len = parseInt(l, 10); + const bits = a.includes(':') ? 128 : 32; + const size = 1n << BigInt(bits - len); + const start = (toBigInt(a) >> BigInt(bits - len)) << BigInt(bits - len); + return { start, end: start + size - 1n, size }; +}; + +const prefixSize = 1n << BigInt(totalBits - prefixLen); +let size = prefixSize; +let used = 0n; +let utilization; + +if (markUtilized) { + utilization = 100; + if (status === 'container') { + used = prefixSize; + } +} else if (status === 'container') { + // Union of child ranges: sort by start and merge overlaps, so nested children aren't double counted. + const children = ((data && data.results) || []) + .filter((r) => r && typeof r.prefix === 'string') + .map((r) => parseCidr(r.prefix)) + .sort((a, b) => (a.start < b.start ? -1 : a.start > b.start ? 1 : 0)); + let curEnd = -1n; + for (const c of children) { + if (c.start > curEnd) { + used += c.size; + curEnd = c.end; + } else if (c.end > curEnd) { + used += c.end - curEnd; + curEnd = c.end; + } + } +} else { + used = BigInt((data && data.count) || 0); + // IPv4 non-pool prefixes shorter than /31 exclude the network and broadcast addresses + if (!isV6 && prefixLen < 31 && !isPool) { + size = prefixSize - 2n; + } +} + +if (utilization === undefined) { + // Scale by 10000 in BigInt to keep precision for huge IPv6 sizes, then cap at 100 + const scaled = size > 0n ? (used * 10000n) / size : 0n; + utilization = Math.min(Number(scaled) / 100, 100); +} + +result = [ + { + prefix: cidr, + vrf: typeof vrfName === 'string' && vrfName ? vrfName : 'Global', + status: status, + size: Number(size), + used: Number(used), + utilization: utilization, + prefixId: unwrap(obj.rawId), + netboxUrl: unwrap(obj.netboxUrl), + }, +]; diff --git a/plugins/NetBox/v1/dataStreams/scripts/rackElevation.js b/plugins/NetBox/v1/dataStreams/scripts/rackElevation.js new file mode 100644 index 00000000..020a9548 --- /dev/null +++ b/plugins/NetBox/v1/dataStreams/scripts/rackElevation.js @@ -0,0 +1,123 @@ +// Builds one row per rack unit (and per device covering it) from the GraphQL rack_list response. +// The GraphQL API returns Decimal fields (position, u_height) as strings such as "32.0". +// GraphQL reports errors as HTTP 200 with an `errors` array, so errorHandling never fires; throw to surface the message. +if (data && data.errors && data.errors.length) { + throw new Error(data.errors.map((e) => e.message).join("; ")); +} +const racks = (data && data.data && data.data.rack_list) || []; + +// Optional role filter: autocomplete values arrive as [{ value }] (slug, or a custom typed name). +const roleFilter = new Set( + ((context.config && context.config.role) || []) + .map((r) => String(r && typeof r === "object" ? r.value : r).toLowerCase()) + .filter((r) => r && r !== "undefined"), +); +const hasRoleFilter = roleFilter.size > 0; +const matchesRole = (role) => + !hasRoleFilter || + (role && (roleFilter.has(String(role.slug).toLowerCase()) || roleFilter.has(String(role.name).toLowerCase()))); + +const unitRange = (lo, hi) => (lo === hi ? `${lo}` : `${lo}–${hi}`); + +const rows = []; +let order = 0; + +// Keep racks together, ordered by site then rack name. +const bySiteAndName = (a, b) => + ((a.site && a.site.name) || "").localeCompare((b.site && b.site.name) || "") || + String(a.name).localeCompare(String(b.name)) || + Number(a.id) - Number(b.id); + +for (const rack of [...racks].sort(bySiteAndName)) { + const startUnit = Number(rack.starting_unit) || 1; + const height = Number(rack.u_height) || 0; + const topUnit = startUnit + height - 1; + const base = { + rack: rack.name, + site: (rack.site && rack.site.name) || "", + rackId: String(rack.id), + }; + + // Split the rack's devices into those mounted at a position and the rest (0U, child devices, unracked). + const mounted = []; + const other = []; + for (const dev of rack.devices || []) { + if (!matchesRole(dev.role)) continue; + const dt = dev.device_type || {}; + const info = { + deviceId: String(dev.id), + device: dev.name || "(unnamed)", + role: (dev.role && dev.role.name) || "", + deviceType: dt.model || "", + manufacturer: (dt.manufacturer && dt.manufacturer.name) || "", + deviceHeight: Number(dt.u_height) || 0, + fullDepth: !!dt.is_full_depth, + face: dev.face === "rear" ? "Rear" : "Front", + }; + const pos = dev.position === null || dev.position === undefined ? null : Number(dev.position); + if (pos !== null && !Number.isNaN(pos) && info.deviceHeight > 0) { + // Whole units the device touches: a 0.5U device at 5.5 still occupies unit 5. + info.lo = Math.floor(pos); + info.hi = Math.max(Math.ceil(pos + info.deviceHeight) - 1, info.lo); + mounted.push(info); + } else { + info.kind = dev.parent_bay ? "Child" : info.deviceHeight === 0 ? "0U" : "Unracked"; + other.push(info); + } + } + + // NetBox renders a descending rack (desc_units) with the starting unit at the top. + const units = []; + for (let i = 0; i < height; i++) units.push(rack.desc_units ? startUnit + i : topUnit - i); + + for (const unit of units) { + const covering = mounted + .filter((d) => d.lo <= unit && d.hi >= unit) + .sort((a, b) => (a.fullDepth === b.fullDepth ? 0 : a.fullDepth ? -1 : 1) || (a.face === "Front" ? -1 : 1)); + if (!covering.length) { + // An empty slot is only meaningful when not filtering by role. + if (!hasRoleFilter) { + rows.push({ ...base, order: order++, unit, unitsLabel: `${unit}`, device: "", deviceId: "", role: "", deviceType: "", manufacturer: "", face: "", deviceHeight: null, isTopUnit: false, occupied: false }); + } + continue; + } + for (const d of covering) { + rows.push({ + ...base, + order: order++, + unit, + unitsLabel: unitRange(d.lo, d.hi), + device: d.device, + deviceId: d.deviceId, + role: d.role, + deviceType: d.deviceType, + manufacturer: d.manufacturer, + face: d.fullDepth ? "Both" : d.face, + deviceHeight: d.deviceHeight, + isTopUnit: unit === (rack.desc_units ? d.lo : d.hi), + occupied: true, + }); + } + } + + // 0U, child and unracked devices follow the units; they have no unit number. + for (const d of other) { + rows.push({ + ...base, + order: order++, + unit: null, + unitsLabel: d.kind, + device: d.device, + deviceId: d.deviceId, + role: d.role, + deviceType: d.deviceType, + manufacturer: d.manufacturer, + face: "", + deviceHeight: d.deviceHeight, + isTopUnit: false, + occupied: true, + }); + } +} + +result = rows; diff --git a/plugins/NetBox/v1/dataStreams/scripts/rackUtilization.js b/plugins/NetBox/v1/dataStreams/scripts/rackUtilization.js new file mode 100644 index 00000000..b8effe2f --- /dev/null +++ b/plugins/NetBox/v1/dataStreams/scripts/rackUtilization.js @@ -0,0 +1,60 @@ +// GraphQL returns { data: { rack_list: [ { id, name, u_height, starting_unit, site, location, devices: [...] } ] } }. +// Occupancy is computed per rack from every positioned device, in half-unit steps (position and height can be .5). +// A device occupies the slots [position, position + height). Slots are unioned across both faces, so a half-depth +// device on the front and another on the rear at the same U count once, and a full-depth device counts once. +// GraphQL reports query errors with HTTP 200 and an `errors` array (errorHandling does not fire on a 200), so surface them here. +if (data && Array.isArray(data.errors) && data.errors.length) { + throw new Error(data.errors.map((e) => e.message).filter(Boolean).join("; ")); +} +const racks = (data && data.data && data.data.rack_list) || []; + +const num = (v) => (v === null || typeof v === "undefined" || v === "" ? null : Number(v)); +const round = (v, dp) => Math.round(v * 10 ** dp) / 10 ** dp; +const faceOf = (d) => String(d.face || "").toLowerCase(); + +result = racks.map((rack) => { + const uHeight = num(rack.u_height) || 0; + const startingUnit = num(rack.starting_unit) || 1; + // Slots are half-units, so unit U maps to slot index U * 2. The rack spans [lo, hi). + const lo = startingUnit * 2; + const hi = lo + uHeight * 2; + + const both = new Set(); + const front = new Set(); + const rear = new Set(); + let deviceCount = 0; + + (rack.devices || []).forEach((d) => { + const position = num(d.position); + if (position === null) return; // child devices (in bays) and unracked devices have no position + deviceCount++; + + const type = d.device_type || {}; + const height = num(type.u_height) || 0; // 0U devices occupy no space + const start = Math.round(position * 2); + const end = start + Math.round(height * 2); + const fullDepth = type.is_full_depth !== false; + const face = faceOf(d); + + for (let s = Math.max(start, lo); s < Math.min(end, hi); s++) { + both.add(s); + if (fullDepth || face === "front") front.add(s); + if (fullDepth || face === "rear") rear.add(s); + } + }); + + const usedUnits = both.size / 2; + return { + rack: rack.name, + site: rack.site && rack.site.name, + location: (rack.location && rack.location.name) || "", + uHeight, + usedUnits, + availableUnits: Math.max(uHeight - usedUnits, 0), + utilization: uHeight ? round((usedUnits / uHeight) * 100, 1) : 0, + frontUsed: front.size / 2, + rearUsed: rear.size / 2, + deviceCount, + rackId: rack.id, + }; +}); diff --git a/plugins/NetBox/v1/dataStreams/sites.json b/plugins/NetBox/v1/dataStreams/sites.json new file mode 100644 index 00000000..a4afc14f --- /dev/null +++ b/plugins/NetBox/v1/dataStreams/sites.json @@ -0,0 +1,168 @@ +{ + "name": "sites", + "displayName": "Sites", + "description": "All sites with status, region, tenant and object counts", + "tags": [ + "Inventory", + "Network" + ], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "api/dcim/sites/", + "getArgs": [ + { + "key": "fields", + "value": "id,display,display_url,status,region,group,tenant,facility,time_zone,description,physical_address,latitude,longitude,device_count,rack_count,prefix_count,vlan_count,circuit_count,virtualmachine_count,created,last_updated,custom_fields" + } + ], + "paging": { + "mode": "offset", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "250" + }, + "offset": { + "mode": "row", + "base": 0, + "rowCountIn": { + "realm": "payloadArraySize", + "path": "results" + } + }, + "out": { + "realm": "queryArg", + "path": "offset" + } + }, + "pathToData": "results", + "expandInnerObjects": true + }, + "matches": "none", + "metadata": [ + { + "name": "id", + "visible": false + }, + { + "name": "display", + "displayName": "Name", + "shape": "string", + "role": "label" + }, + { + "name": "status.label", + "displayName": "Status", + "shape": "string" + }, + { + "name": "status.value", + "visible": false + }, + { + "name": "region.name", + "displayName": "Region", + "shape": "string" + }, + { + "name": "group.name", + "displayName": "Site Group", + "shape": "string" + }, + { + "name": "tenant.name", + "displayName": "Tenant", + "shape": "string" + }, + { + "name": "facility", + "displayName": "Facility", + "shape": "string" + }, + { + "name": "time_zone", + "displayName": "Time Zone", + "shape": "string" + }, + { + "name": "physical_address", + "displayName": "Address", + "shape": "string" + }, + { + "name": "latitude", + "displayName": "Latitude", + "shape": "number" + }, + { + "name": "longitude", + "displayName": "Longitude", + "shape": "number" + }, + { + "name": "device_count", + "displayName": "Devices", + "shape": "number" + }, + { + "name": "rack_count", + "displayName": "Racks", + "shape": "number" + }, + { + "name": "prefix_count", + "displayName": "Prefixes", + "shape": "number" + }, + { + "name": "vlan_count", + "displayName": "VLANs", + "shape": "number" + }, + { + "name": "circuit_count", + "displayName": "Circuits", + "shape": "number" + }, + { + "name": "virtualmachine_count", + "displayName": "Virtual Machines", + "shape": "number" + }, + { + "name": "description", + "displayName": "Description", + "shape": "string" + }, + { + "name": "created", + "displayName": "Created", + "shape": "date" + }, + { + "name": "last_updated", + "displayName": "Last Updated", + "shape": "date" + }, + { + "name": "display_url", + "displayName": "Link", + "shape": [ + "url", + { + "label": "Open in NetBox" + } + ] + }, + { + "sourceId": "id", + "sourceType": "Site", + "name": "display" + }, + { + "pattern": "^custom_fields[.].*" + } + ], + "timeframes": false +} diff --git a/plugins/NetBox/v1/dataStreams/status.json b/plugins/NetBox/v1/dataStreams/status.json new file mode 100644 index 00000000..54345e07 --- /dev/null +++ b/plugins/NetBox/v1/dataStreams/status.json @@ -0,0 +1,60 @@ +{ + "name": "status", + "displayName": "Instance Status", + "description": "NetBox version, installed plugins and background worker count for the connected instance", + "tags": [ + "Status" + ], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "api/status/" + }, + "matches": "none", + "metadata": [ + { + "name": "netbox-version", + "displayName": "NetBox Version", + "shape": "string", + "role": "label" + }, + { + "name": "netbox-full-version", + "displayName": "Full Version", + "shape": "string" + }, + { + "name": "django-version", + "displayName": "Django Version", + "shape": "string" + }, + { + "name": "python-version", + "displayName": "Python Version", + "shape": "string" + }, + { + "name": "rq-workers-running", + "displayName": "Workers Running", + "shape": "number", + "role": "value" + }, + { + "name": "hostname", + "displayName": "Hostname", + "shape": "string" + }, + { + "name": "plugins", + "visible": false + }, + { + "name": "pluginCount", + "displayName": "Plugins Installed", + "computed": true, + "valueExpression": "{{ Object.keys($['plugins'] || {}).length }}", + "shape": "number" + } + ], + "timeframes": false +} diff --git a/plugins/NetBox/v1/dataStreams/virtualMachines.json b/plugins/NetBox/v1/dataStreams/virtualMachines.json new file mode 100644 index 00000000..e95e7540 --- /dev/null +++ b/plugins/NetBox/v1/dataStreams/virtualMachines.json @@ -0,0 +1,181 @@ +{ + "name": "virtualMachines", + "displayName": "Virtual Machines", + "description": "All virtual machines with status, cluster, site, resources and primary IP", + "tags": [ + "Inventory", + "Virtualization" + ], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "api/virtualization/virtual-machines/", + "getArgs": [ + { + "key": "fields", + "value": "id,display,display_url,status,site,cluster,device,role,tenant,platform,primary_ip,vcpus,memory,disk,description,created,last_updated,custom_fields" + }, + { + "key": "site_id", + "value": "{{ (typeof site !== 'undefined' && site && site.length) ? site.map(s => [].concat(s.rawId)[0]) : null }}" + } + ], + "paging": { + "mode": "offset", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "250" + }, + "offset": { + "mode": "row", + "base": 0, + "rowCountIn": { + "realm": "payloadArraySize", + "path": "results" + } + }, + "out": { + "realm": "queryArg", + "path": "offset" + } + }, + "pathToData": "results", + "expandInnerObjects": true + }, + "matches": "none", + "ui": [ + { + "type": "objects", + "name": "site", + "label": "Site (optional)", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Site" + ] + } + } + } + ], + "metadata": [ + { + "name": "id", + "visible": false + }, + { + "name": "display", + "displayName": "Name", + "shape": "string", + "role": "label" + }, + { + "name": "status.label", + "displayName": "Status", + "shape": "string" + }, + { + "name": "status.value", + "visible": false + }, + { + "name": "cluster.name", + "displayName": "Cluster", + "shape": "string" + }, + { + "name": "site.name", + "displayName": "Site", + "shape": "string" + }, + { + "name": "site.id", + "visible": false + }, + { + "name": "site.url", + "visible": false, + "valueExpression": "{{ typeof $['site.id'] === 'number' ? String($['site.id']) : null }}" + }, + { + "name": "device.name", + "displayName": "Host Device", + "shape": "string" + }, + { + "name": "role.name", + "displayName": "Role", + "shape": "string" + }, + { + "name": "tenant.name", + "displayName": "Tenant", + "shape": "string" + }, + { + "name": "platform.name", + "displayName": "Platform", + "shape": "string" + }, + { + "name": "primary_ip.address", + "visible": false + }, + { + "name": "primary_ip.display", + "displayName": "Primary IP", + "shape": "string", + "valueExpression": "{{ typeof $['primary_ip.address'] === 'string' && $['primary_ip.address'] ? $['primary_ip.address'].split('/')[0] : null }}" + }, + { + "name": "vcpus", + "displayName": "vCPUs", + "shape": "number" + }, + { + "name": "memory", + "displayName": "Memory", + "shape": "megabytes" + }, + { + "name": "disk", + "displayName": "Disk", + "shape": "megabytes" + }, + { + "name": "description", + "displayName": "Description", + "shape": "string" + }, + { + "name": "created", + "displayName": "Created", + "shape": "date" + }, + { + "name": "last_updated", + "displayName": "Last Updated", + "shape": "date" + }, + { + "name": "display_url", + "displayName": "Link", + "shape": [ + "url", + { + "label": "Open in NetBox" + } + ] + }, + { + "sourceId": "id", + "sourceType": "Virtual Machine", + "name": "display" + }, + { + "pattern": "^custom_fields[.].*" + } + ], + "timeframes": false +} diff --git a/plugins/NetBox/v1/dataStreams/vlanGroups.json b/plugins/NetBox/v1/dataStreams/vlanGroups.json new file mode 100644 index 00000000..00bb96f4 --- /dev/null +++ b/plugins/NetBox/v1/dataStreams/vlanGroups.json @@ -0,0 +1,147 @@ +{ + "name": "vlanGroups", + "displayName": "VLAN Groups", + "description": "VLAN groups with scope, VLAN ID ranges and utilization", + "tags": [ + "IPAM", + "Capacity" + ], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "api/ipam/vlan-groups/", + "getArgs": [ + { + "key": "fields", + "value": "id,display,display_url,name,slug,scope_type,scope,tenant,vid_ranges,total_vlan_ids,vlan_count,utilization,description,last_updated,custom_fields" + } + ], + "paging": { + "mode": "offset", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "250" + }, + "offset": { + "mode": "row", + "base": 0, + "rowCountIn": { + "realm": "payloadArraySize", + "path": "results" + } + }, + "out": { + "realm": "queryArg", + "path": "offset" + } + }, + "pathToData": "results", + "expandInnerObjects": true + }, + "matches": "none", + "defaultShaping": { + "sort": { + "by": [ + [ + "utilizationPct", + "desc" + ] + ] + } + }, + "metadata": [ + { + "name": "id", + "visible": false + }, + { + "name": "display", + "displayName": "Name", + "shape": "string", + "role": "label" + }, + { + "name": "scope_type", + "visible": false + }, + { + "name": "scopeType", + "displayName": "Scope Type", + "shape": "string", + "computed": true, + "valueExpression": "{{ typeof $['scope_type'] === 'string' && $['scope_type'] ? (({ site: 'Site', sitegroup: 'Site Group', region: 'Region', location: 'Location', rack: 'Rack', cluster: 'Cluster', clustergroup: 'Cluster Group' })[$['scope_type'].split('.').pop()] || $['scope_type']) : null }}" + }, + { + "name": "scope.name", + "displayName": "Scope", + "shape": "string" + }, + { + "name": "tenant.name", + "displayName": "Tenant", + "shape": "string" + }, + { + "name": "vid_ranges", + "visible": false + }, + { + "name": "vidRanges", + "displayName": "VLAN ID Ranges", + "shape": "string", + "computed": true, + "valueExpression": "{{ (() => { let r = $['vid_ranges']; if (typeof r === 'string') { try { r = JSON.parse(r); } catch (e) { return null; } } return Array.isArray(r) ? r.map(x => x[0] === x[1] ? String(x[0]) : x[0] + '-' + x[1]).join(', ') : null; })() }}" + }, + { + "name": "total_vlan_ids", + "displayName": "Total VLAN IDs", + "shape": "number" + }, + { + "name": "vlan_count", + "displayName": "VLANs", + "shape": "number" + }, + { + "name": "utilization", + "visible": false + }, + { + "name": "utilizationPct", + "displayName": "Utilization", + "shape": [ + "percent", + { + "decimalPlaces": 1 + } + ], + "computed": true, + "valueExpression": "{{ Number.isFinite(parseFloat($['utilization'])) ? parseFloat($['utilization']) : null }}" + }, + { + "name": "description", + "displayName": "Description", + "shape": "string" + }, + { + "name": "last_updated", + "displayName": "Last Updated", + "shape": "date" + }, + { + "name": "display_url", + "displayName": "Link", + "shape": [ + "url", + { + "label": "Open in NetBox" + } + ] + }, + { + "pattern": "^custom_fields[.].*" + } + ], + "timeframes": false +} diff --git a/plugins/NetBox/v1/defaultContent/changeActivity.dash.json b/plugins/NetBox/v1/defaultContent/changeActivity.dash.json new file mode 100644 index 00000000..d0594551 --- /dev/null +++ b/plugins/NetBox/v1/defaultContent/changeActivity.dash.json @@ -0,0 +1,677 @@ +{ + "name": "Change Activity", + "schemaVersion": "1.5", + "timeframe": "last7days", + "variables": [], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "56fb80c3-dfd7-415f-9aba-59a3b3c27dc5", + "x": 0, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Total Changes", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[objectChanges]}}", + "name": "objectChanges", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Changes" + } + } + } + } + }, + { + "i": "bf1df4dc-5ec0-486d-9ee3-8c458a20f5c1", + "x": 1, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Active Users", + "description": "Users who made at least one change", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[objectChanges]}}", + "name": "objectChanges", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "distinctCount", + "names": [ + "user_name" + ] + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "user_name_distinctCount", + "comparisonColumn": "none", + "label": "Users" + } + } + } + } + }, + { + "i": "423fe515-ea86-41c6-a167-80c400bba75a", + "x": 2, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Objects Created", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[objectChanges]}}", + "name": "objectChanges", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "action.value", + "operation": "equals", + "value": "create" + } + ] + }, + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Created" + } + } + } + } + }, + { + "i": "0ca0fc13-e4a1-445a-a398-0bceb6d8d5aa", + "x": 3, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Objects Deleted", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[objectChanges]}}", + "name": "objectChanges", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "action.value", + "operation": "equals", + "value": "delete" + } + ] + }, + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Deleted" + } + } + } + } + }, + { + "i": "04eacbc6-1e99-4fe7-a7ce-fdec18ef8aa0", + "x": 0, + "y": 2, + "w": 3, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Changes Over Time", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[objectChanges]}}", + "name": "objectChanges", + "pluginConfigId": "{{configId}}", + "group": { + "by": [ + [ + "time", + "byDay" + ], + [ + "action.label", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + }, + "sort": { + "by": [ + [ + "time_byDay", + "asc" + ] + ] + } + }, + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisData": "time_byDay", + "yAxisData": [ + "count" + ], + "xAxisGroup": "action.label_uniqueValues", + "xAxisLabel": "", + "yAxisLabel": "Changes", + "showXAxisLabel": false, + "showYAxisLabel": true, + "showLegend": true, + "legendPosition": "bottom", + "showGrid": true, + "horizontalLayout": "vertical", + "displayMode": "actual", + "showTotals": false, + "showValue": false, + "grouping": false, + "range": { + "type": "auto" + } + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "bar", + "encoding": { + "x": { + "field": "time_byDay", + "type": "temporal", + "title": "Day" + }, + "y": { + "field": "count", + "type": "quantitative", + "title": "Changes" + }, + "series": { + "field": "action.label_uniqueValues", + "type": "nominal", + "title": "Action" + } + }, + "style": { + "cartesianStack": "on", + "legendPosition": "bottom", + "mappedColors": { + "Created": "#2fb344", + "Updated": "#206bc4", + "Deleted": "#d63939" + } + } + } + } + } + }, + { + "i": "b9d25f0e-f8ba-4fc2-b7f2-ae9d7a6eca32", + "x": 3, + "y": 2, + "w": 1, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Changes by Action", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[objectChanges]}}", + "name": "objectChanges", + "pluginConfigId": "{{configId}}", + "group": { + "by": [ + [ + "action.label", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-donut-chart", + "config": { + "data-stream-donut-chart": { + "valueColumn": "count", + "labelColumn": "action.label_uniqueValues", + "hideCenterValue": false, + "showValuesAsPercentage": false, + "legendPosition": "auto", + "legendMode": "table" + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "pie", + "encoding": { + "label": { + "field": "action.label_uniqueValues", + "type": "nominal", + "title": "Action" + }, + "value": { + "field": "count", + "type": "quantitative", + "title": "Count" + } + }, + "style": { + "pieInnerRadius": 60, + "pieDonutCenterValue": "total", + "labelFontSize": 16, + "pieLegendStyle": "table", + "mappedColors": { + "Created": "#2fb344", + "Updated": "#206bc4", + "Deleted": "#d63939" + } + } + } + } + } + }, + { + "i": "7e03f124-829d-4ebb-a459-66e5ad1a39b4", + "x": 0, + "y": 5, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Top 10 Users by Changes", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[objectChanges]}}", + "name": "objectChanges", + "pluginConfigId": "{{configId}}", + "group": { + "by": [ + [ + "user_name", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + }, + "sort": { + "by": [ + [ + "count", + "desc" + ] + ], + "top": 10 + } + }, + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisData": "user_name_uniqueValues", + "yAxisData": [ + "count" + ], + "xAxisGroup": "none", + "xAxisLabel": "", + "yAxisLabel": "", + "showXAxisLabel": false, + "showYAxisLabel": false, + "showLegend": false, + "legendPosition": "bottom", + "showGrid": true, + "horizontalLayout": "horizontal", + "displayMode": "actual", + "showTotals": false, + "showValue": false, + "grouping": false, + "range": { + "type": "auto" + } + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "bar", + "encoding": { + "x": { + "field": "user_name_uniqueValues", + "type": "nominal", + "title": "User" + }, + "y": { + "field": "count", + "type": "quantitative", + "title": "Changes" + } + }, + "style": { + "cartesianHorizontal": true + } + } + } + } + }, + { + "i": "67c086f7-8b28-4e84-9e99-3b0095630bd5", + "x": 2, + "y": 5, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Top 10 Object Types by Changes", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[objectChanges]}}", + "name": "objectChanges", + "pluginConfigId": "{{configId}}", + "group": { + "by": [ + [ + "changed_object_type", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + }, + "sort": { + "by": [ + [ + "count", + "desc" + ] + ], + "top": 10 + } + }, + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisData": "changed_object_type_uniqueValues", + "yAxisData": [ + "count" + ], + "xAxisGroup": "none", + "xAxisLabel": "", + "yAxisLabel": "", + "showXAxisLabel": false, + "showYAxisLabel": false, + "showLegend": false, + "legendPosition": "bottom", + "showGrid": true, + "horizontalLayout": "horizontal", + "displayMode": "actual", + "showTotals": false, + "showValue": false, + "grouping": false, + "range": { + "type": "auto" + } + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "bar", + "encoding": { + "x": { + "field": "changed_object_type_uniqueValues", + "type": "nominal", + "title": "Object Type" + }, + "y": { + "field": "count", + "type": "quantitative", + "title": "Changes" + } + }, + "style": { + "cartesianHorizontal": true + } + } + } + } + }, + { + "i": "deaf1b8a-00db-4774-8b7f-db8771fc3930", + "x": 0, + "y": 8, + "w": 4, + "h": 5, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Recent Changes", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[objectChanges]}}", + "name": "objectChanges", + "pluginConfigId": "{{configId}}", + "sort": { + "by": [ + [ + "time", + "desc" + ] + ] + } + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "time", + "user_name", + "action.label", + "changed_object_type", + "object_repr", + "message" + ], + "hiddenColumns": [] + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "time", + "title": "Time", + "renderer": { + "type": "date", + "relative": true + } + }, + { + "field": "user_name", + "title": "User", + "renderer": { + "type": "text" + } + }, + { + "field": "action.label", + "title": "Action", + "renderer": { + "type": "badge", + "colors": { + "Created": "#2fb344", + "Updated": "#206bc4", + "Deleted": "#d63939" + } + } + }, + { + "field": "changed_object_type", + "title": "Object Type", + "renderer": { + "type": "text" + } + }, + { + "field": "object_repr", + "title": "Object", + "renderer": { + "type": "text" + } + }, + { + "field": "message", + "title": "Message", + "renderer": { + "type": "text" + } + } + ] + } + } + } + } + } + ] + } +} diff --git a/plugins/NetBox/v1/defaultContent/circuit.dash.json b/plugins/NetBox/v1/defaultContent/circuit.dash.json new file mode 100644 index 00000000..54214db2 --- /dev/null +++ b/plugins/NetBox/v1/defaultContent/circuit.dash.json @@ -0,0 +1,179 @@ +{ + "name": "Circuit", + "schemaVersion": "1.5", + "timeframe": "last30days", + "variables": [ + "{{variables.[NetBox Circuit]}}" + ], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "783841cf-dd6e-4715-9a54-7b8479b8c2a3", + "x": 0, + "y": 0, + "w": 1, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Circuit Properties", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "datastream-properties", + "name": "properties", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[NetBox Circuits]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[NetBox Circuit]}}" + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": true, + "columnOrder": [ + "name" + ], + "hiddenColumns": [ + "id", + "links", + "type", + "label", + "sourceId", + "sourceType", + "siteId", + "rackId", + "vrfId" + ] + } + } + }, + "variables": [ + "{{variables.[NetBox Circuit]}}" + ] + } + }, + { + "i": "af52a83e-2c86-4c5b-a474-42ac34a20a27", + "x": 1, + "y": 0, + "w": 3, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Change Log", + "description": "Changes recorded against this circuit", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[objectChanges]}}", + "name": "objectChanges", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "object": { + "variable": "{{variables.[NetBox Circuit]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[NetBox Circuits]}}" + } + }, + "sort": { + "by": [ + [ + "time", + "desc" + ] + ] + } + }, + "variables": [ + "{{variables.[NetBox Circuit]}}" + ], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "time", + "user_name", + "action.label", + "object_repr", + "message" + ], + "hiddenColumns": [ + "changed_object_type" + ] + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "time", + "title": "Time", + "renderer": { + "type": "date", + "relative": true + } + }, + { + "field": "user_name", + "title": "User", + "renderer": { + "type": "text" + } + }, + { + "field": "action.label", + "title": "Action", + "renderer": { + "type": "badge", + "colors": { + "Created": "#2fb344", + "Updated": "#206bc4", + "Deleted": "#d63939" + } + } + }, + { + "field": "object_repr", + "title": "Object", + "renderer": { + "type": "text" + } + }, + { + "field": "message", + "title": "Message", + "renderer": { + "type": "text" + } + } + ] + } + } + } + } + } + ] + } +} diff --git a/plugins/NetBox/v1/defaultContent/dataCenters.dash.json b/plugins/NetBox/v1/defaultContent/dataCenters.dash.json new file mode 100644 index 00000000..88568317 --- /dev/null +++ b/plugins/NetBox/v1/defaultContent/dataCenters.dash.json @@ -0,0 +1,771 @@ +{ + "name": "Data Centers", + "schemaVersion": "1.5", + "timeframe": "none", + "variables": [], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "eb418e62-e67c-4beb-8029-2582854cacb9", + "x": 0, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Sites", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[sites]}}", + "name": "sites", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Sites" + } + } + } + } + }, + { + "i": "fb8d1ef6-967b-4998-80e1-a1ba58040461", + "x": 1, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Racks", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[sites]}}", + "name": "sites", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "sum", + "names": [ + "rack_count" + ] + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "rack_count_sum", + "comparisonColumn": "none", + "label": "Racks" + } + } + } + } + }, + { + "i": "5e3dc2fe-2b5d-430c-bda1-4f36b895bb4a", + "x": 2, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Devices", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[sites]}}", + "name": "sites", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "sum", + "names": [ + "device_count" + ] + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "device_count_sum", + "comparisonColumn": "none", + "label": "Devices" + } + } + } + } + }, + { + "i": "aeedc5c8-9dea-4aca-a76f-4ee8fde44d88", + "x": 3, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Circuits", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[sites]}}", + "name": "sites", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "sum", + "names": [ + "circuit_count" + ] + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "circuit_count_sum", + "comparisonColumn": "none", + "label": "Circuits" + } + } + } + } + }, + { + "i": "20ece744-d1e4-4e78-9f92-072d09f28766", + "x": 0, + "y": 2, + "w": 4, + "h": 6, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Site Map", + "description": "Sites plotted by latitude and longitude, coloured by status. Sites without coordinates are not shown.", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[sites]}}", + "name": "sites", + "pluginConfigId": "{{configId}}", + "sort": { + "by": [ + [ + "display", + "asc" + ] + ] + } + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "display", + "status.label", + "region.name", + "facility", + "physical_address", + "latitude", + "longitude", + "display_url" + ], + "hiddenColumns": [ + "group.name", + "tenant.name", + "time_zone", + "device_count", + "rack_count", + "prefix_count", + "vlan_count", + "circuit_count", + "virtualmachine_count", + "description", + "created", + "last_updated" + ] + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "geoMap", + "encoding": { + "latitude": { + "field": "latitude", + "type": "quantitative", + "title": "Latitude" + }, + "longitude": { + "field": "longitude", + "type": "quantitative", + "title": "Longitude" + }, + "label": { + "field": "display", + "type": "nominal", + "title": "Site" + }, + "status": { + "field": "status.value", + "type": "nominal", + "title": "Status" + }, + "details": [ + { + "field": "status.label", + "type": "nominal", + "title": "Status" + }, + { + "field": "region.name", + "type": "nominal", + "title": "Region" + }, + { + "field": "facility", + "type": "nominal", + "title": "Facility" + }, + { + "field": "tenant.name", + "type": "nominal", + "title": "Tenant" + }, + { + "field": "rack_count", + "type": "quantitative", + "title": "Racks" + }, + { + "field": "device_count", + "type": "quantitative", + "title": "Devices" + } + ] + }, + "style": { + "geoMapBase": "auto", + "geoMapStatusMap": { + "active": "success", + "planned": "unknown", + "staging": "warning", + "decommissioning": "warning", + "retired": "unknown" + }, + "geoMapSymbol": "circle", + "geoMapPointSize": 10, + "geoMapZoomControls": true, + "geoMapWheelZoom": false, + "showLabels": true + } + } + } + } + }, + { + "i": "f9376cdd-325b-4c51-9b0f-283078283909", + "x": 0, + "y": 8, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Sites by Region", + "description": "Sites with a region assigned", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[sites]}}", + "name": "sites", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "region.name", + "operation": "notempty" + } + ] + }, + "group": { + "by": [ + [ + "region.name", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + }, + "sort": { + "by": [ + [ + "count", + "desc" + ] + ], + "top": 15 + } + }, + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisData": "region.name_uniqueValues", + "yAxisData": [ + "count" + ], + "xAxisGroup": "none", + "xAxisLabel": "", + "yAxisLabel": "", + "showXAxisLabel": false, + "showYAxisLabel": false, + "showLegend": false, + "legendPosition": "bottom", + "showGrid": true, + "horizontalLayout": "horizontal", + "displayMode": "actual", + "showTotals": false, + "showValue": false, + "grouping": false, + "range": { + "type": "auto" + } + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "bar", + "encoding": { + "x": { + "field": "region.name_uniqueValues", + "type": "nominal", + "title": "Region" + }, + "y": { + "field": "count", + "type": "quantitative", + "title": "Sites" + } + }, + "style": { + "cartesianHorizontal": true + } + } + } + } + }, + { + "i": "d175afab-8891-478a-bfe2-5e307faebb90", + "x": 2, + "y": 8, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Sites by Status", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[sites]}}", + "name": "sites", + "pluginConfigId": "{{configId}}", + "group": { + "by": [ + [ + "status.label", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-donut-chart", + "config": { + "data-stream-donut-chart": { + "valueColumn": "count", + "labelColumn": "status.label_uniqueValues", + "hideCenterValue": false, + "showValuesAsPercentage": false, + "legendPosition": "auto", + "legendMode": "table" + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "pie", + "encoding": { + "label": { + "field": "status.label_uniqueValues", + "type": "nominal", + "title": "Status" + }, + "value": { + "field": "count", + "type": "quantitative", + "title": "Count" + } + }, + "style": { + "pieInnerRadius": 60, + "pieDonutCenterValue": "total", + "labelFontSize": 16, + "pieLegendStyle": "table", + "mappedColors": { + "Active": "#2fb344", + "Planned": "#17a2b8", + "Staging": "#206bc4", + "Decommissioning": "#f59f00", + "Retired": "#d63939" + } + } + } + } + } + }, + { + "i": "ac6a9f18-a7f3-4a7c-944d-c9375f44d8e9", + "x": 0, + "y": 12, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Sites by Tenant", + "description": "Sites with a tenant assigned", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[sites]}}", + "name": "sites", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "tenant.name", + "operation": "notempty" + } + ] + }, + "group": { + "by": [ + [ + "tenant.name", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + }, + "sort": { + "by": [ + [ + "count", + "desc" + ] + ], + "top": 15 + } + }, + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisData": "tenant.name_uniqueValues", + "yAxisData": [ + "count" + ], + "xAxisGroup": "none", + "xAxisLabel": "", + "yAxisLabel": "", + "showXAxisLabel": false, + "showYAxisLabel": false, + "showLegend": false, + "legendPosition": "bottom", + "showGrid": true, + "horizontalLayout": "horizontal", + "displayMode": "actual", + "showTotals": false, + "showValue": false, + "grouping": false, + "range": { + "type": "auto" + } + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "bar", + "encoding": { + "x": { + "field": "tenant.name_uniqueValues", + "type": "nominal", + "title": "Tenant" + }, + "y": { + "field": "count", + "type": "quantitative", + "title": "Sites" + } + }, + "style": { + "cartesianHorizontal": true + } + } + } + } + }, + { + "i": "b75e7915-fec0-4f5d-af13-24f50c493f40", + "x": 2, + "y": 12, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Top 10 Sites by Racks", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[sites]}}", + "name": "sites", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "rack_count", + "operation": "greaterthan", + "value": "0" + } + ] + }, + "sort": { + "by": [ + [ + "rack_count", + "desc" + ] + ], + "top": 10 + } + }, + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisData": "display", + "yAxisData": [ + "rack_count" + ], + "xAxisGroup": "none", + "xAxisLabel": "", + "yAxisLabel": "", + "showXAxisLabel": false, + "showYAxisLabel": false, + "showLegend": false, + "legendPosition": "bottom", + "showGrid": true, + "horizontalLayout": "horizontal", + "displayMode": "actual", + "showTotals": false, + "showValue": false, + "grouping": false, + "range": { + "type": "auto" + } + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "bar", + "encoding": { + "x": { + "field": "display", + "type": "nominal", + "title": "Site" + }, + "y": { + "field": "rack_count", + "type": "quantitative", + "title": "Racks" + } + }, + "style": { + "cartesianHorizontal": true + } + } + } + } + }, + { + "i": "df74f570-61c6-4c94-98fa-c1e0692ed2db", + "x": 0, + "y": 16, + "w": 4, + "h": 6, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Data Center Sites", + "description": "All sites, including any site custom fields", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[sites]}}", + "name": "sites", + "pluginConfigId": "{{configId}}", + "sort": { + "by": [ + [ + "display", + "asc" + ] + ] + } + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "display", + "status.label", + "region.name", + "group.name", + "facility", + "tenant.name", + "time_zone", + "physical_address", + "rack_count", + "device_count", + "circuit_count", + "display_url" + ], + "hiddenColumns": [ + "latitude", + "longitude", + "prefix_count", + "vlan_count", + "virtualmachine_count", + "description", + "created", + "last_updated" + ] + } + } + } + } + } + ] + } +} diff --git a/plugins/NetBox/v1/defaultContent/dataQuality.dash.json b/plugins/NetBox/v1/defaultContent/dataQuality.dash.json new file mode 100644 index 00000000..5b549c7e --- /dev/null +++ b/plugins/NetBox/v1/defaultContent/dataQuality.dash.json @@ -0,0 +1,1101 @@ +{ + "name": "Data Quality", + "schemaVersion": "1.5", + "timeframe": "none", + "variables": [], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "aa5f7ca1-7d27-4e8a-9eae-3027afe37ba9", + "x": 0, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Devices Missing Serial", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[devices]}}", + "name": "devices", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "missingSerial", + "operation": "equals", + "value": "true" + } + ] + }, + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Devices" + } + } + } + } + }, + { + "i": "081f5de9-5ec8-492d-92da-d528d02eed0c", + "x": 1, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Devices Missing Primary IP", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[devices]}}", + "name": "devices", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "missingPrimaryIp", + "operation": "equals", + "value": "true" + } + ] + }, + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Devices" + } + } + } + } + }, + { + "i": "1fc7ffac-15b2-49e1-8a3a-79f1e725b9b6", + "x": 2, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Devices Missing Tenant", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[devices]}}", + "name": "devices", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "missingTenant", + "operation": "equals", + "value": "true" + } + ] + }, + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Devices" + } + } + } + } + }, + { + "i": "e5954e24-f656-43fb-b9fe-b6dfb3349c62", + "x": 3, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Devices Missing Platform", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[devices]}}", + "name": "devices", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "missingPlatform", + "operation": "equals", + "value": "true" + } + ] + }, + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Devices" + } + } + } + } + }, + { + "i": "5489be6a-e524-4279-bda5-9f7071de194f", + "x": 0, + "y": 2, + "w": 2, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Incomplete Devices", + "description": "Devices missing a serial, primary IP, tenant or platform", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[devices]}}", + "name": "devices", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "or", + "filters": [ + { + "column": "missingSerial", + "operation": "equals", + "value": "true" + }, + { + "column": "missingPrimaryIp", + "operation": "equals", + "value": "true" + }, + { + "column": "missingTenant", + "operation": "equals", + "value": "true" + }, + { + "column": "missingPlatform", + "operation": "equals", + "value": "true" + } + ] + }, + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Devices" + } + } + } + } + }, + { + "i": "8565907d-84a9-4f6e-96b8-2ac23dc24719", + "x": 2, + "y": 2, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Stale Devices", + "description": "Devices not updated in over a year", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[devices]}}", + "name": "devices", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "daysSinceUpdate", + "operation": "greaterthan", + "value": "365" + } + ] + }, + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Devices" + } + } + } + } + }, + { + "i": "5bf30f9d-0db8-4360-b52c-d192a5e72d1c", + "x": 3, + "y": 2, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Unassigned IP Addresses", + "description": "IP addresses not assigned to an interface", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[ipAddresses]}}", + "name": "ipAddresses", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "assignment": "false" + }, + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "IP Addresses" + } + } + } + } + }, + { + "i": "5dfd6fa5-d7eb-48ad-b374-ee1c4a7422fe", + "x": 0, + "y": 4, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Incomplete Devices by Site", + "description": "Top 10 sites by devices missing a serial, primary IP, tenant or platform", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[devices]}}", + "name": "devices", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "or", + "filters": [ + { + "column": "missingSerial", + "operation": "equals", + "value": "true" + }, + { + "column": "missingPrimaryIp", + "operation": "equals", + "value": "true" + }, + { + "column": "missingTenant", + "operation": "equals", + "value": "true" + }, + { + "column": "missingPlatform", + "operation": "equals", + "value": "true" + } + ] + }, + "group": { + "by": [ + [ + "site.name", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + }, + "sort": { + "by": [ + [ + "count", + "desc" + ] + ], + "top": 10 + } + }, + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisData": "site.name_uniqueValues", + "yAxisData": [ + "count" + ], + "xAxisGroup": "none", + "xAxisLabel": "", + "yAxisLabel": "", + "showXAxisLabel": false, + "showYAxisLabel": false, + "showLegend": false, + "legendPosition": "bottom", + "showGrid": true, + "horizontalLayout": "horizontal", + "displayMode": "actual", + "showTotals": false, + "showValue": false, + "grouping": false, + "range": { + "type": "auto" + } + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "bar", + "encoding": { + "x": { + "field": "site.name_uniqueValues", + "type": "nominal", + "title": "Site" + }, + "y": { + "field": "count", + "type": "quantitative", + "title": "Devices" + } + }, + "style": { + "cartesianHorizontal": true + } + } + } + } + }, + { + "i": "d2c41a8e-79ce-47d0-a779-588d421ed4ac", + "x": 2, + "y": 4, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Incomplete Devices by Role", + "description": "Top 10 roles by devices missing a serial, primary IP, tenant or platform", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[devices]}}", + "name": "devices", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "or", + "filters": [ + { + "column": "missingSerial", + "operation": "equals", + "value": "true" + }, + { + "column": "missingPrimaryIp", + "operation": "equals", + "value": "true" + }, + { + "column": "missingTenant", + "operation": "equals", + "value": "true" + }, + { + "column": "missingPlatform", + "operation": "equals", + "value": "true" + } + ] + }, + "group": { + "by": [ + [ + "role.name", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + }, + "sort": { + "by": [ + [ + "count", + "desc" + ] + ], + "top": 10 + } + }, + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisData": "role.name_uniqueValues", + "yAxisData": [ + "count" + ], + "xAxisGroup": "none", + "xAxisLabel": "", + "yAxisLabel": "", + "showXAxisLabel": false, + "showYAxisLabel": false, + "showLegend": false, + "legendPosition": "bottom", + "showGrid": true, + "horizontalLayout": "horizontal", + "displayMode": "actual", + "showTotals": false, + "showValue": false, + "grouping": false, + "range": { + "type": "auto" + } + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "bar", + "encoding": { + "x": { + "field": "role.name_uniqueValues", + "type": "nominal", + "title": "Role" + }, + "y": { + "field": "count", + "type": "quantitative", + "title": "Devices" + } + }, + "style": { + "cartesianHorizontal": true + } + } + } + } + }, + { + "i": "6bb34eec-6762-4ff0-b057-ba4e2ec61159", + "x": 0, + "y": 7, + "w": 4, + "h": 5, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Devices with Data Gaps", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[devices]}}", + "name": "devices", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "or", + "filters": [ + { + "column": "missingSerial", + "operation": "equals", + "value": "true" + }, + { + "column": "missingPrimaryIp", + "operation": "equals", + "value": "true" + }, + { + "column": "missingTenant", + "operation": "equals", + "value": "true" + }, + { + "column": "missingPlatform", + "operation": "equals", + "value": "true" + } + ] + }, + "sort": { + "by": [ + [ + "site.name", + "asc" + ], + [ + "display", + "asc" + ] + ] + } + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "display", + "status.label", + "role.name", + "site.name", + "missingSerial", + "missingPrimaryIp", + "missingTenant", + "missingPlatform", + "serial", + "primary_ip.display", + "tenant.name", + "platform.name", + "display_url" + ], + "hiddenColumns": [ + "daysSinceUpdate", + "created", + "description", + "oob_ip.address", + "asset_tag", + "device_type.manufacturer.name", + "device_type.model", + "rack.name", + "position", + "location.name" + ] + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "display_url", + "title": "Device", + "renderer": { + "type": "link", + "labelField": "display" + } + }, + { + "field": "status.label", + "title": "Status", + "renderer": { + "type": "badge", + "colors": { + "Active": "#2fb344", + "Planned": "#17a2b8", + "Staged": "#206bc4", + "Failed": "#d63939", + "Offline": "#9ba3af", + "Inventory": "#ae3ec9", + "Decommissioning": "#f59f00" + } + } + }, + { + "field": "role.name", + "title": "Role", + "renderer": { + "type": "text" + } + }, + { + "field": "site.name", + "title": "Site", + "renderer": { + "type": "text" + } + }, + { + "field": "missingSerial", + "title": "Serial", + "renderer": { + "type": "boolean", + "trueLabel": "Missing", + "falseLabel": "—", + "trueColor": "#f59f00", + "falseColor": "#9ba3af" + }, + "align": "center" + }, + { + "field": "missingPrimaryIp", + "title": "Primary IP", + "renderer": { + "type": "boolean", + "trueLabel": "Missing", + "falseLabel": "—", + "trueColor": "#f59f00", + "falseColor": "#9ba3af" + }, + "align": "center" + }, + { + "field": "missingTenant", + "title": "Tenant", + "renderer": { + "type": "boolean", + "trueLabel": "Missing", + "falseLabel": "—", + "trueColor": "#f59f00", + "falseColor": "#9ba3af" + }, + "align": "center" + }, + { + "field": "missingPlatform", + "title": "Platform", + "renderer": { + "type": "boolean", + "trueLabel": "Missing", + "falseLabel": "—", + "trueColor": "#f59f00", + "falseColor": "#9ba3af" + }, + "align": "center" + }, + { + "field": "last_updated", + "title": "Last Updated", + "renderer": { + "type": "date", + "relative": true + } + } + ] + } + } + } + } + }, + { + "i": "517507f4-2275-4b77-925d-b99dac353728", + "x": 0, + "y": 12, + "w": 4, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Stale Device Details", + "description": "Devices not updated in over a year", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[devices]}}", + "name": "devices", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "daysSinceUpdate", + "operation": "greaterthan", + "value": "365" + } + ] + }, + "sort": { + "by": [ + [ + "daysSinceUpdate", + "desc" + ] + ] + } + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "display", + "daysSinceUpdate", + "last_updated", + "status.label", + "role.name", + "site.name", + "device_type.manufacturer.name", + "device_type.model", + "display_url" + ], + "hiddenColumns": [ + "missingSerial", + "missingPrimaryIp", + "missingTenant", + "missingPlatform", + "created", + "description", + "oob_ip.address", + "asset_tag" + ] + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "display_url", + "title": "Device", + "renderer": { + "type": "link", + "labelField": "display" + } + }, + { + "field": "daysSinceUpdate", + "title": "Days Since Update", + "renderer": { + "type": "number" + }, + "align": "right" + }, + { + "field": "last_updated", + "title": "Last Updated", + "renderer": { + "type": "date", + "relative": true + } + }, + { + "field": "status.label", + "title": "Status", + "renderer": { + "type": "badge", + "colors": { + "Active": "#2fb344", + "Planned": "#17a2b8", + "Staged": "#206bc4", + "Failed": "#d63939", + "Offline": "#9ba3af", + "Inventory": "#ae3ec9", + "Decommissioning": "#f59f00" + } + } + }, + { + "field": "role.name", + "title": "Role", + "renderer": { + "type": "text" + } + }, + { + "field": "site.name", + "title": "Site", + "renderer": { + "type": "text" + } + }, + { + "field": "device_type.manufacturer.name", + "title": "Manufacturer", + "renderer": { + "type": "text" + } + }, + { + "field": "device_type.model", + "title": "Model", + "renderer": { + "type": "text" + } + } + ] + } + } + } + } + }, + { + "i": "edf1a364-5f9b-41d1-99da-d9a487e3ad2d", + "x": 0, + "y": 16, + "w": 4, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Unassigned IP Address Details", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[ipAddresses]}}", + "name": "ipAddresses", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "assignment": "false" + }, + "sort": { + "by": [ + [ + "address", + "asc" + ] + ] + } + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "address", + "status.label", + "vrfName", + "dns_name", + "tenant.name", + "description", + "last_updated", + "display_url" + ], + "hiddenColumns": [ + "display", + "created", + "family.label", + "assigned", + "assignedTo", + "role.label" + ] + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "display_url", + "title": "Address", + "renderer": { + "type": "link", + "labelField": "address" + } + }, + { + "field": "status.label", + "title": "Status", + "renderer": { + "type": "badge", + "colors": { + "Active": "#2fb344", + "Reserved": "#17a2b8", + "Deprecated": "#d63939", + "DHCP": "#2fb344", + "SLAAC": "#2fb344" + } + } + }, + { + "field": "vrfName", + "title": "VRF", + "renderer": { + "type": "text" + } + }, + { + "field": "dns_name", + "title": "DNS Name", + "renderer": { + "type": "text" + } + }, + { + "field": "tenant.name", + "title": "Tenant", + "renderer": { + "type": "text" + } + }, + { + "field": "description", + "title": "Description", + "renderer": { + "type": "text" + } + }, + { + "field": "last_updated", + "title": "Last Updated", + "renderer": { + "type": "date", + "relative": true + } + } + ] + } + } + } + } + } + ] + } +} diff --git a/plugins/NetBox/v1/defaultContent/device.dash.json b/plugins/NetBox/v1/defaultContent/device.dash.json new file mode 100644 index 00000000..7ba64544 --- /dev/null +++ b/plugins/NetBox/v1/defaultContent/device.dash.json @@ -0,0 +1,486 @@ +{ + "name": "Device", + "schemaVersion": "1.5", + "timeframe": "last30days", + "variables": [ + "{{variables.[NetBox Device]}}" + ], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "5d69700a-0cfb-401a-a8f1-a384fe86db5f", + "x": 0, + "y": 0, + "w": 1, + "h": 5, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Device Properties", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "datastream-properties", + "name": "properties", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[NetBox Devices]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[NetBox Device]}}" + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": true, + "columnOrder": [ + "name" + ], + "hiddenColumns": [ + "id", + "links", + "type", + "label", + "sourceId", + "sourceType", + "siteId", + "rackId", + "vrfId" + ] + } + } + }, + "variables": [ + "{{variables.[NetBox Device]}}" + ] + } + }, + { + "i": "586308e3-ef01-4c2b-8937-ce3081b02164", + "x": 1, + "y": 0, + "w": 3, + "h": 5, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Interfaces", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[deviceInterfaces]}}", + "name": "deviceInterfaces", + "pluginConfigId": "{{configId}}", + "sort": { + "by": [ + [ + "name", + "asc" + ] + ] + } + }, + "scope": { + "scope": "{{scopes.[NetBox Devices]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[NetBox Device]}}" + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "name", + "type.label", + "enabled", + "speed", + "mode.label", + "untagged_vlan.display", + "connectedTo", + "cabled", + "mac_address", + "count_ipaddresses", + "description", + "display_url" + ], + "hiddenColumns": [ + "device.name", + "label", + "mgmt_only", + "mtu", + "duplex.label", + "lag.name", + "parent.name", + "taggedVlanCount", + "vrf.name", + "connected_endpoints_reachable", + "last_updated" + ] + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "display_url", + "title": "Interface", + "renderer": { + "type": "link", + "labelField": "name" + } + }, + { + "field": "type.label", + "title": "Type", + "renderer": { + "type": "text" + } + }, + { + "field": "enabled", + "title": "Enabled", + "renderer": { + "type": "boolean", + "trueLabel": "Yes", + "falseLabel": "No" + }, + "align": "center" + }, + { + "field": "speed", + "title": "Speed", + "renderer": { + "type": "number", + "compact": true, + "suffix": " Kbps" + }, + "align": "right" + }, + { + "field": "mode.label", + "title": "Mode", + "renderer": { + "type": "text" + } + }, + { + "field": "untagged_vlan.display", + "title": "Untagged VLAN", + "renderer": { + "type": "text" + } + }, + { + "field": "connectedTo", + "title": "Connected To", + "renderer": { + "type": "text" + } + }, + { + "field": "cabled", + "title": "Cabled", + "renderer": { + "type": "boolean", + "trueLabel": "Yes", + "falseLabel": "No" + }, + "align": "center" + }, + { + "field": "mac_address", + "title": "MAC Address", + "renderer": { + "type": "text" + } + }, + { + "field": "count_ipaddresses", + "title": "IPs", + "renderer": { + "type": "number" + }, + "align": "right" + }, + { + "field": "description", + "title": "Description", + "renderer": { + "type": "text" + } + } + ] + } + } + }, + "variables": [ + "{{variables.[NetBox Device]}}" + ] + } + }, + { + "i": "522b3b38-1fcd-455a-ab00-78544c606f98", + "x": 0, + "y": 5, + "w": 4, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "IP Addresses", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[ipAddresses]}}", + "name": "ipAddresses", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "device": { + "variable": "{{variables.[NetBox Device]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[NetBox Devices]}}" + } + }, + "sort": { + "by": [ + [ + "address", + "asc" + ] + ] + } + }, + "variables": [ + "{{variables.[NetBox Device]}}" + ], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "address", + "status.label", + "vrfName", + "dns_name", + "assignedTo", + "tenant.name", + "description", + "display_url" + ], + "hiddenColumns": [ + "display", + "created", + "family.label", + "assigned", + "role.label" + ] + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "display_url", + "title": "Address", + "renderer": { + "type": "link", + "labelField": "address" + } + }, + { + "field": "status.label", + "title": "Status", + "renderer": { + "type": "badge", + "colors": { + "Active": "#2fb344", + "Reserved": "#17a2b8", + "Deprecated": "#d63939", + "DHCP": "#2fb344", + "SLAAC": "#2fb344" + } + } + }, + { + "field": "vrfName", + "title": "VRF", + "renderer": { + "type": "text" + } + }, + { + "field": "dns_name", + "title": "DNS Name", + "renderer": { + "type": "text" + } + }, + { + "field": "assignedTo", + "title": "Assigned To", + "renderer": { + "type": "text" + } + }, + { + "field": "tenant.name", + "title": "Tenant", + "renderer": { + "type": "text" + } + }, + { + "field": "description", + "title": "Description", + "renderer": { + "type": "text" + } + } + ] + } + } + } + } + }, + { + "i": "20a1a299-769e-464d-8731-20eec0a996b7", + "x": 0, + "y": 8, + "w": 4, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Change Log", + "description": "Changes recorded against this device", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[objectChanges]}}", + "name": "objectChanges", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "object": { + "variable": "{{variables.[NetBox Device]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[NetBox Devices]}}" + } + }, + "sort": { + "by": [ + [ + "time", + "desc" + ] + ] + } + }, + "variables": [ + "{{variables.[NetBox Device]}}" + ], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "time", + "user_name", + "action.label", + "object_repr", + "message" + ], + "hiddenColumns": [ + "changed_object_type" + ] + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "time", + "title": "Time", + "renderer": { + "type": "date", + "relative": true + } + }, + { + "field": "user_name", + "title": "User", + "renderer": { + "type": "text" + } + }, + { + "field": "action.label", + "title": "Action", + "renderer": { + "type": "badge", + "colors": { + "Created": "#2fb344", + "Updated": "#206bc4", + "Deleted": "#d63939" + } + } + }, + { + "field": "object_repr", + "title": "Object", + "renderer": { + "type": "text" + } + }, + { + "field": "message", + "title": "Message", + "renderer": { + "type": "text" + } + } + ] + } + } + } + } + } + ] + } +} diff --git a/plugins/NetBox/v1/defaultContent/deviceInventory.dash.json b/plugins/NetBox/v1/defaultContent/deviceInventory.dash.json new file mode 100644 index 00000000..d99d67fb --- /dev/null +++ b/plugins/NetBox/v1/defaultContent/deviceInventory.dash.json @@ -0,0 +1,457 @@ +{ + "name": "Device Inventory", + "schemaVersion": "1.5", + "timeframe": "none", + "variables": [], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "5ae1af6e-dc82-4cc0-a057-e778c99fe57a", + "x": 0, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Devices", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[devices]}}", + "name": "devices", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Devices" + } + } + } + } + }, + { + "i": "545a65fe-5988-4294-9d6e-39af3f39bfae", + "x": 1, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Device Models", + "description": "Distinct device types in use", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[devices]}}", + "name": "devices", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "distinctCount", + "names": [ + "device_type.model" + ] + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "device_type.model_distinctCount", + "comparisonColumn": "none", + "label": "Models" + } + } + } + } + }, + { + "i": "5b330995-da8d-4232-942d-ca0f24a0b657", + "x": 2, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Manufacturers", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[devices]}}", + "name": "devices", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "distinctCount", + "names": [ + "device_type.manufacturer.name" + ] + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "device_type.manufacturer.name_distinctCount", + "comparisonColumn": "none", + "label": "Manufacturers" + } + } + } + } + }, + { + "i": "66dac578-577a-441e-bb91-56978ed02a5f", + "x": 3, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Sites with Devices", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[devices]}}", + "name": "devices", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "distinctCount", + "names": [ + "site.name" + ] + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "site.name_distinctCount", + "comparisonColumn": "none", + "label": "Sites" + } + } + } + } + }, + { + "i": "23087dfa-b635-48ed-8f78-44e8cf87c627", + "x": 0, + "y": 2, + "w": 2, + "h": 5, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Devices per Model", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[devices]}}", + "name": "devices", + "pluginConfigId": "{{configId}}", + "group": { + "by": [ + [ + "device_type.model", + "uniqueValues" + ], + [ + "device_type.manufacturer.name", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + }, + "sort": { + "by": [ + [ + "count", + "desc" + ], + [ + "device_type.model_uniqueValues", + "asc" + ] + ] + } + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "device_type.model_uniqueValues", + "device_type.manufacturer.name_uniqueValues", + "count" + ], + "hiddenColumns": [], + "columnDisplayNames": { + "device_type.model_uniqueValues": "Model", + "device_type.manufacturer.name_uniqueValues": "Manufacturer", + "count": "Devices" + } + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "device_type.model_uniqueValues", + "title": "Model", + "renderer": { + "type": "text" + } + }, + { + "field": "device_type.manufacturer.name_uniqueValues", + "title": "Manufacturer", + "renderer": { + "type": "text" + } + }, + { + "field": "count", + "title": "Devices", + "renderer": { + "type": "bar" + } + } + ] + } + } + } + } + }, + { + "i": "7d8520f2-af66-4b68-900a-660398009166", + "x": 2, + "y": 2, + "w": 2, + "h": 5, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Devices per Site", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[devices]}}", + "name": "devices", + "pluginConfigId": "{{configId}}", + "group": { + "by": [ + [ + "site.name", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + }, + "sort": { + "by": [ + [ + "count", + "desc" + ], + [ + "site.name_uniqueValues", + "asc" + ] + ] + } + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "site.name_uniqueValues", + "count" + ], + "hiddenColumns": [], + "columnDisplayNames": { + "site.name_uniqueValues": "Site", + "count": "Devices" + } + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "site.name_uniqueValues", + "title": "Site", + "renderer": { + "type": "text" + } + }, + { + "field": "count", + "title": "Devices", + "renderer": { + "type": "bar" + } + } + ] + } + } + } + } + }, + { + "i": "f6d6f11a-1e66-49b7-9d25-bc0814fde71f", + "x": 0, + "y": 7, + "w": 4, + "h": 7, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Device Details", + "description": "All devices, including any device custom fields", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[devices]}}", + "name": "devices", + "pluginConfigId": "{{configId}}", + "sort": { + "by": [ + [ + "site.name", + "asc" + ], + [ + "display", + "asc" + ] + ] + } + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "display", + "device_type.model", + "device_type.manufacturer.name", + "site.name", + "role.name", + "rack.name", + "position", + "status.label", + "serial", + "primary_ip.display", + "tenant.name", + "display_url" + ], + "hiddenColumns": [ + "missingSerial", + "missingPrimaryIp", + "missingTenant", + "missingPlatform", + "daysSinceUpdate", + "created", + "description", + "oob_ip.address", + "asset_tag", + "platform.name", + "location.name", + "cluster.name", + "parent_device.name", + "last_updated" + ] + } + } + } + } + } + ] + } +} diff --git a/plugins/NetBox/v1/defaultContent/ipamCapacity.dash.json b/plugins/NetBox/v1/defaultContent/ipamCapacity.dash.json new file mode 100644 index 00000000..e465096c --- /dev/null +++ b/plugins/NetBox/v1/defaultContent/ipamCapacity.dash.json @@ -0,0 +1,680 @@ +{ + "name": "IPAM Capacity", + "schemaVersion": "1.5", + "timeframe": "none", + "variables": [], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "7174e594-da58-424a-b245-8efe6288f9f1", + "x": 0, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Prefixes", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[prefixes]}}", + "name": "prefixes", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Prefixes" + } + } + } + } + }, + { + "i": "9feebca5-2aff-47d7-bf06-2814dcf59d50", + "x": 1, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Total VLAN Groups", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[vlanGroups]}}", + "name": "vlanGroups", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "VLAN Groups" + } + } + } + } + }, + { + "i": "514fafdb-2127-4b6f-9212-6b1e544fe4ee", + "x": 2, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "IP Addresses", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[ipAddresses]}}", + "name": "ipAddresses", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "IP Addresses" + } + } + } + } + }, + { + "i": "82f0d193-92a6-4a41-8254-d6d40dbf261a", + "x": 3, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Unassigned IP Addresses", + "description": "IP addresses not assigned to an interface", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[ipAddresses]}}", + "name": "ipAddresses", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "assignment": "false" + }, + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Unassigned" + } + } + } + } + }, + { + "i": "93952ffc-c959-44a8-a508-b51a50f438e8", + "x": 0, + "y": 2, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Prefixes by Status", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[prefixes]}}", + "name": "prefixes", + "pluginConfigId": "{{configId}}", + "group": { + "by": [ + [ + "status.label", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + }, + "sort": { + "by": [ + [ + "count", + "desc" + ] + ] + } + }, + "visualisation": { + "type": "data-stream-donut-chart", + "config": { + "data-stream-donut-chart": { + "valueColumn": "count", + "labelColumn": "status.label_uniqueValues", + "hideCenterValue": false, + "showValuesAsPercentage": false, + "legendPosition": "auto", + "legendMode": "table" + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "pie", + "encoding": { + "label": { + "field": "status.label_uniqueValues", + "type": "nominal", + "title": "Status" + }, + "value": { + "field": "count", + "type": "quantitative", + "title": "Count" + } + }, + "style": { + "pieInnerRadius": 60, + "pieDonutCenterValue": "total", + "labelFontSize": 16, + "pieLegendStyle": "table" + } + } + } + } + }, + { + "i": "2a160721-415c-4a26-b903-171a04808541", + "x": 2, + "y": 2, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "IP Addresses by Status", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[ipAddresses]}}", + "name": "ipAddresses", + "pluginConfigId": "{{configId}}", + "group": { + "by": [ + [ + "status.label", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-donut-chart", + "config": { + "data-stream-donut-chart": { + "valueColumn": "count", + "labelColumn": "status.label_uniqueValues", + "hideCenterValue": false, + "showValuesAsPercentage": false, + "legendPosition": "auto", + "legendMode": "table" + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "pie", + "encoding": { + "label": { + "field": "status.label_uniqueValues", + "type": "nominal", + "title": "Status" + }, + "value": { + "field": "count", + "type": "quantitative", + "title": "Count" + } + }, + "style": { + "pieInnerRadius": 60, + "pieDonutCenterValue": "total", + "labelFontSize": 16, + "pieLegendStyle": "table", + "mappedColors": { + "Active": "#2fb344", + "Reserved": "#17a2b8", + "Deprecated": "#d63939", + "DHCP": "#2fb344", + "SLAAC": "#2fb344" + } + } + } + } + } + }, + { + "i": "673097ce-d716-4f78-b719-5e066d186648", + "x": 0, + "y": 6, + "w": 4, + "h": 6, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Prefix Utilization", + "description": "Rows over 80% are highlighted amber, over 90% red", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[prefixUtilization]}}", + "name": "prefixUtilization", + "pluginConfigId": "{{configId}}", + "sort": { + "by": [ + [ + "utilization", + "desc" + ] + ] + } + }, + "scope": { + "scope": "{{scopes.[NetBox Prefixes]}}", + "workspace": "{{workspaceId}}" + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "prefix", + "vrf", + "status", + "size", + "used", + "utilization", + "netboxUrl" + ], + "hiddenColumns": [] + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "netboxUrl", + "title": "Prefix", + "renderer": { + "type": "link", + "labelField": "prefix" + } + }, + { + "field": "vrf", + "title": "VRF", + "renderer": { + "type": "text" + } + }, + { + "field": "status", + "title": "Status", + "renderer": { + "type": "badge", + "colors": { + "Active": "#2fb344", + "Container": "#9ba3af", + "Reserved": "#17a2b8", + "Deprecated": "#d63939" + } + } + }, + { + "field": "size", + "title": "Size", + "renderer": { + "type": "number" + }, + "align": "right" + }, + { + "field": "used", + "title": "Used", + "renderer": { + "type": "number" + }, + "align": "right" + }, + { + "field": "utilization", + "title": "Utilization", + "renderer": { + "type": "progress", + "max": 100 + } + } + ] + }, + "style": { + "richTableRowHighlight": { + "field": "utilization", + "thresholds": [ + { + "to": 79.99, + "color": "rgba(0,0,0,0)" + }, + { + "to": 89.99, + "color": "rgba(245,158,11,0.12)" + }, + { + "to": 100, + "color": "rgba(239,68,68,0.15)" + } + ] + } + } + } + } + } + }, + { + "i": "3fdffd7d-fbdc-4163-bc53-308b73385b5b", + "x": 0, + "y": 12, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "VLAN Group Utilization", + "description": "Top 10 VLAN groups by VLAN IDs used", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[vlanGroups]}}", + "name": "vlanGroups", + "pluginConfigId": "{{configId}}", + "sort": { + "by": [ + [ + "utilizationPct", + "desc" + ] + ], + "top": 10 + } + }, + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisData": "display", + "yAxisData": [ + "utilizationPct" + ], + "xAxisGroup": "none", + "xAxisLabel": "", + "yAxisLabel": "Utilization (%)", + "showXAxisLabel": false, + "showYAxisLabel": true, + "showLegend": false, + "legendPosition": "bottom", + "showGrid": true, + "horizontalLayout": "horizontal", + "displayMode": "actual", + "showTotals": false, + "showValue": false, + "grouping": false, + "range": { + "type": "auto" + } + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "ringGrid", + "encoding": { + "value": { + "field": "utilizationPct", + "type": "quantitative", + "title": "Utilization" + }, + "label": { + "field": "display", + "type": "nominal", + "title": "VLAN Group" + } + }, + "style": { + "ringGridColumnsPerRow": 5, + "ringGridThresholds": [ + { + "to": 0.8, + "color": "#2fb344" + }, + { + "to": 0.9, + "color": "#f59f00" + }, + { + "to": 1, + "color": "#d63939" + } + ] + } + } + } + } + }, + { + "i": "e89e45dc-9e12-4911-b8ed-20f3dbc9dc5d", + "x": 2, + "y": 12, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "VLAN Groups", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[vlanGroups]}}", + "name": "vlanGroups", + "pluginConfigId": "{{configId}}", + "sort": { + "by": [ + [ + "utilizationPct", + "desc" + ] + ] + } + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "display", + "scopeType", + "scope.name", + "vidRanges", + "total_vlan_ids", + "vlan_count", + "utilizationPct", + "tenant.name", + "display_url" + ], + "hiddenColumns": [ + "last_updated" + ] + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "display_url", + "title": "VLAN Group", + "renderer": { + "type": "link", + "labelField": "display" + } + }, + { + "field": "scope.name", + "title": "Scope", + "renderer": { + "type": "text" + } + }, + { + "field": "vlan_count", + "title": "VLANs", + "renderer": { + "type": "number" + }, + "align": "right" + }, + { + "field": "total_vlan_ids", + "title": "VLAN IDs", + "renderer": { + "type": "number" + }, + "align": "right" + }, + { + "field": "utilizationPct", + "title": "Utilization", + "renderer": { + "type": "progress", + "max": 100 + } + } + ] + } + } + } + } + } + ] + } +} diff --git a/plugins/NetBox/v1/defaultContent/lifecycleAndCircuits.dash.json b/plugins/NetBox/v1/defaultContent/lifecycleAndCircuits.dash.json new file mode 100644 index 00000000..c558dfa4 --- /dev/null +++ b/plugins/NetBox/v1/defaultContent/lifecycleAndCircuits.dash.json @@ -0,0 +1,972 @@ +{ + "name": "Lifecycle and Circuits", + "schemaVersion": "1.5", + "timeframe": "none", + "variables": [], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "ecc23965-115b-46bf-a5c2-6a0ea2f4401a", + "x": 0, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Inactive Devices", + "description": "Planned, staged, offline, failed, inventory or decommissioning", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[devices]}}", + "name": "devices", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "status.value", + "operation": "notequals", + "value": "active" + } + ] + }, + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Devices" + } + } + } + } + }, + { + "i": "0832f840-dadd-4e2f-903e-8079a731425a", + "x": 1, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Inactive Virtual Machines", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[virtualMachines]}}", + "name": "virtualMachines", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "status.value", + "operation": "notequals", + "value": "active" + } + ] + }, + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Virtual Machines" + } + } + } + } + }, + { + "i": "50ab53f2-c668-4c41-98ac-d1dea71fcc26", + "x": 2, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Circuits", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[circuits]}}", + "name": "circuits", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Circuits" + } + } + } + } + }, + { + "i": "773fabb5-5e49-4517-aa0c-9b59a87b958a", + "x": 3, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Circuits Terminating Within 90 Days", + "description": "Includes circuits already past their termination date", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[circuits]}}", + "name": "circuits", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "termination_date", + "operation": "notempty" + }, + { + "column": "daysUntilTermination", + "operation": "lessthan", + "value": "91" + } + ] + }, + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Circuits" + } + } + } + } + }, + { + "i": "07a04792-9e47-4ad6-971a-1c837ed1655c", + "x": 0, + "y": 2, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Devices by Status", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[devices]}}", + "name": "devices", + "pluginConfigId": "{{configId}}", + "group": { + "by": [ + [ + "status.label", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + }, + "sort": { + "by": [ + [ + "count", + "desc" + ] + ] + } + }, + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisData": "status.label_uniqueValues", + "yAxisData": [ + "count" + ], + "xAxisGroup": "none", + "xAxisLabel": "", + "yAxisLabel": "", + "showXAxisLabel": false, + "showYAxisLabel": false, + "showLegend": false, + "legendPosition": "bottom", + "showGrid": true, + "horizontalLayout": "vertical", + "displayMode": "actual", + "showTotals": false, + "showValue": false, + "grouping": false, + "range": { + "type": "auto" + } + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "bar", + "encoding": { + "x": { + "field": "status.label_uniqueValues", + "type": "nominal", + "title": "Status" + }, + "y": { + "field": "count", + "type": "quantitative", + "title": "Devices" + } + }, + "style": { + "cartesianColorRules": [ + { + "label": "Active", + "color": "#2fb344" + }, + { + "label": "Planned", + "color": "#17a2b8" + }, + { + "label": "Staged", + "color": "#206bc4" + }, + { + "label": "Failed", + "color": "#d63939" + }, + { + "label": "Offline", + "color": "#9ba3af" + }, + { + "label": "Inventory", + "color": "#ae3ec9" + }, + { + "label": "Decommissioning", + "color": "#f59f00" + } + ] + } + } + } + } + }, + { + "i": "5296e27c-3e1d-42bc-a1a8-22102351efab", + "x": 2, + "y": 2, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Virtual Machines by Status", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[virtualMachines]}}", + "name": "virtualMachines", + "pluginConfigId": "{{configId}}", + "group": { + "by": [ + [ + "status.label", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + }, + "sort": { + "by": [ + [ + "count", + "desc" + ] + ] + } + }, + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisData": "status.label_uniqueValues", + "yAxisData": [ + "count" + ], + "xAxisGroup": "none", + "xAxisLabel": "", + "yAxisLabel": "", + "showXAxisLabel": false, + "showYAxisLabel": false, + "showLegend": false, + "legendPosition": "bottom", + "showGrid": true, + "horizontalLayout": "vertical", + "displayMode": "actual", + "showTotals": false, + "showValue": false, + "grouping": false, + "range": { + "type": "auto" + } + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "bar", + "encoding": { + "x": { + "field": "status.label_uniqueValues", + "type": "nominal", + "title": "Status" + }, + "y": { + "field": "count", + "type": "quantitative", + "title": "Virtual Machines" + } + }, + "style": { + "cartesianColorRules": [ + { + "label": "Active", + "color": "#2fb344" + }, + { + "label": "Planned", + "color": "#17a2b8" + }, + { + "label": "Staged", + "color": "#206bc4" + }, + { + "label": "Failed", + "color": "#d63939" + }, + { + "label": "Offline", + "color": "#9ba3af" + }, + { + "label": "Paused", + "color": "#f76707" + }, + { + "label": "Decommissioning", + "color": "#f59f00" + } + ] + } + } + } + } + }, + { + "i": "9a3d6f17-01d5-41ae-9714-c27e86892d57", + "x": 0, + "y": 5, + "w": 4, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Devices Not Active", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[devices]}}", + "name": "devices", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "status.value", + "operation": "notequals", + "value": "active" + } + ] + }, + "sort": { + "by": [ + [ + "status.label", + "asc" + ], + [ + "display", + "asc" + ] + ] + } + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "display", + "status.label", + "role.name", + "device_type.manufacturer.name", + "device_type.model", + "platform.name", + "site.name", + "rack.name", + "position", + "primary_ip.display", + "tenant.name", + "serial", + "display_url" + ], + "hiddenColumns": [ + "missingSerial", + "missingPrimaryIp", + "missingTenant", + "missingPlatform", + "daysSinceUpdate", + "created", + "description", + "oob_ip.address", + "asset_tag" + ] + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "display_url", + "title": "Device", + "renderer": { + "type": "link", + "labelField": "display" + } + }, + { + "field": "status.label", + "title": "Status", + "renderer": { + "type": "badge", + "colors": { + "Active": "#2fb344", + "Planned": "#17a2b8", + "Staged": "#206bc4", + "Failed": "#d63939", + "Offline": "#9ba3af", + "Inventory": "#ae3ec9", + "Decommissioning": "#f59f00" + } + } + }, + { + "field": "role.name", + "title": "Role", + "renderer": { + "type": "text" + } + }, + { + "field": "device_type.manufacturer.name", + "title": "Manufacturer", + "renderer": { + "type": "text" + } + }, + { + "field": "device_type.model", + "title": "Model", + "renderer": { + "type": "text" + } + }, + { + "field": "site.name", + "title": "Site", + "renderer": { + "type": "text" + } + }, + { + "field": "rack.name", + "title": "Rack", + "renderer": { + "type": "text" + } + }, + { + "field": "tenant.name", + "title": "Tenant", + "renderer": { + "type": "text" + } + }, + { + "field": "last_updated", + "title": "Last Updated", + "renderer": { + "type": "date", + "relative": true + } + } + ] + } + } + } + } + }, + { + "i": "0b9c0aca-8326-4ac4-90a7-0f2a16cdc7d8", + "x": 0, + "y": 9, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Circuits by Provider", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[circuits]}}", + "name": "circuits", + "pluginConfigId": "{{configId}}", + "group": { + "by": [ + [ + "provider.name", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + }, + "sort": { + "by": [ + [ + "count", + "desc" + ] + ], + "top": 10 + } + }, + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisData": "provider.name_uniqueValues", + "yAxisData": [ + "count" + ], + "xAxisGroup": "none", + "xAxisLabel": "", + "yAxisLabel": "", + "showXAxisLabel": false, + "showYAxisLabel": false, + "showLegend": false, + "legendPosition": "bottom", + "showGrid": true, + "horizontalLayout": "horizontal", + "displayMode": "actual", + "showTotals": false, + "showValue": false, + "grouping": false, + "range": { + "type": "auto" + } + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "bar", + "encoding": { + "x": { + "field": "provider.name_uniqueValues", + "type": "nominal", + "title": "Provider" + }, + "y": { + "field": "count", + "type": "quantitative", + "title": "Circuits" + } + }, + "style": { + "cartesianHorizontal": true + } + } + } + } + }, + { + "i": "01161a70-8ee3-4af4-92a9-eb3ba327fa7a", + "x": 2, + "y": 9, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Circuits by Status", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[circuits]}}", + "name": "circuits", + "pluginConfigId": "{{configId}}", + "group": { + "by": [ + [ + "status.label", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-donut-chart", + "config": { + "data-stream-donut-chart": { + "valueColumn": "count", + "labelColumn": "status.label_uniqueValues", + "hideCenterValue": false, + "showValuesAsPercentage": false, + "legendPosition": "auto", + "legendMode": "table" + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "pie", + "encoding": { + "label": { + "field": "status.label_uniqueValues", + "type": "nominal", + "title": "Status" + }, + "value": { + "field": "count", + "type": "quantitative", + "title": "Count" + } + }, + "style": { + "pieInnerRadius": 60, + "pieDonutCenterValue": "total", + "labelFontSize": 16, + "pieLegendStyle": "table", + "mappedColors": { + "Active": "#2fb344", + "Planned": "#17a2b8", + "Provisioning": "#206bc4", + "Offline": "#d63939", + "Deprovisioning": "#f59f00", + "Decommissioned": "#9ba3af" + } + } + } + } + } + }, + { + "i": "60ac6be0-e3a7-4eab-8ebe-25f7f08dec97", + "x": 0, + "y": 12, + "w": 4, + "h": 5, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Circuit Terminations", + "description": "Circuits ordered by days until termination", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[circuits]}}", + "name": "circuits", + "pluginConfigId": "{{configId}}", + "sort": { + "by": [ + [ + "daysUntilTermination", + "asc" + ] + ] + } + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "cid", + "provider.name", + "type.name", + "status.label", + "termination_date", + "daysUntilTermination", + "commit_rate", + "install_date", + "termination_a.termination.display", + "termination_z.termination.display", + "tenant.name", + "display_url" + ], + "hiddenColumns": [ + "created", + "last_updated", + "description" + ] + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "display_url", + "title": "Circuit ID", + "renderer": { + "type": "link", + "labelField": "cid" + } + }, + { + "field": "provider.name", + "title": "Provider", + "renderer": { + "type": "text" + } + }, + { + "field": "type.name", + "title": "Type", + "renderer": { + "type": "text" + } + }, + { + "field": "status.label", + "title": "Status", + "renderer": { + "type": "badge", + "colors": { + "Active": "#2fb344", + "Planned": "#17a2b8", + "Provisioning": "#206bc4", + "Offline": "#d63939", + "Deprovisioning": "#f59f00", + "Decommissioned": "#9ba3af" + } + } + }, + { + "field": "termination_date", + "title": "Termination Date", + "renderer": { + "type": "date" + } + }, + { + "field": "daysUntilTermination", + "title": "Days Until Termination", + "renderer": { + "type": "number", + "thresholds": [ + { + "to": 0, + "color": "#d63939" + }, + { + "to": 90, + "color": "#f59f00" + }, + { + "to": 1000000, + "color": "#2fb344" + } + ] + }, + "align": "right" + }, + { + "field": "commit_rate", + "title": "Commit Rate", + "renderer": { + "type": "number", + "compact": true, + "suffix": " Kbps" + }, + "align": "right" + }, + { + "field": "install_date", + "title": "Install Date", + "renderer": { + "type": "date" + } + }, + { + "field": "termination_a.termination.display", + "title": "Side A", + "renderer": { + "type": "text" + } + }, + { + "field": "termination_z.termination.display", + "title": "Side Z", + "renderer": { + "type": "text" + } + }, + { + "field": "tenant.name", + "title": "Tenant", + "renderer": { + "type": "text" + } + } + ] + } + } + } + } + } + ] + } +} diff --git a/plugins/NetBox/v1/defaultContent/manifest.json b/plugins/NetBox/v1/defaultContent/manifest.json new file mode 100644 index 00000000..13343d93 --- /dev/null +++ b/plugins/NetBox/v1/defaultContent/manifest.json @@ -0,0 +1,64 @@ +{ + "items": [ + { + "name": "overview", + "type": "dashboard" + }, + { + "name": "ipamCapacity", + "type": "dashboard" + }, + { + "name": "rackCapacity", + "type": "dashboard" + }, + { + "name": "dataQuality", + "type": "dashboard" + }, + { + "name": "lifecycleAndCircuits", + "type": "dashboard" + }, + { + "name": "changeActivity", + "type": "dashboard" + }, + { + "name": "dataCenters", + "type": "dashboard" + }, + { + "name": "power", + "type": "dashboard" + }, + { + "name": "deviceInventory", + "type": "dashboard" + }, + { + "name": "site", + "type": "dashboard" + }, + { + "name": "rack", + "type": "dashboard" + }, + { + "name": "device", + "type": "dashboard" + }, + { + "name": "virtualMachine", + "type": "dashboard" + }, + { + "name": "prefix", + "type": "dashboard" + }, + { + "name": "circuit", + "type": "dashboard" + } + ] +} diff --git a/plugins/NetBox/v1/defaultContent/overview.dash.json b/plugins/NetBox/v1/defaultContent/overview.dash.json new file mode 100644 index 00000000..36a5c8a8 --- /dev/null +++ b/plugins/NetBox/v1/defaultContent/overview.dash.json @@ -0,0 +1,897 @@ +{ + "name": "Overview", + "schemaVersion": "1.5", + "timeframe": "none", + "variables": [], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "bf31d423-f2fb-4740-a715-fcb629c90f2e", + "x": 0, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Sites", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[sites]}}", + "name": "sites", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Sites" + } + } + } + } + }, + { + "i": "a87f696a-ed47-4998-b3bf-7f8bfa0a08d0", + "x": 1, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Racks", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[racks]}}", + "name": "racks", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Racks" + } + } + } + } + }, + { + "i": "e7506eb4-a411-4ffa-97ed-b748c354acc2", + "x": 2, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Devices", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[devices]}}", + "name": "devices", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Devices" + } + } + } + } + }, + { + "i": "33e2dc4d-b335-4e42-9c0e-760a5d0e3205", + "x": 3, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Virtual Machines", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[virtualMachines]}}", + "name": "virtualMachines", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Virtual Machines" + } + } + } + } + }, + { + "i": "5fd8b80c-c6fd-4b29-850d-948feff122c4", + "x": 0, + "y": 2, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Prefixes", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[prefixes]}}", + "name": "prefixes", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Prefixes" + } + } + } + } + }, + { + "i": "e9f93dc9-4fcd-49b9-9177-8c86d0967fa2", + "x": 1, + "y": 2, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "IP Addresses", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[ipAddresses]}}", + "name": "ipAddresses", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "IP Addresses" + } + } + } + } + }, + { + "i": "13d8a594-20e9-458c-8899-0ea9c23ba51e", + "x": 2, + "y": 2, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Circuits", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[circuits]}}", + "name": "circuits", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Circuits" + } + } + } + } + }, + { + "i": "627b9b5d-7448-47a1-acad-6443a46cdf16", + "x": 3, + "y": 2, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "NetBox Version", + "description": "Version of the connected NetBox instance", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[status]}}", + "name": "status", + "pluginConfigId": "{{configId}}" + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "netbox-version", + "comparisonColumn": "none", + "label": "Version" + } + } + } + } + }, + { + "i": "2940b5ba-6b25-4153-a688-ee1c16e4973b", + "x": 0, + "y": 4, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Devices by Status", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[devices]}}", + "name": "devices", + "pluginConfigId": "{{configId}}", + "group": { + "by": [ + [ + "status.label", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-donut-chart", + "config": { + "data-stream-donut-chart": { + "valueColumn": "count", + "labelColumn": "status.label_uniqueValues", + "hideCenterValue": false, + "showValuesAsPercentage": false, + "legendPosition": "auto", + "legendMode": "table" + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "pie", + "encoding": { + "label": { + "field": "status.label_uniqueValues", + "type": "nominal", + "title": "Status" + }, + "value": { + "field": "count", + "type": "quantitative", + "title": "Count" + } + }, + "style": { + "pieInnerRadius": 60, + "pieDonutCenterValue": "total", + "labelFontSize": 16, + "pieLegendStyle": "table", + "mappedColors": { + "Active": "#2fb344", + "Planned": "#17a2b8", + "Staged": "#206bc4", + "Failed": "#d63939", + "Offline": "#9ba3af", + "Inventory": "#ae3ec9", + "Decommissioning": "#f59f00" + } + } + } + } + } + }, + { + "i": "2fb65d1f-6d0e-47c7-83fb-15904fded3cf", + "x": 2, + "y": 4, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Devices by Role", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[devices]}}", + "name": "devices", + "pluginConfigId": "{{configId}}", + "group": { + "by": [ + [ + "role.name", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + }, + "sort": { + "by": [ + [ + "count", + "desc" + ] + ], + "top": 10 + } + }, + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisData": "role.name_uniqueValues", + "yAxisData": [ + "count" + ], + "xAxisGroup": "none", + "xAxisLabel": "", + "yAxisLabel": "", + "showXAxisLabel": false, + "showYAxisLabel": false, + "showLegend": false, + "legendPosition": "bottom", + "showGrid": true, + "horizontalLayout": "horizontal", + "displayMode": "actual", + "showTotals": false, + "showValue": false, + "grouping": false, + "range": { + "type": "auto" + } + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "bar", + "encoding": { + "x": { + "field": "role.name_uniqueValues", + "type": "nominal", + "title": "Role" + }, + "y": { + "field": "count", + "type": "quantitative", + "title": "Devices" + } + }, + "style": { + "cartesianHorizontal": true + } + } + } + } + }, + { + "i": "128aeab0-ec18-4447-a5e1-7c28ee6f3fcf", + "x": 0, + "y": 7, + "w": 4, + "h": 5, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Devices by Manufacturer and Model", + "description": "Device count per model, grouped by manufacturer", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[devices]}}", + "name": "devices", + "pluginConfigId": "{{configId}}", + "group": { + "by": [ + [ + "device_type.manufacturer.name", + "uniqueValues" + ], + [ + "device_type.model", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + }, + "sort": { + "by": [ + [ + "count", + "desc" + ] + ] + } + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "device_type.manufacturer.name_uniqueValues", + "device_type.model_uniqueValues", + "count" + ], + "hiddenColumns": [] + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "treemap", + "encoding": { + "label": { + "field": "device_type.model_uniqueValues", + "type": "nominal", + "title": "Model" + }, + "value": { + "field": "count", + "type": "quantitative", + "title": "Devices" + }, + "parent": { + "field": "device_type.manufacturer.name_uniqueValues", + "type": "nominal", + "title": "Manufacturer" + } + }, + "style": { + "showLabels": true, + "treemapGap": 2 + } + } + } + } + }, + { + "i": "31357f86-697b-42fa-a962-bacd3cd8b80f", + "x": 0, + "y": 12, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Devices by Platform", + "description": "Devices with a platform assigned", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[devices]}}", + "name": "devices", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "platform.name", + "operation": "notempty" + } + ] + }, + "group": { + "by": [ + [ + "platform.name", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + }, + "sort": { + "by": [ + [ + "count", + "desc" + ] + ], + "top": 10 + } + }, + "visualisation": { + "type": "data-stream-donut-chart", + "config": { + "data-stream-donut-chart": { + "valueColumn": "count", + "labelColumn": "platform.name_uniqueValues", + "hideCenterValue": false, + "showValuesAsPercentage": false, + "legendPosition": "auto", + "legendMode": "table" + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "pie", + "encoding": { + "label": { + "field": "platform.name_uniqueValues", + "type": "nominal", + "title": "Platform" + }, + "value": { + "field": "count", + "type": "quantitative", + "title": "Count" + } + }, + "style": { + "pieInnerRadius": 60, + "pieDonutCenterValue": "total", + "labelFontSize": 16, + "pieLegendStyle": "table" + } + } + } + } + }, + { + "i": "f2ed10ba-4845-4fc0-bb28-78184d0fb915", + "x": 2, + "y": 12, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Top 10 Sites by Devices", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[devices]}}", + "name": "devices", + "pluginConfigId": "{{configId}}", + "group": { + "by": [ + [ + "site.name", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + }, + "sort": { + "by": [ + [ + "count", + "desc" + ] + ], + "top": 10 + } + }, + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisData": "site.name_uniqueValues", + "yAxisData": [ + "count" + ], + "xAxisGroup": "none", + "xAxisLabel": "", + "yAxisLabel": "", + "showXAxisLabel": false, + "showYAxisLabel": false, + "showLegend": false, + "legendPosition": "bottom", + "showGrid": true, + "horizontalLayout": "horizontal", + "displayMode": "actual", + "showTotals": false, + "showValue": false, + "grouping": false, + "range": { + "type": "auto" + } + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "bar", + "encoding": { + "x": { + "field": "site.name_uniqueValues", + "type": "nominal", + "title": "Site" + }, + "y": { + "field": "count", + "type": "quantitative", + "title": "Devices" + } + }, + "style": { + "cartesianHorizontal": true + } + } + } + } + }, + { + "i": "82078e48-0542-4ad8-acdc-9f56151824e0", + "x": 0, + "y": 15, + "w": 4, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Virtual Machines by Cluster", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[virtualMachines]}}", + "name": "virtualMachines", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "cluster.name", + "operation": "notempty" + } + ] + }, + "group": { + "by": [ + [ + "cluster.name", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + }, + "sort": { + "by": [ + [ + "count", + "desc" + ] + ], + "top": 10 + } + }, + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisData": "cluster.name_uniqueValues", + "yAxisData": [ + "count" + ], + "xAxisGroup": "none", + "xAxisLabel": "", + "yAxisLabel": "", + "showXAxisLabel": false, + "showYAxisLabel": false, + "showLegend": false, + "legendPosition": "bottom", + "showGrid": true, + "horizontalLayout": "horizontal", + "displayMode": "actual", + "showTotals": false, + "showValue": false, + "grouping": false, + "range": { + "type": "auto" + } + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "bar", + "encoding": { + "x": { + "field": "cluster.name_uniqueValues", + "type": "nominal", + "title": "Cluster" + }, + "y": { + "field": "count", + "type": "quantitative", + "title": "Virtual Machines" + } + }, + "style": { + "cartesianHorizontal": true + } + } + } + } + } + ] + } +} diff --git a/plugins/NetBox/v1/defaultContent/power.dash.json b/plugins/NetBox/v1/defaultContent/power.dash.json new file mode 100644 index 00000000..e2dc3d35 --- /dev/null +++ b/plugins/NetBox/v1/defaultContent/power.dash.json @@ -0,0 +1,606 @@ +{ + "name": "Power", + "schemaVersion": "1.5", + "timeframe": "none", + "variables": [], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "cbf41122-ff57-4c66-9944-5788eb0abd4f", + "x": 0, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Total Power Feeds", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[powerFeeds]}}", + "name": "powerFeeds", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Power Feeds" + } + } + } + } + }, + { + "i": "4188eeeb-4b47-4de2-99a5-be96ceb65615", + "x": 1, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Available Power", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[powerFeeds]}}", + "name": "powerFeeds", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "sum", + "names": [ + "availableKva" + ] + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "availableKva_sum", + "comparisonColumn": "none", + "label": "kVA" + } + } + } + } + }, + { + "i": "41e2a680-1cf8-4e07-9651-8516af5c09d3", + "x": 2, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Power Panels", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[powerFeeds]}}", + "name": "powerFeeds", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "distinctCount", + "names": [ + "power_panel.name" + ] + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "power_panel.name_distinctCount", + "comparisonColumn": "none", + "label": "Power Panels" + } + } + } + } + }, + { + "i": "b94a970e-eae1-441b-a0c6-0bae9c905e6c", + "x": 3, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Sites with Power Feeds", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[powerFeeds]}}", + "name": "powerFeeds", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "distinctCount", + "names": [ + "power_panel.site.name" + ] + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "power_panel.site.name_distinctCount", + "comparisonColumn": "none", + "label": "Sites" + } + } + } + } + }, + { + "i": "1b28f95c-02f6-4ab9-aa29-3f01b1438824", + "x": 0, + "y": 2, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Available Power by Site", + "description": "Top 15 sites by available power (kVA)", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[powerFeeds]}}", + "name": "powerFeeds", + "pluginConfigId": "{{configId}}", + "group": { + "by": [ + [ + "power_panel.site.name", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "sum", + "names": [ + "availableKva" + ] + } + ] + }, + "sort": { + "by": [ + [ + "availableKva_sum", + "desc" + ] + ], + "top": 15 + } + }, + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisData": "power_panel.site.name_uniqueValues", + "yAxisData": [ + "availableKva_sum" + ], + "xAxisGroup": "none", + "xAxisLabel": "", + "yAxisLabel": "kVA", + "showXAxisLabel": false, + "showYAxisLabel": true, + "showLegend": false, + "legendPosition": "bottom", + "showGrid": true, + "horizontalLayout": "horizontal", + "displayMode": "actual", + "showTotals": false, + "showValue": false, + "grouping": false, + "range": { + "type": "auto" + } + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "bar", + "encoding": { + "x": { + "field": "power_panel.site.name_uniqueValues", + "type": "nominal", + "title": "Site" + }, + "y": { + "field": "availableKva_sum", + "type": "quantitative", + "title": "Available Power (kVA)" + } + }, + "style": { + "cartesianHorizontal": true + } + } + } + } + }, + { + "i": "52b9fc68-40c6-444e-8c4c-ac5dca54e3d2", + "x": 2, + "y": 2, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Power Feeds by Status", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[powerFeeds]}}", + "name": "powerFeeds", + "pluginConfigId": "{{configId}}", + "group": { + "by": [ + [ + "statusLabel", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "visualisation": { + "type": "data-stream-donut-chart", + "config": { + "data-stream-donut-chart": { + "valueColumn": "count", + "labelColumn": "statusLabel_uniqueValues", + "hideCenterValue": false, + "showValuesAsPercentage": false, + "legendPosition": "auto", + "legendMode": "table" + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "pie", + "encoding": { + "label": { + "field": "statusLabel_uniqueValues", + "type": "nominal", + "title": "Status" + }, + "value": { + "field": "count", + "type": "quantitative", + "title": "Count" + } + }, + "style": { + "pieInnerRadius": 60, + "pieDonutCenterValue": "total", + "labelFontSize": 16, + "pieLegendStyle": "table", + "mappedColors": { + "Active": "#2fb344", + "Planned": "#17a2b8", + "Offline": "#9ba3af", + "Failed": "#d63939" + } + } + } + } + } + }, + { + "i": "0aace71b-f7db-4a1f-ab07-da3424841520", + "x": 0, + "y": 6, + "w": 4, + "h": 7, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Power Feeds", + "description": "Power feeds by site and rack, with available power and maximum utilization", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[powerFeeds]}}", + "name": "powerFeeds", + "pluginConfigId": "{{configId}}", + "sort": { + "by": [ + [ + "power_panel.site.name", + "asc" + ], + [ + "rack.name", + "asc" + ], + [ + "name", + "asc" + ] + ] + } + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "power_panel.site.name", + "rack.name", + "name", + "power_panel.name", + "phaseLabel", + "voltage", + "amperage", + "availableKva", + "max_utilization", + "statusLabel", + "connected" + ], + "hiddenColumns": [ + "typeLabel", + "supplyLabel", + "tenant.name", + "description", + "created", + "last_updated" + ] + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "power_panel.site.name", + "title": "Site", + "renderer": { + "type": "text" + } + }, + { + "field": "rack.name", + "title": "Rack", + "renderer": { + "type": "text" + } + }, + { + "field": "name", + "title": "Power Feed", + "renderer": { + "type": "text" + } + }, + { + "field": "power_panel.name", + "title": "Power Panel", + "renderer": { + "type": "text" + } + }, + { + "field": "phaseLabel", + "title": "Phase", + "renderer": { + "type": "text" + } + }, + { + "field": "voltage", + "title": "Voltage (V)", + "renderer": { + "type": "number" + }, + "align": "right" + }, + { + "field": "amperage", + "title": "Amperage (A)", + "renderer": { + "type": "number" + }, + "align": "right" + }, + { + "field": "availableKva", + "title": "Available Power (kVA)", + "renderer": { + "type": "number", + "decimals": 2 + }, + "align": "right" + }, + { + "field": "max_utilization", + "title": "Max Utilization", + "renderer": { + "type": "progress", + "max": 100 + } + }, + { + "field": "statusLabel", + "title": "Status", + "renderer": { + "type": "badge", + "colors": { + "Active": "#2fb344", + "Planned": "#17a2b8", + "Offline": "#9ba3af", + "Failed": "#d63939" + } + } + }, + { + "field": "connected", + "title": "Connected", + "renderer": { + "type": "boolean", + "trueLabel": "Yes", + "falseLabel": "No" + }, + "align": "center" + } + ] + } + } + } + } + }, + { + "i": "5ed849da-0f2b-454a-88dc-bb37e8c73285", + "x": 0, + "y": 13, + "w": 4, + "h": 5, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Power Feed Details", + "description": "All power feeds, including any power feed custom fields", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[powerFeeds]}}", + "name": "powerFeeds", + "pluginConfigId": "{{configId}}", + "sort": { + "by": [ + [ + "power_panel.site.name", + "asc" + ], + [ + "rack.name", + "asc" + ], + [ + "name", + "asc" + ] + ] + } + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "name", + "power_panel.site.name", + "rack.name", + "power_panel.name", + "statusLabel", + "typeLabel", + "supplyLabel", + "phaseLabel", + "tenant.name", + "description" + ], + "hiddenColumns": [ + "voltage", + "amperage", + "availableKva", + "max_utilization", + "connected", + "created", + "last_updated" + ] + } + } + } + } + } + ] + } +} diff --git a/plugins/NetBox/v1/defaultContent/prefix.dash.json b/plugins/NetBox/v1/defaultContent/prefix.dash.json new file mode 100644 index 00000000..0605d7e8 --- /dev/null +++ b/plugins/NetBox/v1/defaultContent/prefix.dash.json @@ -0,0 +1,471 @@ +{ + "name": "Prefix", + "schemaVersion": "1.5", + "timeframe": "last30days", + "variables": [ + "{{variables.[NetBox Prefix]}}" + ], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "9881a921-a9e3-4856-be7b-7f26ef11ec5c", + "x": 0, + "y": 0, + "w": 1, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Prefix Properties", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "datastream-properties", + "name": "properties", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[NetBox Prefixes]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[NetBox Prefix]}}" + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": true, + "columnOrder": [ + "name" + ], + "hiddenColumns": [ + "id", + "links", + "type", + "label", + "sourceId", + "sourceType", + "siteId", + "rackId", + "vrfId" + ] + } + } + }, + "variables": [ + "{{variables.[NetBox Prefix]}}" + ] + } + }, + { + "i": "bf430aa6-68da-4fd3-90db-d202d90cbab1", + "x": 1, + "y": 0, + "w": 1, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Prefix Utilization", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[prefixUtilization]}}", + "name": "prefixUtilization", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[NetBox Prefixes]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[NetBox Prefix]}}" + }, + "visualisation": { + "type": "data-stream-gauge", + "config": { + "data-stream-gauge": { + "value": { + "type": "mean", + "columns": [ + "utilization" + ] + }, + "label": "Utilization (%)", + "minimum": 0, + "maximum": 100 + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "gauge", + "encoding": { + "value": { + "field": "utilization", + "type": "quantitative", + "title": "Utilization" + } + }, + "style": { + "gaugeLabelText": "Utilization", + "gaugeBands": [ + { + "to": 0.8, + "color": "#2fb344" + }, + { + "to": 0.9, + "color": "#f59f00" + }, + { + "to": 1, + "color": "#d63939" + } + ] + } + } + }, + "variables": [ + "{{variables.[NetBox Prefix]}}" + ] + } + }, + { + "i": "b2b3dfa7-52c7-4065-8198-23466cb00eb0", + "x": 2, + "y": 0, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Address Space", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[prefixUtilization]}}", + "name": "prefixUtilization", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[NetBox Prefixes]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[NetBox Prefix]}}" + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": true, + "columnOrder": [ + "prefix", + "vrf", + "status", + "size", + "used", + "utilization", + "netboxUrl" + ], + "hiddenColumns": [] + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "scalar", + "encoding": { + "values": [ + { + "field": "size", + "type": "quantitative", + "title": "Addresses" + }, + { + "field": "used", + "type": "quantitative", + "title": "Used" + }, + { + "field": "utilization", + "type": "quantitative", + "title": "Utilization" + } + ] + }, + "style": { + "scalarBoxWidth": 300, + "scalarBoxHeight": 220, + "scalarFontSize": 56 + } + } + }, + "variables": [ + "{{variables.[NetBox Prefix]}}" + ] + } + }, + { + "i": "17000eb1-6f73-4ba6-981f-b9bacd2d6ff4", + "x": 0, + "y": 3, + "w": 4, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "IP Addresses in Prefix", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[ipAddresses]}}", + "name": "ipAddresses", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "prefix": { + "variable": "{{variables.[NetBox Prefix]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[NetBox Prefixes]}}" + } + }, + "sort": { + "by": [ + [ + "address", + "asc" + ] + ] + } + }, + "variables": [ + "{{variables.[NetBox Prefix]}}" + ], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "address", + "status.label", + "vrfName", + "dns_name", + "assignedTo", + "tenant.name", + "description", + "display_url" + ], + "hiddenColumns": [ + "display", + "created", + "family.label", + "assigned", + "role.label" + ] + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "display_url", + "title": "Address", + "renderer": { + "type": "link", + "labelField": "address" + } + }, + { + "field": "status.label", + "title": "Status", + "renderer": { + "type": "badge", + "colors": { + "Active": "#2fb344", + "Reserved": "#17a2b8", + "Deprecated": "#d63939", + "DHCP": "#2fb344", + "SLAAC": "#2fb344" + } + } + }, + { + "field": "vrfName", + "title": "VRF", + "renderer": { + "type": "text" + } + }, + { + "field": "dns_name", + "title": "DNS Name", + "renderer": { + "type": "text" + } + }, + { + "field": "assignedTo", + "title": "Assigned To", + "renderer": { + "type": "text" + } + }, + { + "field": "tenant.name", + "title": "Tenant", + "renderer": { + "type": "text" + } + }, + { + "field": "description", + "title": "Description", + "renderer": { + "type": "text" + } + } + ] + } + } + } + } + }, + { + "i": "892f873a-2e86-400d-9414-d1d02025fe83", + "x": 0, + "y": 7, + "w": 4, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Change Log", + "description": "Changes recorded against this prefix", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[objectChanges]}}", + "name": "objectChanges", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "object": { + "variable": "{{variables.[NetBox Prefix]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[NetBox Prefixes]}}" + } + }, + "sort": { + "by": [ + [ + "time", + "desc" + ] + ] + } + }, + "variables": [ + "{{variables.[NetBox Prefix]}}" + ], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "time", + "user_name", + "action.label", + "object_repr", + "message" + ], + "hiddenColumns": [ + "changed_object_type" + ] + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "time", + "title": "Time", + "renderer": { + "type": "date", + "relative": true + } + }, + { + "field": "user_name", + "title": "User", + "renderer": { + "type": "text" + } + }, + { + "field": "action.label", + "title": "Action", + "renderer": { + "type": "badge", + "colors": { + "Created": "#2fb344", + "Updated": "#206bc4", + "Deleted": "#d63939" + } + } + }, + { + "field": "object_repr", + "title": "Object", + "renderer": { + "type": "text" + } + }, + { + "field": "message", + "title": "Message", + "renderer": { + "type": "text" + } + } + ] + } + } + } + } + } + ] + } +} diff --git a/plugins/NetBox/v1/defaultContent/rack.dash.json b/plugins/NetBox/v1/defaultContent/rack.dash.json new file mode 100644 index 00000000..58134f0b --- /dev/null +++ b/plugins/NetBox/v1/defaultContent/rack.dash.json @@ -0,0 +1,652 @@ +{ + "name": "Rack", + "schemaVersion": "1.5", + "timeframe": "last30days", + "variables": [ + "{{variables.[NetBox Rack]}}" + ], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "43988400-b66b-4bb7-83aa-f0338c85412e", + "x": 0, + "y": 0, + "w": 1, + "h": 6, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Rack Properties", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "datastream-properties", + "name": "properties", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[NetBox Racks]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[NetBox Rack]}}" + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": true, + "columnOrder": [ + "name" + ], + "hiddenColumns": [ + "id", + "links", + "type", + "label", + "sourceId", + "sourceType", + "siteId", + "rackId", + "vrfId" + ] + } + } + }, + "variables": [ + "{{variables.[NetBox Rack]}}" + ] + } + }, + { + "i": "f17e7c09-9f36-4406-9595-d7312fa32c58", + "x": 1, + "y": 0, + "w": 1, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Rack Utilization", + "description": "Rack units used on either face", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[rackUtilization]}}", + "name": "rackUtilization", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "rack": { + "variable": "{{variables.[NetBox Rack]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[NetBox Racks]}}" + } + } + }, + "variables": [ + "{{variables.[NetBox Rack]}}" + ], + "visualisation": { + "type": "data-stream-gauge", + "config": { + "data-stream-gauge": { + "value": { + "type": "mean", + "columns": [ + "utilization" + ] + }, + "label": "Utilization (%)", + "minimum": 0, + "maximum": 100 + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "gauge", + "encoding": { + "value": { + "field": "utilization", + "type": "quantitative", + "title": "Utilization" + } + }, + "style": { + "gaugeLabelText": "Utilization" + } + } + } + } + }, + { + "i": "82367003-5ed9-4d70-ae6d-9e0102289fbe", + "x": 2, + "y": 0, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Rack Space", + "description": "Rack units used on either face", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[rackUtilization]}}", + "name": "rackUtilization", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "rack": { + "variable": "{{variables.[NetBox Rack]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[NetBox Racks]}}" + } + } + }, + "variables": [ + "{{variables.[NetBox Rack]}}" + ], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": true, + "columnOrder": [ + "uHeight", + "usedUnits", + "availableUnits", + "frontUsed", + "rearUsed", + "utilization", + "deviceCount" + ], + "hiddenColumns": [ + "rack", + "site", + "location" + ] + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "scalar", + "encoding": { + "values": [ + { + "field": "uHeight", + "type": "quantitative", + "title": "Rack Units" + }, + { + "field": "usedUnits", + "type": "quantitative", + "title": "Used Units" + }, + { + "field": "availableUnits", + "type": "quantitative", + "title": "Available Units" + }, + { + "field": "deviceCount", + "type": "quantitative", + "title": "Devices" + } + ] + }, + "style": { + "scalarBoxWidth": 440, + "scalarBoxHeight": 130, + "scalarFontSize": 48 + } + } + } + } + }, + { + "i": "d1a3e0f6-e23f-424a-bac4-808ff6494e06", + "x": 1, + "y": 3, + "w": 3, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Devices by Role in Rack", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[devices]}}", + "name": "devices", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "rack": { + "variable": "{{variables.[NetBox Rack]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[NetBox Racks]}}" + } + }, + "group": { + "by": [ + [ + "role.name", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + }, + "sort": { + "by": [ + [ + "count", + "desc" + ] + ] + } + }, + "variables": [ + "{{variables.[NetBox Rack]}}" + ], + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisData": "role.name_uniqueValues", + "yAxisData": [ + "count" + ], + "xAxisGroup": "none", + "xAxisLabel": "", + "yAxisLabel": "", + "showXAxisLabel": false, + "showYAxisLabel": false, + "showLegend": false, + "legendPosition": "bottom", + "showGrid": true, + "horizontalLayout": "horizontal", + "displayMode": "actual", + "showTotals": false, + "showValue": false, + "grouping": false, + "range": { + "type": "auto" + } + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "bar", + "encoding": { + "x": { + "field": "role.name_uniqueValues", + "type": "nominal", + "title": "Role" + }, + "y": { + "field": "count", + "type": "quantitative", + "title": "Devices" + } + }, + "style": { + "cartesianHorizontal": true + } + } + } + } + }, + { + "i": "16624438-7872-4cf0-a871-44954036cc69", + "x": 0, + "y": 6, + "w": 4, + "h": 9, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Rack Elevation", + "description": "Each rack unit from the top down, with the device mounted at it. Empty units are listed; 0U, child and unracked devices follow the units.", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[rackElevation]}}", + "name": "rackElevation", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "rack": { + "variable": "{{variables.[NetBox Rack]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[NetBox Racks]}}" + } + }, + "sort": { + "by": [ + [ + "order", + "asc" + ] + ] + } + }, + "variables": [ + "{{variables.[NetBox Rack]}}" + ], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "unit", + "device", + "role", + "deviceType", + "manufacturer", + "face", + "deviceHeight", + "unitsLabel" + ], + "hiddenColumns": [ + "rack", + "site", + "isTopUnit", + "occupied" + ] + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "unit", + "title": "Unit", + "renderer": { + "type": "number" + }, + "align": "right", + "width": 70 + }, + { + "field": "device", + "title": "Device", + "renderer": { + "type": "text" + } + }, + { + "field": "role", + "title": "Role", + "renderer": { + "type": "text" + } + }, + { + "field": "deviceType", + "title": "Device Type", + "renderer": { + "type": "text" + } + }, + { + "field": "manufacturer", + "title": "Manufacturer", + "renderer": { + "type": "text" + } + }, + { + "field": "face", + "title": "Face", + "renderer": { + "type": "text" + } + }, + { + "field": "deviceHeight", + "title": "Height (U)", + "renderer": { + "type": "number" + }, + "align": "right" + }, + { + "field": "unitsLabel", + "title": "Device Units", + "renderer": { + "type": "text" + } + } + ] + }, + "style": { + "richTableCellPadding": 4 + } + } + } + } + }, + { + "i": "16d97f67-d02d-464e-a737-974718bcc13c", + "x": 0, + "y": 15, + "w": 4, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Devices in Rack", + "description": "Includes any device custom fields", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[devices]}}", + "name": "devices", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "rack": { + "variable": "{{variables.[NetBox Rack]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[NetBox Racks]}}" + } + }, + "sort": { + "by": [ + [ + "position", + "desc" + ] + ] + } + }, + "variables": [ + "{{variables.[NetBox Rack]}}" + ], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "display", + "position", + "status.label", + "role.name", + "device_type.manufacturer.name", + "device_type.model", + "primary_ip.display", + "serial", + "display_url" + ], + "hiddenColumns": [ + "missingSerial", + "missingPrimaryIp", + "missingTenant", + "missingPlatform", + "daysSinceUpdate", + "created", + "description", + "oob_ip.address", + "asset_tag", + "site.name", + "rack.name", + "location.name" + ] + } + } + } + } + }, + { + "i": "e6ac60cc-259e-4381-8a5e-54fef71f4543", + "x": 0, + "y": 19, + "w": 4, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Change Log", + "description": "Changes recorded against this rack", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[objectChanges]}}", + "name": "objectChanges", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "object": { + "variable": "{{variables.[NetBox Rack]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[NetBox Racks]}}" + } + }, + "sort": { + "by": [ + [ + "time", + "desc" + ] + ] + } + }, + "variables": [ + "{{variables.[NetBox Rack]}}" + ], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "time", + "user_name", + "action.label", + "object_repr", + "message" + ], + "hiddenColumns": [ + "changed_object_type" + ] + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "time", + "title": "Time", + "renderer": { + "type": "date", + "relative": true + } + }, + { + "field": "user_name", + "title": "User", + "renderer": { + "type": "text" + } + }, + { + "field": "action.label", + "title": "Action", + "renderer": { + "type": "badge", + "colors": { + "Created": "#2fb344", + "Updated": "#206bc4", + "Deleted": "#d63939" + } + } + }, + { + "field": "object_repr", + "title": "Object", + "renderer": { + "type": "text" + } + }, + { + "field": "message", + "title": "Message", + "renderer": { + "type": "text" + } + } + ] + } + } + } + } + } + ] + } +} diff --git a/plugins/NetBox/v1/defaultContent/rackCapacity.dash.json b/plugins/NetBox/v1/defaultContent/rackCapacity.dash.json new file mode 100644 index 00000000..3321948a --- /dev/null +++ b/plugins/NetBox/v1/defaultContent/rackCapacity.dash.json @@ -0,0 +1,569 @@ +{ + "name": "Rack Capacity", + "schemaVersion": "1.5", + "timeframe": "none", + "variables": [], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "d15771ec-0ca0-4434-b854-4709532f339a", + "x": 0, + "y": 0, + "w": 4, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Rack Space Summary", + "description": "Rack units used on either face, across all racks", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[rackUtilization]}}", + "name": "rackUtilization", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + }, + { + "type": "sum", + "names": [ + "uHeight" + ] + }, + { + "type": "sum", + "names": [ + "usedUnits" + ] + }, + { + "type": "sum", + "names": [ + "availableUnits" + ] + }, + { + "type": "mean", + "names": [ + "utilization" + ] + }, + { + "type": "sum", + "names": [ + "deviceCount" + ] + } + ] + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Racks" + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "scalar", + "encoding": { + "values": [ + { + "field": "count", + "type": "quantitative", + "title": "Racks" + }, + { + "field": "uHeight_sum", + "type": "quantitative", + "title": "Rack Units" + }, + { + "field": "usedUnits_sum", + "type": "quantitative", + "title": "Used Units" + }, + { + "field": "availableUnits_sum", + "type": "quantitative", + "title": "Available Units" + }, + { + "field": "utilization_mean", + "type": "quantitative", + "title": "Average Utilization" + }, + { + "field": "deviceCount_sum", + "type": "quantitative", + "title": "Racked Devices" + } + ] + }, + "style": { + "scalarBoxWidth": 310, + "scalarBoxHeight": 140, + "scalarFontSize": 48 + } + } + } + } + }, + { + "i": "8f9df73b-a543-409b-a217-3f3e5778f12f", + "x": 0, + "y": 2, + "w": 2, + "h": 5, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Top 15 Racks by Utilization", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[rackUtilization]}}", + "name": "rackUtilization", + "pluginConfigId": "{{configId}}", + "sort": { + "by": [ + [ + "utilization", + "desc" + ] + ], + "top": 15 + } + }, + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisData": "rack", + "yAxisData": [ + "utilization" + ], + "xAxisGroup": "none", + "xAxisLabel": "", + "yAxisLabel": "Utilization (%)", + "showXAxisLabel": false, + "showYAxisLabel": true, + "showLegend": false, + "legendPosition": "bottom", + "showGrid": true, + "horizontalLayout": "horizontal", + "displayMode": "actual", + "showTotals": false, + "showValue": false, + "grouping": false, + "range": { + "type": "auto" + } + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "bar", + "encoding": { + "x": { + "field": "rack", + "type": "nominal", + "title": "Rack" + }, + "y": { + "field": "utilization", + "type": "quantitative", + "title": "Utilization" + } + }, + "style": { + "cartesianHorizontal": true, + "cartesianYAxisRangeMode": "percentage" + } + } + } + } + }, + { + "i": "7b57f4a0-6736-4f2e-b104-529f7449f052", + "x": 2, + "y": 2, + "w": 2, + "h": 5, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Racks per Site", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[rackUtilization]}}", + "name": "rackUtilization", + "pluginConfigId": "{{configId}}", + "group": { + "by": [ + [ + "site", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + }, + { + "type": "sum", + "names": [ + "deviceCount" + ] + }, + { + "type": "sum", + "names": [ + "usedUnits" + ] + }, + { + "type": "sum", + "names": [ + "availableUnits" + ] + }, + { + "type": "mean", + "names": [ + "utilization" + ] + } + ] + }, + "sort": { + "by": [ + [ + "count", + "desc" + ], + [ + "site_uniqueValues", + "asc" + ] + ] + } + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "site_uniqueValues", + "count", + "deviceCount_sum", + "usedUnits_sum", + "availableUnits_sum", + "utilization_mean" + ], + "hiddenColumns": [], + "columnDisplayNames": { + "site_uniqueValues": "Site", + "count": "Racks", + "deviceCount_sum": "Devices", + "usedUnits_sum": "Used Units", + "availableUnits_sum": "Available Units", + "utilization_mean": "Average Utilization" + } + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "site_uniqueValues", + "title": "Site", + "renderer": { + "type": "text" + } + }, + { + "field": "count", + "title": "Racks", + "renderer": { + "type": "bar" + } + }, + { + "field": "deviceCount_sum", + "title": "Devices", + "renderer": { + "type": "number" + }, + "align": "right" + }, + { + "field": "usedUnits_sum", + "title": "Used Units", + "renderer": { + "type": "number" + }, + "align": "right" + }, + { + "field": "availableUnits_sum", + "title": "Available Units", + "renderer": { + "type": "number" + }, + "align": "right" + }, + { + "field": "utilization_mean", + "title": "Average Utilization", + "renderer": { + "type": "progress", + "max": 100 + } + } + ] + } + } + } + } + }, + { + "i": "df7f4b44-8a16-4b73-bdc5-c3608f110f5d", + "x": 0, + "y": 7, + "w": 4, + "h": 6, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Rack Utilization", + "description": "Rack units used on either face of each rack", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[rackUtilization]}}", + "name": "rackUtilization", + "pluginConfigId": "{{configId}}", + "sort": { + "by": [ + [ + "utilization", + "desc" + ] + ] + } + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "rack", + "site", + "location", + "uHeight", + "deviceCount", + "usedUnits", + "availableUnits", + "frontUsed", + "rearUsed", + "utilization" + ], + "hiddenColumns": [] + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "rack", + "title": "Rack", + "renderer": { + "type": "text" + } + }, + { + "field": "site", + "title": "Site", + "renderer": { + "type": "text" + } + }, + { + "field": "location", + "title": "Location", + "renderer": { + "type": "text" + } + }, + { + "field": "uHeight", + "title": "U Height", + "renderer": { + "type": "number" + }, + "align": "right" + }, + { + "field": "deviceCount", + "title": "Devices", + "renderer": { + "type": "number" + }, + "align": "right" + }, + { + "field": "usedUnits", + "title": "Used Units", + "renderer": { + "type": "number" + }, + "align": "right" + }, + { + "field": "availableUnits", + "title": "Available Units", + "renderer": { + "type": "number" + }, + "align": "right" + }, + { + "field": "frontUsed", + "title": "Front Used", + "renderer": { + "type": "number" + }, + "align": "right" + }, + { + "field": "rearUsed", + "title": "Rear Used", + "renderer": { + "type": "number" + }, + "align": "right" + }, + { + "field": "utilization", + "title": "Utilization", + "renderer": { + "type": "progress", + "max": 100 + } + } + ] + } + } + } + } + }, + { + "i": "c4e4ec98-facd-4e81-8875-b5956c6a9bc4", + "x": 0, + "y": 13, + "w": 4, + "h": 5, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Rack Details", + "description": "Includes any rack custom fields", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[racks]}}", + "name": "racks", + "pluginConfigId": "{{configId}}", + "sort": { + "by": [ + [ + "site.name", + "asc" + ], + [ + "display", + "asc" + ] + ] + } + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "display", + "status.label", + "site.name", + "location.name", + "role.name", + "u_height", + "device_count", + "powerfeed_count", + "tenant.name", + "facility_id", + "display_url" + ], + "hiddenColumns": [ + "created", + "serial", + "asset_tag", + "form_factor.label" + ] + } + } + } + } + } + ] + } +} diff --git a/plugins/NetBox/v1/defaultContent/scopes.json b/plugins/NetBox/v1/defaultContent/scopes.json new file mode 100644 index 00000000..f9ad16b3 --- /dev/null +++ b/plugins/NetBox/v1/defaultContent/scopes.json @@ -0,0 +1,104 @@ +[ + { + "name": "NetBox Sites", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Site" + ] + } + }, + "variable": { + "name": "NetBox Site", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + }, + { + "name": "NetBox Racks", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Rack" + ] + } + }, + "variable": { + "name": "NetBox Rack", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + }, + { + "name": "NetBox Devices", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Device" + ] + } + }, + "variable": { + "name": "NetBox Device", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + }, + { + "name": "NetBox Virtual Machines", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Virtual Machine" + ] + } + }, + "variable": { + "name": "NetBox Virtual Machine", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + }, + { + "name": "NetBox Prefixes", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Prefix" + ] + } + }, + "variable": { + "name": "NetBox Prefix", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + }, + { + "name": "NetBox Circuits", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Circuit" + ] + } + }, + "variable": { + "name": "NetBox Circuit", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + } +] diff --git a/plugins/NetBox/v1/defaultContent/site.dash.json b/plugins/NetBox/v1/defaultContent/site.dash.json new file mode 100644 index 00000000..237b14b5 --- /dev/null +++ b/plugins/NetBox/v1/defaultContent/site.dash.json @@ -0,0 +1,1135 @@ +{ + "name": "Site", + "schemaVersion": "1.5", + "timeframe": "last30days", + "variables": [ + "{{variables.[NetBox Site]}}" + ], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "f3d9fd21-2157-4e09-862d-39ba62eb31fe", + "x": 0, + "y": 0, + "w": 1, + "h": 6, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Site Properties", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "datastream-properties", + "name": "properties", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[NetBox Sites]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[NetBox Site]}}" + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": true, + "columnOrder": [ + "name" + ], + "hiddenColumns": [ + "id", + "links", + "type", + "label", + "sourceId", + "sourceType", + "siteId", + "rackId", + "vrfId" + ] + } + } + }, + "variables": [ + "{{variables.[NetBox Site]}}" + ] + } + }, + { + "i": "e73db72a-e2b9-456c-8b27-62a5dfc8275f", + "x": 1, + "y": 0, + "w": 3, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Rack Space at Site", + "description": "Rack units used on either face, across the racks at this site", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[rackUtilization]}}", + "name": "rackUtilization", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "site": { + "variable": "{{variables.[NetBox Site]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[NetBox Sites]}}" + } + }, + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + }, + { + "type": "sum", + "names": [ + "usedUnits" + ] + }, + { + "type": "sum", + "names": [ + "availableUnits" + ] + }, + { + "type": "mean", + "names": [ + "utilization" + ] + }, + { + "type": "sum", + "names": [ + "deviceCount" + ] + } + ] + } + }, + "variables": [ + "{{variables.[NetBox Site]}}" + ], + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "usedUnits_sum", + "comparisonColumn": "none", + "label": "Used Units" + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "scalar", + "encoding": { + "values": [ + { + "field": "count", + "type": "quantitative", + "title": "Racks" + }, + { + "field": "usedUnits_sum", + "type": "quantitative", + "title": "Used Units" + }, + { + "field": "availableUnits_sum", + "type": "quantitative", + "title": "Available Units" + }, + { + "field": "utilization_mean", + "type": "quantitative", + "title": "Average Utilization" + }, + { + "field": "deviceCount_sum", + "type": "quantitative", + "title": "Racked Devices" + } + ] + }, + "style": { + "scalarBoxWidth": 285, + "scalarBoxHeight": 140, + "scalarFontSize": 44 + } + } + } + } + }, + { + "i": "8bbdf85a-bf67-4415-963c-dbcdb8bd60cd", + "x": 1, + "y": 2, + "w": 3, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Devices by Role at Site", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[devices]}}", + "name": "devices", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "site": { + "variable": "{{variables.[NetBox Site]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[NetBox Sites]}}" + } + }, + "group": { + "by": [ + [ + "role.name", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + }, + "sort": { + "by": [ + [ + "count", + "desc" + ] + ] + } + }, + "variables": [ + "{{variables.[NetBox Site]}}" + ], + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisData": "role.name_uniqueValues", + "yAxisData": [ + "count" + ], + "xAxisGroup": "none", + "xAxisLabel": "", + "yAxisLabel": "", + "showXAxisLabel": false, + "showYAxisLabel": false, + "showLegend": false, + "legendPosition": "bottom", + "showGrid": true, + "horizontalLayout": "horizontal", + "displayMode": "actual", + "showTotals": false, + "showValue": false, + "grouping": false, + "range": { + "type": "auto" + } + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "bar", + "encoding": { + "x": { + "field": "role.name_uniqueValues", + "type": "nominal", + "title": "Role" + }, + "y": { + "field": "count", + "type": "quantitative", + "title": "Devices" + } + }, + "style": { + "cartesianHorizontal": true + } + } + } + } + }, + { + "i": "8065eedd-aaed-440d-b064-f47ebccb4d18", + "x": 0, + "y": 6, + "w": 2, + "h": 5, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Rack Utilization at Site", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[rackUtilization]}}", + "name": "rackUtilization", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "site": { + "variable": "{{variables.[NetBox Site]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[NetBox Sites]}}" + } + }, + "sort": { + "by": [ + [ + "rack", + "asc" + ] + ] + } + }, + "variables": [ + "{{variables.[NetBox Site]}}" + ], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "rack", + "location", + "uHeight", + "deviceCount", + "usedUnits", + "availableUnits", + "utilization" + ], + "hiddenColumns": [ + "site", + "frontUsed", + "rearUsed" + ] + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "rack", + "title": "Rack", + "renderer": { + "type": "text" + } + }, + { + "field": "location", + "title": "Location", + "renderer": { + "type": "text" + } + }, + { + "field": "uHeight", + "title": "U Height", + "renderer": { + "type": "number" + }, + "align": "right" + }, + { + "field": "deviceCount", + "title": "Devices", + "renderer": { + "type": "number" + }, + "align": "right" + }, + { + "field": "usedUnits", + "title": "Used Units", + "renderer": { + "type": "number" + }, + "align": "right" + }, + { + "field": "availableUnits", + "title": "Available Units", + "renderer": { + "type": "number" + }, + "align": "right" + }, + { + "field": "utilization", + "title": "Utilization", + "renderer": { + "type": "progress", + "max": 100 + } + } + ] + } + } + } + } + }, + { + "i": "a37be3cb-d8ef-4d54-8c4e-86978bda496f", + "x": 2, + "y": 6, + "w": 2, + "h": 5, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Power Feeds at Site", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[powerFeeds]}}", + "name": "powerFeeds", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "site": { + "variable": "{{variables.[NetBox Site]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[NetBox Sites]}}" + } + }, + "sort": { + "by": [ + [ + "rack.name", + "asc" + ], + [ + "name", + "asc" + ] + ] + } + }, + "variables": [ + "{{variables.[NetBox Site]}}" + ], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "rack.name", + "name", + "power_panel.name", + "phaseLabel", + "availableKva", + "max_utilization", + "statusLabel" + ], + "hiddenColumns": [ + "power_panel.site.name", + "typeLabel", + "supplyLabel", + "voltage", + "amperage", + "connected", + "tenant.name", + "description", + "created", + "last_updated" + ] + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "rack.name", + "title": "Rack", + "renderer": { + "type": "text" + } + }, + { + "field": "name", + "title": "Power Feed", + "renderer": { + "type": "text" + } + }, + { + "field": "power_panel.name", + "title": "Power Panel", + "renderer": { + "type": "text" + } + }, + { + "field": "phaseLabel", + "title": "Phase", + "renderer": { + "type": "text" + } + }, + { + "field": "availableKva", + "title": "Available Power (kVA)", + "renderer": { + "type": "number", + "decimals": 2 + }, + "align": "right" + }, + { + "field": "max_utilization", + "title": "Max Utilization", + "renderer": { + "type": "progress", + "max": 100 + } + }, + { + "field": "statusLabel", + "title": "Status", + "renderer": { + "type": "badge", + "colors": { + "Active": "#2fb344", + "Planned": "#17a2b8", + "Offline": "#9ba3af", + "Failed": "#d63939" + } + } + } + ] + } + } + } + } + }, + { + "i": "e513f662-5c76-468d-91e9-d4e784800020", + "x": 0, + "y": 11, + "w": 4, + "h": 5, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Devices", + "description": "Includes any device custom fields", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[devices]}}", + "name": "devices", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "site": { + "variable": "{{variables.[NetBox Site]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[NetBox Sites]}}" + } + }, + "sort": { + "by": [ + [ + "display", + "asc" + ] + ] + } + }, + "variables": [ + "{{variables.[NetBox Site]}}" + ], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "display", + "status.label", + "role.name", + "device_type.manufacturer.name", + "device_type.model", + "platform.name", + "rack.name", + "position", + "primary_ip.display", + "tenant.name", + "serial", + "display_url" + ], + "hiddenColumns": [ + "missingSerial", + "missingPrimaryIp", + "missingTenant", + "missingPlatform", + "daysSinceUpdate", + "created", + "description", + "oob_ip.address", + "asset_tag", + "site.name" + ] + } + } + } + } + }, + { + "i": "e8c86afa-32d8-4de3-9c17-baed5cad9862", + "x": 0, + "y": 16, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Prefixes", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[prefixes]}}", + "name": "prefixes", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "site": { + "variable": "{{variables.[NetBox Site]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[NetBox Sites]}}" + } + }, + "sort": { + "by": [ + [ + "prefix", + "asc" + ] + ] + } + }, + "variables": [ + "{{variables.[NetBox Site]}}" + ], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "prefix", + "status.label", + "vrf.name", + "role.name", + "vlan.display", + "display_url" + ], + "hiddenColumns": [ + "created", + "_depth", + "mark_utilized", + "family.label", + "scope.name", + "tenant.name", + "is_pool", + "children", + "description" + ] + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "display_url", + "title": "Prefix", + "renderer": { + "type": "link", + "labelField": "prefix" + } + }, + { + "field": "status.label", + "title": "Status", + "renderer": { + "type": "badge", + "colors": { + "Active": "#2fb344", + "Container": "#9ba3af", + "Reserved": "#17a2b8", + "Deprecated": "#d63939" + } + } + }, + { + "field": "vrf.name", + "title": "VRF", + "renderer": { + "type": "text" + } + }, + { + "field": "role.name", + "title": "Role", + "renderer": { + "type": "text" + } + }, + { + "field": "vlan.display", + "title": "VLAN", + "renderer": { + "type": "text" + } + } + ] + } + } + } + } + }, + { + "i": "8ece4343-0a2d-4620-971f-921b9e6764ab", + "x": 2, + "y": 16, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Virtual Machines", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[virtualMachines]}}", + "name": "virtualMachines", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "site": { + "variable": "{{variables.[NetBox Site]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[NetBox Sites]}}" + } + }, + "sort": { + "by": [ + [ + "display", + "asc" + ] + ] + } + }, + "variables": [ + "{{variables.[NetBox Site]}}" + ], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "display", + "status.label", + "cluster.name", + "device.name", + "role.name", + "platform.name", + "primary_ip.display", + "vcpus", + "memory", + "disk", + "tenant.name", + "display_url" + ], + "hiddenColumns": [ + "created", + "description", + "site.name" + ] + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "display_url", + "title": "Virtual Machine", + "renderer": { + "type": "link", + "labelField": "display" + } + }, + { + "field": "status.label", + "title": "Status", + "renderer": { + "type": "badge", + "colors": { + "Active": "#2fb344", + "Planned": "#17a2b8", + "Staged": "#206bc4", + "Failed": "#d63939", + "Offline": "#9ba3af", + "Paused": "#f76707", + "Decommissioning": "#f59f00" + } + } + }, + { + "field": "cluster.name", + "title": "Cluster", + "renderer": { + "type": "text" + } + }, + { + "field": "role.name", + "title": "Role", + "renderer": { + "type": "text" + } + }, + { + "field": "primary_ip.display", + "title": "Primary IP", + "renderer": { + "type": "text" + } + } + ] + } + } + } + } + }, + { + "i": "12c71db6-1863-480a-b4b9-8a9cc9ee2af9", + "x": 0, + "y": 20, + "w": 4, + "h": 9, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Rack Elevation at Site", + "description": "Every rack at the site, each unit from the top down with the device mounted at it. Empty units are listed.", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[rackElevation]}}", + "name": "rackElevation", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "site": { + "variable": "{{variables.[NetBox Site]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[NetBox Sites]}}" + } + }, + "sort": { + "by": [ + [ + "order", + "asc" + ] + ] + } + }, + "variables": [ + "{{variables.[NetBox Site]}}" + ], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "rack", + "unit", + "device", + "role", + "deviceType", + "manufacturer", + "face", + "deviceHeight", + "unitsLabel" + ], + "hiddenColumns": [ + "site", + "isTopUnit", + "occupied" + ] + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "rack", + "title": "Rack", + "renderer": { + "type": "text" + } + }, + { + "field": "unit", + "title": "Unit", + "renderer": { + "type": "number" + }, + "align": "right", + "width": 70 + }, + { + "field": "device", + "title": "Device", + "renderer": { + "type": "text" + } + }, + { + "field": "role", + "title": "Role", + "renderer": { + "type": "text" + } + }, + { + "field": "deviceType", + "title": "Device Type", + "renderer": { + "type": "text" + } + }, + { + "field": "manufacturer", + "title": "Manufacturer", + "renderer": { + "type": "text" + } + }, + { + "field": "face", + "title": "Face", + "renderer": { + "type": "text" + } + }, + { + "field": "deviceHeight", + "title": "Height (U)", + "renderer": { + "type": "number" + }, + "align": "right" + }, + { + "field": "unitsLabel", + "title": "Device Units", + "renderer": { + "type": "text" + } + } + ] + }, + "style": { + "richTableCellPadding": 4 + } + } + } + } + }, + { + "i": "ca968a13-efcd-44a5-80b2-f604773022be", + "x": 0, + "y": 29, + "w": 4, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Change Log", + "description": "Changes recorded against this site", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[objectChanges]}}", + "name": "objectChanges", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "object": { + "variable": "{{variables.[NetBox Site]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[NetBox Sites]}}" + } + }, + "sort": { + "by": [ + [ + "time", + "desc" + ] + ] + } + }, + "variables": [ + "{{variables.[NetBox Site]}}" + ], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "time", + "user_name", + "action.label", + "object_repr", + "message" + ], + "hiddenColumns": [ + "changed_object_type" + ] + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "time", + "title": "Time", + "renderer": { + "type": "date", + "relative": true + } + }, + { + "field": "user_name", + "title": "User", + "renderer": { + "type": "text" + } + }, + { + "field": "action.label", + "title": "Action", + "renderer": { + "type": "badge", + "colors": { + "Created": "#2fb344", + "Updated": "#206bc4", + "Deleted": "#d63939" + } + } + }, + { + "field": "object_repr", + "title": "Object", + "renderer": { + "type": "text" + } + }, + { + "field": "message", + "title": "Message", + "renderer": { + "type": "text" + } + } + ] + } + } + } + } + } + ] + } +} diff --git a/plugins/NetBox/v1/defaultContent/virtualMachine.dash.json b/plugins/NetBox/v1/defaultContent/virtualMachine.dash.json new file mode 100644 index 00000000..a34f5a19 --- /dev/null +++ b/plugins/NetBox/v1/defaultContent/virtualMachine.dash.json @@ -0,0 +1,313 @@ +{ + "name": "Virtual Machine", + "schemaVersion": "1.5", + "timeframe": "last30days", + "variables": [ + "{{variables.[NetBox Virtual Machine]}}" + ], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "c503a756-16b7-42c7-9984-7c51b9348e52", + "x": 0, + "y": 0, + "w": 1, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Virtual Machine Properties", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "datastream-properties", + "name": "properties", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[NetBox Virtual Machines]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[NetBox Virtual Machine]}}" + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": true, + "columnOrder": [ + "name" + ], + "hiddenColumns": [ + "id", + "links", + "type", + "label", + "sourceId", + "sourceType", + "siteId", + "rackId", + "vrfId" + ] + } + } + }, + "variables": [ + "{{variables.[NetBox Virtual Machine]}}" + ] + } + }, + { + "i": "e2d657f2-5dcb-492a-bee7-dd1673bdcdca", + "x": 1, + "y": 0, + "w": 3, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "IP Addresses", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[ipAddresses]}}", + "name": "ipAddresses", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "virtualMachine": { + "variable": "{{variables.[NetBox Virtual Machine]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[NetBox Virtual Machines]}}" + } + }, + "sort": { + "by": [ + [ + "address", + "asc" + ] + ] + } + }, + "variables": [ + "{{variables.[NetBox Virtual Machine]}}" + ], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "address", + "status.label", + "vrfName", + "dns_name", + "assignedTo", + "tenant.name", + "description", + "display_url" + ], + "hiddenColumns": [ + "display", + "created", + "family.label", + "assigned", + "role.label" + ] + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "display_url", + "title": "Address", + "renderer": { + "type": "link", + "labelField": "address" + } + }, + { + "field": "status.label", + "title": "Status", + "renderer": { + "type": "badge", + "colors": { + "Active": "#2fb344", + "Reserved": "#17a2b8", + "Deprecated": "#d63939", + "DHCP": "#2fb344", + "SLAAC": "#2fb344" + } + } + }, + { + "field": "vrfName", + "title": "VRF", + "renderer": { + "type": "text" + } + }, + { + "field": "dns_name", + "title": "DNS Name", + "renderer": { + "type": "text" + } + }, + { + "field": "assignedTo", + "title": "Assigned To", + "renderer": { + "type": "text" + } + }, + { + "field": "tenant.name", + "title": "Tenant", + "renderer": { + "type": "text" + } + }, + { + "field": "description", + "title": "Description", + "renderer": { + "type": "text" + } + } + ] + } + } + } + } + }, + { + "i": "26dcc49b-a0e9-4fee-8ce1-87892ec65583", + "x": 0, + "y": 4, + "w": 4, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Change Log", + "description": "Changes recorded against this virtual machine", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[objectChanges]}}", + "name": "objectChanges", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "object": { + "variable": "{{variables.[NetBox Virtual Machine]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[NetBox Virtual Machines]}}" + } + }, + "sort": { + "by": [ + [ + "time", + "desc" + ] + ] + } + }, + "variables": [ + "{{variables.[NetBox Virtual Machine]}}" + ], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "time", + "user_name", + "action.label", + "object_repr", + "message" + ], + "hiddenColumns": [ + "changed_object_type" + ] + } + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "time", + "title": "Time", + "renderer": { + "type": "date", + "relative": true + } + }, + { + "field": "user_name", + "title": "User", + "renderer": { + "type": "text" + } + }, + { + "field": "action.label", + "title": "Action", + "renderer": { + "type": "badge", + "colors": { + "Created": "#2fb344", + "Updated": "#206bc4", + "Deleted": "#d63939" + } + } + }, + { + "field": "object_repr", + "title": "Object", + "renderer": { + "type": "text" + } + }, + { + "field": "message", + "title": "Message", + "renderer": { + "type": "text" + } + } + ] + } + } + } + } + } + ] + } +} diff --git a/plugins/NetBox/v1/docs/README.md b/plugins/NetBox/v1/docs/README.md new file mode 100644 index 00000000..71711a86 --- /dev/null +++ b/plugins/NetBox/v1/docs/README.md @@ -0,0 +1,45 @@ +Visualize your [NetBox](https://netboxlabs.com/oss/netbox/) source of truth in SquaredUp: device and site inventory, IP prefix capacity, rack space, data quality gaps, circuits, and the change log. Data comes from the [NetBox REST API](https://netboxlabs.com/docs/netbox/integrations/rest-api/). + +The plugin works with **NetBox Community**, **NetBox Enterprise**, and **NetBox Cloud**. All three expose the same REST API, so no edition-specific setup is needed. + +## Setup + +You need the address of your NetBox instance and an **API token** for a user that can view the objects you want in SquaredUp. + +1. Sign in to NetBox in a browser and note the address. Examples: `https://netbox.example.com` for a self-managed instance, or `https://.cloud.netboxapp.com` for NetBox Cloud. +2. Open the user menu (top right) and choose **API Tokens**, then click **Add a token**. + - On NetBox 4.5 and later, create a **v2** token. NetBox shows the full token as `Bearer nbt_.`. v1 tokens still work, but they are deprecated and will be removed in a future NetBox release. + - Leave **Write enabled** unticked. The plugin only reads data. + - If you set **Allowed IPs**, include the addresses SquaredUp connects from, or the on-premises agent's address. +3. Copy the values immediately. NetBox shows the secret only once. +4. In SquaredUp, enter the address in **NetBox URL**, choose the **Token version**, and paste the token into **Token** exactly as NetBox shows it. Including the leading `Bearer` is fine. + +The token inherits the permissions of the user that owns it. Objects the user cannot view are not imported. + +## Configuration fields + +| Field | What it is | Where to find it | Required | +| ----- | ---------- | ---------------- | -------- | +| **NetBox URL** | The address of the NetBox instance. Do not include `/api`. If you do, it is removed automatically. | The address bar when NetBox is open in a browser. | Yes | +| **Token version** | The NetBox API token format. **v2** sends `Authorization: Bearer nbt_.`. **v1** sends `Authorization: Token `. | v2 tokens require NetBox 4.5 or later. | Yes | +| **Token** | The API token. For v2, paste the full value NetBox shows (`Bearer nbt_.`). The `Bearer` prefix is optional. For v1, paste the 40-character token. | NetBox → user menu → **API Tokens**. | Yes | +| **Key** | The public key of a v2 token. Only needed if **Token** contains just the secret, without the `nbt_.` prefix. Shown only when **Token version** is v2. | NetBox → user menu → **API Tokens**. | No | +| **Ignore certificate errors** | Skips TLS certificate validation. Only tick this for self-managed instances that use a self-signed or private CA certificate (common on new NetBox Enterprise installs). | n/a | No | + +When you save, the plugin calls the NetBox status endpoint. A failure means the address is unreachable, the token is invalid, expired or disabled, or the token's **Allowed IPs** blocks SquaredUp. + +## What this plugin monitors + + + +## Data streams + + + +## What gets indexed + + + +## Known limitations + + diff --git a/plugins/NetBox/v1/icon.svg b/plugins/NetBox/v1/icon.svg new file mode 100644 index 00000000..0d756a7a --- /dev/null +++ b/plugins/NetBox/v1/icon.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/NetBox/v1/indexDefinitions/default.json b/plugins/NetBox/v1/indexDefinitions/default.json new file mode 100644 index 00000000..2f5fd2b2 --- /dev/null +++ b/plugins/NetBox/v1/indexDefinitions/default.json @@ -0,0 +1,265 @@ +{ + "steps": [ + { + "name": "sites", + "dataStream": { + "name": "sites" + }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "display", + "type": { + "value": "Site" + }, + "properties": [ + { + "status": "status.value" + }, + { + "region": "region.name" + }, + { + "siteGroup": "group.name" + }, + { + "tenant": "tenant.name" + }, + { + "facility": "facility" + }, + { + "timeZone": "time_zone" + }, + { + "netboxUrl": "display_url" + } + ] + } + }, + { + "name": "racks", + "dataStream": { + "name": "racks" + }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "display", + "type": { + "value": "Rack" + }, + "properties": [ + { + "status": "status.value" + }, + { + "siteId": "site.url" + }, + { + "site": "site.name" + }, + { + "location": "location.name" + }, + { + "role": "role.name" + }, + { + "tenant": "tenant.name" + }, + { + "uHeight": "u_height" + }, + { + "netboxUrl": "display_url" + } + ] + } + }, + { + "name": "devices", + "dataStream": { + "name": "devices" + }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "display", + "type": { + "value": "Device" + }, + "properties": [ + { + "status": "status.value" + }, + { + "role": "role.name" + }, + { + "manufacturer": "device_type.manufacturer.name" + }, + { + "deviceType": "device_type.model" + }, + { + "platform": "platform.name" + }, + { + "siteId": "site.url" + }, + { + "site": "site.name" + }, + { + "rackId": "rack.url" + }, + { + "rack": "rack.name" + }, + { + "serial": "serial" + }, + { + "assetTag": "asset_tag" + }, + { + "tenant": "tenant.name" + }, + { + "primaryIp": "primary_ip.display" + }, + { + "cluster": "cluster.name" + }, + { + "netboxUrl": "display_url" + } + ] + } + }, + { + "name": "virtualMachines", + "dataStream": { + "name": "virtualMachines" + }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "display", + "type": { + "value": "Virtual Machine" + }, + "properties": [ + { + "status": "status.value" + }, + { + "cluster": "cluster.name" + }, + { + "siteId": "site.url" + }, + { + "site": "site.name" + }, + { + "role": "role.name" + }, + { + "platform": "platform.name" + }, + { + "tenant": "tenant.name" + }, + { + "primaryIp": "primary_ip.display" + }, + { + "netboxUrl": "display_url" + } + ] + } + }, + { + "name": "prefixes", + "dataStream": { + "name": "prefixes" + }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "prefix", + "type": { + "value": "Prefix" + }, + "properties": [ + { + "vrfId": "vrf.id" + }, + { + "vrf": "vrf.name" + }, + { + "status": "status.value" + }, + { + "role": "role.name" + }, + { + "tenant": "tenant.name" + }, + { + "scope": "scope.name" + }, + { + "isPool": "is_pool" + }, + { + "markUtilized": "mark_utilized" + }, + { + "netboxUrl": "display_url" + } + ] + } + }, + { + "name": "circuits", + "dataStream": { + "name": "circuits" + }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "cid", + "type": { + "value": "Circuit" + }, + "properties": [ + { + "provider": "provider.name" + }, + { + "circuitType": "type.name" + }, + { + "status": "status.value" + }, + { + "tenant": "tenant.name" + }, + { + "commitRate": "commit_rate" + }, + { + "terminationDate": "termination_date" + }, + { + "netboxUrl": "display_url" + } + ] + } + } + ] +} diff --git a/plugins/NetBox/v1/metadata.json b/plugins/NetBox/v1/metadata.json new file mode 100644 index 00000000..9521f2ba --- /dev/null +++ b/plugins/NetBox/v1/metadata.json @@ -0,0 +1,85 @@ +{ + "name": "netbox", + "displayName": "NetBox", + "version": "1.0.0", + "author": { + "name": "@shawn149", + "type": "community" + }, + "description": "Visualize your NetBox inventory, IP address capacity, data quality, circuits and change activity.", + "category": "Network", + "type": "hybrid", + "schemaVersion": "2.1", + "importNotSupported": false, + "restrictedToPlatforms": [], + "keywords": [ + "netbox", + "netbox labs", + "source of truth", + "dcim", + "ipam", + "network", + "inventory", + "data center", + "racks", + "prefixes", + "circuits", + "cmdb" + ], + "objectTypes": [ + "Site", + "Rack", + "Device", + "Virtual Machine", + "Prefix", + "Circuit" + ], + "links": [ + { + "category": "documentation", + "url": "https://github.com/squaredup/plugins/blob/main/plugins/NetBox/v1/docs/README.md", + "label": "Help adding this plugin" + }, + { + "category": "source", + "url": "https://github.com/squaredup/plugins/tree/main/plugins/NetBox/v1", + "label": "Repository" + } + ], + "base": { + "plugin": "WebAPI", + "majorVersion": "1", + "config": { + "baseUrl": "{{ netboxUrl.trim().replace(/[/]+$/, '').replace(/[/]api$/i, '') }}", + "authMode": "none", + "headers": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "queryArgs": [], + "ignoreCertificateErrors": "{{ignoreCertificateErrors}}", + "showScripting": true, + "scriptingVariables": [ + { + "key": "tokenVersion", + "value": "{{tokenVersion}}" + }, + { + "key": "v1Token", + "value": "{{v1Token}}" + }, + { + "key": "v2Token", + "value": "{{v2Token}}" + }, + { + "key": "v2Key", + "value": "{{v2Key}}" + } + ], + "preRequestScript": "preRequest.js" + } + } +} diff --git a/plugins/NetBox/v1/preRequest.js b/plugins/NetBox/v1/preRequest.js new file mode 100644 index 00000000..aa481a28 --- /dev/null +++ b/plugins/NetBox/v1/preRequest.js @@ -0,0 +1,16 @@ +// Builds the NetBox Authorization header from the token fields. +// This lives in a pre-request script, not a base header expression, because newer platform builds +// no longer evaluate JavaScript over secret fields in base headers (the header arrives empty). +const clean = (value) => (typeof value === 'string' && value !== 'undefined' ? value.trim() : ''); + +if (clean(secrets.tokenVersion) === 'v1') { + headers['Authorization'] = 'Token ' + clean(secrets.v1Token).replace(/^token +/i, ''); +} else { + // NetBox displays v2 tokens as "Bearer nbt_."; accept that, the bare "nbt_..." form, + // or just the secret with the key entered separately. + let token = clean(secrets.v2Token).replace(/^bearer +/i, ''); + if (!token.startsWith('nbt_')) { + token = 'nbt_' + clean(secrets.v2Key).replace(/^nbt_/i, '') + '.' + token; + } + headers['Authorization'] = 'Bearer ' + token; +} diff --git a/plugins/NetBox/v1/ui.json b/plugins/NetBox/v1/ui.json new file mode 100644 index 00000000..1cda6ecd --- /dev/null +++ b/plugins/NetBox/v1/ui.json @@ -0,0 +1,72 @@ +[ + { + "type": "text", + "name": "netboxUrl", + "label": "NetBox URL", + "placeholder": "https://netbox.example.com", + "help": "The address used to open NetBox in a browser. For NetBox Cloud this is `https://.cloud.netboxapp.com`.", + "validation": { + "required": true, + "pattern": { + "value": "^https?://[^ ]+$", + "message": "Enter the full address of the NetBox instance, e.g. https://netbox.example.com" + } + } + }, + { + "type": "radio", + "name": "tokenVersion", + "label": "Token version", + "defaultValue": "v2", + "options": [ + { "value": "v2", "label": "v2 (NetBox 4.5 and later)" }, + { "value": "v1", "label": "v1 (legacy)" } + ], + "validation": { "required": true } + }, + { + "type": "fieldGroup", + "visible": { "tokenVersion": "v2" }, + "fields": [ + { + "type": "password", + "name": "v2Token", + "label": "Token", + "placeholder": "Bearer nbt_xxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "help": "Paste the value exactly as NetBox shows it, with or without the leading `Bearer`.", + "validation": { "required": true } + }, + { + "type": "text", + "name": "v2Key", + "label": "Key", + "placeholder": "Enter the NetBox token key", + "help": "Only needed if **Token** contains just the secret, without the `nbt_.` prefix." + } + ] + }, + { + "type": "fieldGroup", + "visible": { "tokenVersion": "v1" }, + "fields": [ + { + "type": "markdown", + "name": "v1Deprecation", + "content": "⚠️ v1 API tokens are deprecated and will be removed in a future NetBox release. Please use v2 tokens instead." + }, + { + "type": "password", + "name": "v1Token", + "label": "Token", + "placeholder": "Enter the 40-character NetBox token", + "validation": { "required": true } + } + ] + }, + { + "type": "checkbox", + "name": "ignoreCertificateErrors", + "label": "Ignore certificate errors", + "help": "Enable when connecting to a self-managed NetBox instance that uses a self-signed or private CA certificate." + } +] From ffaf1373b1874ec7ed7f610189bc859c2dba6e67 Mon Sep 17 00:00:00 2001 From: Shawn Williams Date: Tue, 6 Oct 2026 15:30:08 -0400 Subject: [PATCH 2/7] Document NetBox plugin capabilities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expanded the NetBox plugin docs to describe the supported APIs, monitored inventory and capacity features, built-in dashboards, indexed objects, and known limitations. This brings the setup and overview documentation in line with the current implementation and makes the plugin’s lifecycle, power, rack, and change-log coverage clearer. --- plugins/NetBox/v1/docs/README.md | 87 +++++++++++++++++++++++++++++--- 1 file changed, 80 insertions(+), 7 deletions(-) diff --git a/plugins/NetBox/v1/docs/README.md b/plugins/NetBox/v1/docs/README.md index 71711a86..c9beae83 100644 --- a/plugins/NetBox/v1/docs/README.md +++ b/plugins/NetBox/v1/docs/README.md @@ -1,6 +1,6 @@ -Visualize your [NetBox](https://netboxlabs.com/oss/netbox/) source of truth in SquaredUp: device and site inventory, IP prefix capacity, rack space, data quality gaps, circuits, and the change log. Data comes from the [NetBox REST API](https://netboxlabs.com/docs/netbox/integrations/rest-api/). +Visualize your [NetBox](https://netboxlabs.com/oss/netbox/) source of truth in SquaredUp: device and site inventory, IP prefix capacity, rack space and elevations, power feeds, data quality gaps, circuits, and the change log. Data comes from the [NetBox REST API](https://netboxlabs.com/docs/netbox/integrations/rest-api/) and the [NetBox GraphQL API](https://netboxlabs.com/docs/netbox/integrations/graphql-api/). -The plugin works with **NetBox Community**, **NetBox Enterprise**, and **NetBox Cloud**. All three expose the same REST API, so no edition-specific setup is needed. +The plugin works with **NetBox Community**, **NetBox Enterprise**, and **NetBox Cloud**. All three expose the same APIs, so no edition-specific setup is needed. NetBox records the intended state of your network, not live health, so the dashboards show inventory, capacity and lifecycle rather than up/down status. ## Setup @@ -26,20 +26,93 @@ The token inherits the permissions of the user that owns it. Objects the user ca | **Key** | The public key of a v2 token. Only needed if **Token** contains just the secret, without the `nbt_.` prefix. Shown only when **Token version** is v2. | NetBox → user menu → **API Tokens**. | No | | **Ignore certificate errors** | Skips TLS certificate validation. Only tick this for self-managed instances that use a self-signed or private CA certificate (common on new NetBox Enterprise installs). | n/a | No | -When you save, the plugin calls the NetBox status endpoint. A failure means the address is unreachable, the token is invalid, expired or disabled, or the token's **Allowed IPs** blocks SquaredUp. +When you save, the plugin checks two things: +1. It calls the NetBox status endpoint. A failure here means the address is unreachable, the token is invalid, expired or disabled, or the token's **Allowed IPs** blocks SquaredUp. +2. It reads the list of sites. A warning here means the token's user can't view DCIM objects. ## What this plugin monitors - +- **Inventory**: sites, racks, devices, virtual machines, circuits and IP addresses. This covers status, role, manufacturer, model, platform, tenant and location. +- **IP capacity**: prefix utilization, calculated the same way as NetBox. Containers count their child prefixes, pools count their full size, and prefixes marked as utilized count as full. VLAN group utilization is also shown. +- **Rack capacity**: + - Rack space used and available per rack and per site, counting both faces. + - A unit-by-unit rack elevation listing for one rack or every rack at a site, including empty units, multi-unit devices and a role filter. +- **Power**: power feeds by site, rack and panel, with voltage, amperage, phase and available power in kVA. +- **Data quality**: + - Devices missing a serial number, primary IP, tenant or platform. + - Devices not updated in over a year. + - Unassigned IP addresses. +- **Lifecycle**: devices and virtual machines that aren't active (planned, staged, offline, decommissioning), and circuit termination dates. +- **Change activity**: the NetBox change log by user, action and object type over time. +- **Custom fields**: every custom field on sites, racks, devices, virtual machines, prefixes, circuits, IP addresses and power feeds appears as an extra column. + +**Dashboards.** The out-of-the-box dashboards are **Overview**, **IPAM Capacity**, **Rack Capacity**, **Data Quality**, **Lifecycle and Circuits**, **Change Activity**, **Data Centers** (including a map of sites), **Power**, and **Device Inventory**. There is also a perspective dashboard for each **Site**, **Rack**, **Device**, **Virtual Machine**, **Prefix** and **Circuit**. + +**Comparing NetBox with monitoring data.** Device and virtual machine objects carry their name and primary IP address (without the mask). You can use them to compare NetBox's intended inventory with what other SquaredUp data sources actually monitor, for example by joining the **Devices** stream with a monitoring stream on name or IP in a dashboard tile. ## Data streams - +- **Sites**: every site with status, region, site group, tenant, facility, coordinates, object counts and custom fields. Account-wide. +- **Racks**: every rack with site, location, status, role, height and device count. Account-wide, with an optional **Site** filter. +- **Devices**: every device with status, role, manufacturer, model, platform, site, rack and position, serial, tenant, primary IP and data-quality flags. Account-wide, with optional **Site**, **Rack** and **Role(s)** filters. +- **Virtual Machines**: every virtual machine with status, cluster, site, role, platform, vCPUs, memory, disk and primary IP. Account-wide, with an optional **Site** filter. +- **Prefixes**: every IP prefix with VRF, scope, status, role, VLAN and pool settings. Account-wide, with an optional **Site** filter. +- **Prefix Utilization**: used and total addresses and utilization % for each selected prefix. Per prefix. +- **IP Addresses**: IP addresses with VRF, status, role, DNS name and the device or virtual machine they're assigned to. + - Account-wide. + - Optional filters: **Prefix**, **Device**, **Virtual machine**, **Assignment** (assigned or unassigned) and **Status**. +- **VLAN Groups**: VLAN groups with scope, VLAN ID ranges and utilization. Account-wide. +- **Circuits**: circuits with provider, type, status, commit rate, terminations, and install and termination dates. Account-wide. +- **Device Interfaces**: interfaces on the selected devices, with type, enabled state, speed, MTU, VLAN mode, cabling and what each is connected to. Per device. +- **Rack Utilization**: rack units used and available per rack, counting both faces. Account-wide, with optional **Site** and **Rack** filters. +- **Rack Elevation**: one row per rack unit from the top down, with the device, role, type, manufacturer, face and height at that unit, including empty units. Account-wide, with optional **Site**, **Rack** and **Role** filters. +- **Power Feeds**: power feeds with site, rack, panel, status, type, supply, phase, voltage, amperage, maximum utilization and available power in kVA. Account-wide, with an optional **Site** filter. +- **Change Log**: changes recorded in NetBox, with time, user, action and object. + - Account-wide, for timeframes up to 30 days. + - Optional filters: an **Object**, **Action** or **Object type**. +- **Instance Status**: the NetBox version, number of installed plugins and number of background workers. +- **GraphQL Query**: runs a NetBox GraphQL query you write and returns one row per item in the first list in the result, with nested fields flattened into columns. + - Use it for views the other streams don't cover. + - Build and test queries in NetBox's GraphiQL explorer at `https:///graphql/`. + - Add `pagination: { limit: 1000, offset: 0 }` to lists that may be longer than 1,000 items. ## What gets indexed - +| Object type | API source | Represents | +| ----------- | ---------- | ---------- | +| **Site** | `GET /api/dcim/sites/` | A NetBox site (data centre, office or campus). | +| **Rack** | `GET /api/dcim/racks/` | An equipment rack. | +| **Device** | `GET /api/dcim/devices/` | A physical device, such as a switch, router, server or PDU. | +| **Virtual Machine** | `GET /api/virtualization/virtual-machines/` | A virtual machine. | +| **Prefix** | `GET /api/ipam/prefixes/` | An IPv4 or IPv6 prefix, in a VRF or the global table. | +| **Circuit** | `GET /api/circuits/circuits/` | A carrier circuit. | + +Each object is identified by its NetBox ID and keeps key properties such as status, site, role and tenant. Each also keeps a link back to the object in NetBox. + +Imports run every 12 hours by default. Dashboard tiles query NetBox live, but SquaredUp caches results briefly, so use the dashboard's refresh button to see a change straight away. ## Known limitations - +- **Read-only.** The plugin never creates, changes or deletes anything in NetBox. +- **Prefix utilization makes one request per prefix.** + - NetBox's API has no utilization or IP-count field, so each prefix is calculated with its own request. + - Tiles that show many prefixes take longer on large NetBox instances. The IPAM Capacity dashboard keeps this to a single tile. + - IP ranges marked as populated aren't counted, so such prefixes can read lower than in NetBox. + - A container prefix with more than 1,000 child prefixes is under-counted. +- **Large lists.** List streams return every matching object. On very large instances (many thousands of devices or IP addresses), use the Site, Prefix, Status or Assignment filters to keep tiles within SquaredUp's response size limit. +- **GraphQL lists stop at 1,000 items.** NetBox returns at most 1,000 items per GraphQL list, even when a higher limit is requested. + - The plugin's own GraphQL streams (Rack Utilization, Rack Elevation and Power Feeds) page through results automatically. + - Queries in the **GraphQL Query** stream must add `pagination` themselves. +- **Rack space.** + - Utilization counts a unit as used if a device occupies it on either face. + - 0U devices and devices in device bays don't use rack units. Rack Elevation lists them after the units. +- **Power.** Available power is the feed's rated capacity (voltage × amperage × maximum utilization, adjusted for three-phase). NetBox doesn't record live power draw. If you track actual load in a custom field, it appears as a column. +- **Site map.** + - Sites appear on the Data Centers map only if their latitude and longitude are set in NetBox. NetBox doesn't convert addresses into coordinates. + - The map uses SmartViz Geo Map. Until it's available in your SquaredUp region, the tile shows a table of sites instead. +- **Change log.** Available for up to 30 days at a time. On busy instances, filter by object, action or object type. +- **Reconnecting after plugin updates.** If a plugin update changes how SquaredUp connects to NetBox, open the data source and save it again. +- **NetBox version.** + - Built for NetBox 4.x and tested with NetBox 4.7. + - v2 tokens need NetBox 4.5 or later. + - On older 4.x releases, check the Change Log stream and the GraphQL-based streams (Rack Utilization, Rack Elevation, Power Feeds). They depend on the current change log API location and GraphQL filter syntax. From 5996ed4ad6f055ed62325cf39ff306174665a710 Mon Sep 17 00:00:00 2001 From: Shawn Williams Date: Tue, 6 Oct 2026 16:22:19 -0400 Subject: [PATCH 3/7] Clarify NetBox compatibility docs Moved the NetBox edition compatibility and intended-state explanation from the intro into the monitoring section so the setup docs read more clearly and the feature summary sits in the right context. --- plugins/NetBox/v1/docs/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/NetBox/v1/docs/README.md b/plugins/NetBox/v1/docs/README.md index c9beae83..1b52d67e 100644 --- a/plugins/NetBox/v1/docs/README.md +++ b/plugins/NetBox/v1/docs/README.md @@ -1,7 +1,5 @@ Visualize your [NetBox](https://netboxlabs.com/oss/netbox/) source of truth in SquaredUp: device and site inventory, IP prefix capacity, rack space and elevations, power feeds, data quality gaps, circuits, and the change log. Data comes from the [NetBox REST API](https://netboxlabs.com/docs/netbox/integrations/rest-api/) and the [NetBox GraphQL API](https://netboxlabs.com/docs/netbox/integrations/graphql-api/). -The plugin works with **NetBox Community**, **NetBox Enterprise**, and **NetBox Cloud**. All three expose the same APIs, so no edition-specific setup is needed. NetBox records the intended state of your network, not live health, so the dashboards show inventory, capacity and lifecycle rather than up/down status. - ## Setup You need the address of your NetBox instance and an **API token** for a user that can view the objects you want in SquaredUp. @@ -32,6 +30,8 @@ When you save, the plugin checks two things: ## What this plugin monitors +The plugin works with **NetBox Community**, **NetBox Enterprise**, and **NetBox Cloud**. All three expose the same APIs, so no edition-specific setup is needed. NetBox records the intended state of your network, not live health, so the dashboards show inventory, capacity and lifecycle rather than up/down status. + - **Inventory**: sites, racks, devices, virtual machines, circuits and IP addresses. This covers status, role, manufacturer, model, platform, tenant and location. - **IP capacity**: prefix utilization, calculated the same way as NetBox. Containers count their child prefixes, pools count their full size, and prefixes marked as utilized count as full. VLAN group utilization is also shown. - **Rack capacity**: From 01e864834cf8ef5823cfbac14c270b78c2b00cee Mon Sep 17 00:00:00 2001 From: Shawn Williams Date: Tue, 6 Oct 2026 16:24:19 -0400 Subject: [PATCH 4/7] Fix NetBox token header handling Avoids malformed Authorization headers when NetBox token fields are blank or partially configured. The v1 and v2 token logic now only sets the header when a usable token is present, and strips common prefixes more safely for both token formats. --- plugins/NetBox/v1/preRequest.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/plugins/NetBox/v1/preRequest.js b/plugins/NetBox/v1/preRequest.js index aa481a28..aa8c5eab 100644 --- a/plugins/NetBox/v1/preRequest.js +++ b/plugins/NetBox/v1/preRequest.js @@ -3,14 +3,21 @@ // no longer evaluate JavaScript over secret fields in base headers (the header arrives empty). const clean = (value) => (typeof value === 'string' && value !== 'undefined' ? value.trim() : ''); +// The header is only set when the selected token field has a value, so a key on its own never +// produces a malformed "Bearer nbt_." header. if (clean(secrets.tokenVersion) === 'v1') { - headers['Authorization'] = 'Token ' + clean(secrets.v1Token).replace(/^token +/i, ''); + const token = clean(secrets.v1Token).replace(/^token(\s+|$)/i, ''); + if (token) { + headers['Authorization'] = 'Token ' + token; + } } else { // NetBox displays v2 tokens as "Bearer nbt_."; accept that, the bare "nbt_..." form, // or just the secret with the key entered separately. - let token = clean(secrets.v2Token).replace(/^bearer +/i, ''); - if (!token.startsWith('nbt_')) { - token = 'nbt_' + clean(secrets.v2Key).replace(/^nbt_/i, '') + '.' + token; + let token = clean(secrets.v2Token).replace(/^bearer(\s+|$)/i, ''); + if (token) { + if (!token.startsWith('nbt_')) { + token = 'nbt_' + clean(secrets.v2Key).replace(/^nbt_/i, '') + '.' + token; + } + headers['Authorization'] = 'Bearer ' + token; } - headers['Authorization'] = 'Bearer ' + token; } From a39a0d32d6a5f12602ca57b615fb100da92f94e0 Mon Sep 17 00:00:00 2001 From: Shawn Williams Date: Tue, 6 Oct 2026 16:36:40 -0400 Subject: [PATCH 5/7] Guard NetBox v2 token auth Avoid sending malformed NetBox v2 bearer tokens when the key is missing. This change normalizes the key value, rejects bare secrets without a key, and reports a clear error telling users to paste the full token or provide the key. --- plugins/NetBox/v1/preRequest.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/NetBox/v1/preRequest.js b/plugins/NetBox/v1/preRequest.js index aa8c5eab..076296d3 100644 --- a/plugins/NetBox/v1/preRequest.js +++ b/plugins/NetBox/v1/preRequest.js @@ -14,9 +14,15 @@ if (clean(secrets.tokenVersion) === 'v1') { // NetBox displays v2 tokens as "Bearer nbt_."; accept that, the bare "nbt_..." form, // or just the secret with the key entered separately. let token = clean(secrets.v2Token).replace(/^bearer(\s+|$)/i, ''); - if (token) { + const key = clean(secrets.v2Key).replace(/^nbt_/i, ''); + if (token && !token.startsWith('nbt_') && !key) { + // A bare secret can't authenticate without its key; don't send a malformed "nbt_." header. + if (typeof api !== 'undefined' && api.report) { + api.report.error('The v2 token has no key. Paste the full "Bearer nbt_." token, or enter the Key field.'); + } + } else if (token) { if (!token.startsWith('nbt_')) { - token = 'nbt_' + clean(secrets.v2Key).replace(/^nbt_/i, '') + '.' + token; + token = 'nbt_' + key + '.' + token; } headers['Authorization'] = 'Bearer ' + token; } From 9492271cfec9b9c64de91eb63aeb2081296ecd57 Mon Sep 17 00:00:00 2001 From: Shawn Williams Date: Wed, 7 Oct 2026 15:39:29 -0400 Subject: [PATCH 6/7] Organize NetBox default content into folders This change reorganizes NetBox default dashboards by grouping them into Capacity, Inventory, and Perspectives folders. It adds per-folder manifest files and updates the root defaultContent manifest to reference those folders instead of listing each dashboard individually. --- .../{ => Capacity}/dataCenters.dash.json | 0 .../{ => Capacity}/ipamCapacity.dash.json | 0 .../v1/defaultContent/Capacity/manifest.json | 20 +++++++ .../{ => Capacity}/power.dash.json | 0 .../{ => Capacity}/rackCapacity.dash.json | 0 .../{ => Inventory}/changeActivity.dash.json | 0 .../{ => Inventory}/dataQuality.dash.json | 0 .../{ => Inventory}/deviceInventory.dash.json | 0 .../lifecycleAndCircuits.dash.json | 0 .../v1/defaultContent/Inventory/manifest.json | 20 +++++++ .../{ => Perspectives}/circuit.dash.json | 0 .../{ => Perspectives}/device.dash.json | 0 .../defaultContent/Perspectives/manifest.json | 28 ++++++++++ .../{ => Perspectives}/prefix.dash.json | 0 .../{ => Perspectives}/rack.dash.json | 0 .../{ => Perspectives}/site.dash.json | 0 .../virtualMachine.dash.json | 0 .../NetBox/v1/defaultContent/manifest.json | 56 ++----------------- 18 files changed, 74 insertions(+), 50 deletions(-) rename plugins/NetBox/v1/defaultContent/{ => Capacity}/dataCenters.dash.json (100%) rename plugins/NetBox/v1/defaultContent/{ => Capacity}/ipamCapacity.dash.json (100%) create mode 100644 plugins/NetBox/v1/defaultContent/Capacity/manifest.json rename plugins/NetBox/v1/defaultContent/{ => Capacity}/power.dash.json (100%) rename plugins/NetBox/v1/defaultContent/{ => Capacity}/rackCapacity.dash.json (100%) rename plugins/NetBox/v1/defaultContent/{ => Inventory}/changeActivity.dash.json (100%) rename plugins/NetBox/v1/defaultContent/{ => Inventory}/dataQuality.dash.json (100%) rename plugins/NetBox/v1/defaultContent/{ => Inventory}/deviceInventory.dash.json (100%) rename plugins/NetBox/v1/defaultContent/{ => Inventory}/lifecycleAndCircuits.dash.json (100%) create mode 100644 plugins/NetBox/v1/defaultContent/Inventory/manifest.json rename plugins/NetBox/v1/defaultContent/{ => Perspectives}/circuit.dash.json (100%) rename plugins/NetBox/v1/defaultContent/{ => Perspectives}/device.dash.json (100%) create mode 100644 plugins/NetBox/v1/defaultContent/Perspectives/manifest.json rename plugins/NetBox/v1/defaultContent/{ => Perspectives}/prefix.dash.json (100%) rename plugins/NetBox/v1/defaultContent/{ => Perspectives}/rack.dash.json (100%) rename plugins/NetBox/v1/defaultContent/{ => Perspectives}/site.dash.json (100%) rename plugins/NetBox/v1/defaultContent/{ => Perspectives}/virtualMachine.dash.json (100%) diff --git a/plugins/NetBox/v1/defaultContent/dataCenters.dash.json b/plugins/NetBox/v1/defaultContent/Capacity/dataCenters.dash.json similarity index 100% rename from plugins/NetBox/v1/defaultContent/dataCenters.dash.json rename to plugins/NetBox/v1/defaultContent/Capacity/dataCenters.dash.json diff --git a/plugins/NetBox/v1/defaultContent/ipamCapacity.dash.json b/plugins/NetBox/v1/defaultContent/Capacity/ipamCapacity.dash.json similarity index 100% rename from plugins/NetBox/v1/defaultContent/ipamCapacity.dash.json rename to plugins/NetBox/v1/defaultContent/Capacity/ipamCapacity.dash.json diff --git a/plugins/NetBox/v1/defaultContent/Capacity/manifest.json b/plugins/NetBox/v1/defaultContent/Capacity/manifest.json new file mode 100644 index 00000000..9f7b2029 --- /dev/null +++ b/plugins/NetBox/v1/defaultContent/Capacity/manifest.json @@ -0,0 +1,20 @@ +{ + "items": [ + { + "name": "ipamCapacity", + "type": "dashboard" + }, + { + "name": "rackCapacity", + "type": "dashboard" + }, + { + "name": "power", + "type": "dashboard" + }, + { + "name": "dataCenters", + "type": "dashboard" + } + ] +} diff --git a/plugins/NetBox/v1/defaultContent/power.dash.json b/plugins/NetBox/v1/defaultContent/Capacity/power.dash.json similarity index 100% rename from plugins/NetBox/v1/defaultContent/power.dash.json rename to plugins/NetBox/v1/defaultContent/Capacity/power.dash.json diff --git a/plugins/NetBox/v1/defaultContent/rackCapacity.dash.json b/plugins/NetBox/v1/defaultContent/Capacity/rackCapacity.dash.json similarity index 100% rename from plugins/NetBox/v1/defaultContent/rackCapacity.dash.json rename to plugins/NetBox/v1/defaultContent/Capacity/rackCapacity.dash.json diff --git a/plugins/NetBox/v1/defaultContent/changeActivity.dash.json b/plugins/NetBox/v1/defaultContent/Inventory/changeActivity.dash.json similarity index 100% rename from plugins/NetBox/v1/defaultContent/changeActivity.dash.json rename to plugins/NetBox/v1/defaultContent/Inventory/changeActivity.dash.json diff --git a/plugins/NetBox/v1/defaultContent/dataQuality.dash.json b/plugins/NetBox/v1/defaultContent/Inventory/dataQuality.dash.json similarity index 100% rename from plugins/NetBox/v1/defaultContent/dataQuality.dash.json rename to plugins/NetBox/v1/defaultContent/Inventory/dataQuality.dash.json diff --git a/plugins/NetBox/v1/defaultContent/deviceInventory.dash.json b/plugins/NetBox/v1/defaultContent/Inventory/deviceInventory.dash.json similarity index 100% rename from plugins/NetBox/v1/defaultContent/deviceInventory.dash.json rename to plugins/NetBox/v1/defaultContent/Inventory/deviceInventory.dash.json diff --git a/plugins/NetBox/v1/defaultContent/lifecycleAndCircuits.dash.json b/plugins/NetBox/v1/defaultContent/Inventory/lifecycleAndCircuits.dash.json similarity index 100% rename from plugins/NetBox/v1/defaultContent/lifecycleAndCircuits.dash.json rename to plugins/NetBox/v1/defaultContent/Inventory/lifecycleAndCircuits.dash.json diff --git a/plugins/NetBox/v1/defaultContent/Inventory/manifest.json b/plugins/NetBox/v1/defaultContent/Inventory/manifest.json new file mode 100644 index 00000000..c3813a5d --- /dev/null +++ b/plugins/NetBox/v1/defaultContent/Inventory/manifest.json @@ -0,0 +1,20 @@ +{ + "items": [ + { + "name": "deviceInventory", + "type": "dashboard" + }, + { + "name": "dataQuality", + "type": "dashboard" + }, + { + "name": "lifecycleAndCircuits", + "type": "dashboard" + }, + { + "name": "changeActivity", + "type": "dashboard" + } + ] +} diff --git a/plugins/NetBox/v1/defaultContent/circuit.dash.json b/plugins/NetBox/v1/defaultContent/Perspectives/circuit.dash.json similarity index 100% rename from plugins/NetBox/v1/defaultContent/circuit.dash.json rename to plugins/NetBox/v1/defaultContent/Perspectives/circuit.dash.json diff --git a/plugins/NetBox/v1/defaultContent/device.dash.json b/plugins/NetBox/v1/defaultContent/Perspectives/device.dash.json similarity index 100% rename from plugins/NetBox/v1/defaultContent/device.dash.json rename to plugins/NetBox/v1/defaultContent/Perspectives/device.dash.json diff --git a/plugins/NetBox/v1/defaultContent/Perspectives/manifest.json b/plugins/NetBox/v1/defaultContent/Perspectives/manifest.json new file mode 100644 index 00000000..d31966c6 --- /dev/null +++ b/plugins/NetBox/v1/defaultContent/Perspectives/manifest.json @@ -0,0 +1,28 @@ +{ + "items": [ + { + "name": "site", + "type": "dashboard" + }, + { + "name": "rack", + "type": "dashboard" + }, + { + "name": "device", + "type": "dashboard" + }, + { + "name": "virtualMachine", + "type": "dashboard" + }, + { + "name": "prefix", + "type": "dashboard" + }, + { + "name": "circuit", + "type": "dashboard" + } + ] +} diff --git a/plugins/NetBox/v1/defaultContent/prefix.dash.json b/plugins/NetBox/v1/defaultContent/Perspectives/prefix.dash.json similarity index 100% rename from plugins/NetBox/v1/defaultContent/prefix.dash.json rename to plugins/NetBox/v1/defaultContent/Perspectives/prefix.dash.json diff --git a/plugins/NetBox/v1/defaultContent/rack.dash.json b/plugins/NetBox/v1/defaultContent/Perspectives/rack.dash.json similarity index 100% rename from plugins/NetBox/v1/defaultContent/rack.dash.json rename to plugins/NetBox/v1/defaultContent/Perspectives/rack.dash.json diff --git a/plugins/NetBox/v1/defaultContent/site.dash.json b/plugins/NetBox/v1/defaultContent/Perspectives/site.dash.json similarity index 100% rename from plugins/NetBox/v1/defaultContent/site.dash.json rename to plugins/NetBox/v1/defaultContent/Perspectives/site.dash.json diff --git a/plugins/NetBox/v1/defaultContent/virtualMachine.dash.json b/plugins/NetBox/v1/defaultContent/Perspectives/virtualMachine.dash.json similarity index 100% rename from plugins/NetBox/v1/defaultContent/virtualMachine.dash.json rename to plugins/NetBox/v1/defaultContent/Perspectives/virtualMachine.dash.json diff --git a/plugins/NetBox/v1/defaultContent/manifest.json b/plugins/NetBox/v1/defaultContent/manifest.json index 13343d93..d44e1625 100644 --- a/plugins/NetBox/v1/defaultContent/manifest.json +++ b/plugins/NetBox/v1/defaultContent/manifest.json @@ -5,60 +5,16 @@ "type": "dashboard" }, { - "name": "ipamCapacity", - "type": "dashboard" - }, - { - "name": "rackCapacity", - "type": "dashboard" - }, - { - "name": "dataQuality", - "type": "dashboard" - }, - { - "name": "lifecycleAndCircuits", - "type": "dashboard" - }, - { - "name": "changeActivity", - "type": "dashboard" - }, - { - "name": "dataCenters", - "type": "dashboard" - }, - { - "name": "power", - "type": "dashboard" - }, - { - "name": "deviceInventory", - "type": "dashboard" - }, - { - "name": "site", - "type": "dashboard" - }, - { - "name": "rack", - "type": "dashboard" + "name": "Capacity", + "type": "folder" }, { - "name": "device", - "type": "dashboard" - }, - { - "name": "virtualMachine", - "type": "dashboard" + "name": "Inventory", + "type": "folder" }, { - "name": "prefix", - "type": "dashboard" - }, - { - "name": "circuit", - "type": "dashboard" + "name": "Perspectives", + "type": "folder" } ] } From a0e7bf8a56302e5db6771e7f57d699e3c9143723 Mon Sep 17 00:00:00 2001 From: Shawn Williams Date: Thu, 8 Oct 2026 10:48:31 -0400 Subject: [PATCH 7/7] Move NetBox auth into metadata; delete preRequest Construct the NetBox Authorization header inline in plugins/NetBox/v1/metadata.json and remove the separate preRequest.js script. Also removed the scriptingVariables array and the preRequestScript reference. Accept header and ignoreCertificateErrors remain unchanged. This consolidates token handling into the metadata header expression and deletes the now-unused preRequest.js. See also these Jira's: `SAAS-10172` and `SAAS-10173` --- plugins/NetBox/v1/metadata.json | 26 +++++--------------------- plugins/NetBox/v1/preRequest.js | 29 ----------------------------- 2 files changed, 5 insertions(+), 50 deletions(-) delete mode 100644 plugins/NetBox/v1/preRequest.js diff --git a/plugins/NetBox/v1/metadata.json b/plugins/NetBox/v1/metadata.json index 9521f2ba..921888b8 100644 --- a/plugins/NetBox/v1/metadata.json +++ b/plugins/NetBox/v1/metadata.json @@ -53,33 +53,17 @@ "baseUrl": "{{ netboxUrl.trim().replace(/[/]+$/, '').replace(/[/]api$/i, '') }}", "authMode": "none", "headers": [ + { + "key": "Authorization", + "value": "{{ (typeof tokenVersion === 'string' && tokenVersion.trim() === 'v1') ? ((typeof v1Token === 'string' ? v1Token.trim().replace(/^token( +|$)/i, '') : '') ? 'Token ' + (typeof v1Token === 'string' ? v1Token.trim().replace(/^token( +|$)/i, '') : '') : null) : ((typeof v2Token === 'string' ? v2Token.trim().replace(/^bearer( +|$)/i, '') : '') ? ((typeof v2Token === 'string' ? v2Token.trim().replace(/^bearer( +|$)/i, '') : '').indexOf('nbt_') === 0 ? 'Bearer ' + (typeof v2Token === 'string' ? v2Token.trim().replace(/^bearer( +|$)/i, '') : '') : ((typeof v2Key === 'string' ? v2Key.trim().replace(/^nbt_/i, '') : '') ? 'Bearer nbt_' + (typeof v2Key === 'string' ? v2Key.trim().replace(/^nbt_/i, '') : '') + '.' + (typeof v2Token === 'string' ? v2Token.trim().replace(/^bearer( +|$)/i, '') : '') : null)) : null) }}" + }, { "key": "Accept", "value": "application/json" } ], "queryArgs": [], - "ignoreCertificateErrors": "{{ignoreCertificateErrors}}", - "showScripting": true, - "scriptingVariables": [ - { - "key": "tokenVersion", - "value": "{{tokenVersion}}" - }, - { - "key": "v1Token", - "value": "{{v1Token}}" - }, - { - "key": "v2Token", - "value": "{{v2Token}}" - }, - { - "key": "v2Key", - "value": "{{v2Key}}" - } - ], - "preRequestScript": "preRequest.js" + "ignoreCertificateErrors": "{{ignoreCertificateErrors}}" } } } diff --git a/plugins/NetBox/v1/preRequest.js b/plugins/NetBox/v1/preRequest.js deleted file mode 100644 index 076296d3..00000000 --- a/plugins/NetBox/v1/preRequest.js +++ /dev/null @@ -1,29 +0,0 @@ -// Builds the NetBox Authorization header from the token fields. -// This lives in a pre-request script, not a base header expression, because newer platform builds -// no longer evaluate JavaScript over secret fields in base headers (the header arrives empty). -const clean = (value) => (typeof value === 'string' && value !== 'undefined' ? value.trim() : ''); - -// The header is only set when the selected token field has a value, so a key on its own never -// produces a malformed "Bearer nbt_." header. -if (clean(secrets.tokenVersion) === 'v1') { - const token = clean(secrets.v1Token).replace(/^token(\s+|$)/i, ''); - if (token) { - headers['Authorization'] = 'Token ' + token; - } -} else { - // NetBox displays v2 tokens as "Bearer nbt_."; accept that, the bare "nbt_..." form, - // or just the secret with the key entered separately. - let token = clean(secrets.v2Token).replace(/^bearer(\s+|$)/i, ''); - const key = clean(secrets.v2Key).replace(/^nbt_/i, ''); - if (token && !token.startsWith('nbt_') && !key) { - // A bare secret can't authenticate without its key; don't send a malformed "nbt_." header. - if (typeof api !== 'undefined' && api.report) { - api.report.error('The v2 token has no key. Paste the full "Bearer nbt_." token, or enter the Key field.'); - } - } else if (token) { - if (!token.startsWith('nbt_')) { - token = 'nbt_' + key + '.' + token; - } - headers['Authorization'] = 'Bearer ' + token; - } -}