Compare commits

..
12 Commits
Author SHA1 Message Date
mattieserver 79a234fcc9 v4.3.0 (#642) 2025-05-28 16:19:42 +02:00
larsen0815 db76752a82 Clearly specify file locations (#638) 2025-05-15 11:47:29 +02:00
Mario 47f263f5c2 Update version table in README.md (#637) 2025-05-15 09:21:27 +02:00
larsen0815 6eb20004f9 Improve installation instructions (#635) 2025-05-15 09:16:37 +02:00
Mario 836d9f40d7 Fix group boxes too small for multiline labels (#632) 2025-05-11 11:43:11 +02:00
Mario 607347ff18 Fix unnecessary field User id (#631) 2025-05-09 17:55:50 +02:00
Chris Russell 0f6051731a Fixes #627 - update template_content to models tuple (#628) 2025-05-05 12:59:03 +02:00
Kenneth Hardy 3c9aebfd2f Update README.md (#625)
Bumping version. Works on my machine.
2025-04-30 08:02:01 +02:00
Maurice Kok 60a5fe1eae fix for issue 593 (#626) 2025-04-30 08:01:13 +02:00
mattieserver 3fbeb97810 bump version (#619) 2025-03-19 22:26:39 +01:00
Tobias Genannt f3b822e18d Use new version identifier from Netbox (#608) 2025-03-19 22:08:21 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 555c1fac4c Bump esbuild and esbuild-sass-plugin (#610)
Bumps [esbuild](https://github.com/evanw/esbuild) and [esbuild-sass-plugin](https://github.com/glromeo/esbuild-sass-plugin). These dependencies needed to be updated together.

Updates `esbuild` from 0.12.29 to 0.25.0
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG-2021.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.12.29...v0.25.0)

Updates `esbuild-sass-plugin` from 1.8.2 to 3.3.1
- [Release notes](https://github.com/glromeo/esbuild-sass-plugin/releases)
- [Commits](https://github.com/glromeo/esbuild-sass-plugin/commits/v3.3.1)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-type: direct:development
- dependency-name: esbuild-sass-plugin
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-19 22:07:20 +01:00
14 changed files with 1149 additions and 575 deletions
+25 -19
View File
@@ -14,31 +14,15 @@ Options to export to xml (for draw.io/diagrams.net) or png.
## Install
**_NOTE:_** For docker please see: [Docker install](https://github.com/netbox-community/netbox-docker/wiki/Using-Netbox-Plugins)
**_NOTE:_** For Docker please see: [Docker install](https://github.com/netbox-community/netbox-docker/wiki/Using-Netbox-Plugins)
**_NOTE:_** Add `RUN mkdir -p /opt/netbox/netbox/static/netbox_topology_views/img` to the Dockerfile-Plugins file to create the image folder
**_NOTE:_** Add `RUN mkdir -p /opt/netbox/netbox/static/netbox_topology_views/img` to the Dockerfile-Plugins file to create the image folder.
The plugin is available as a Python package and can be installed with pip.
Run `pip install netbox-topology-views` in your virtual env.
To ensure NetBox Topology Views plugin is automatically re-installed during future upgrades, create a file named `local_requirements.txt` (if not already existing) in the NetBox root directory (alongside `requirements.txt`) and list the `netbox-topology-views` package:
```no-highlight
# echo netbox-topology-views >> local_requirements.txt
```
Once installed, the plugin needs to be enabled in your `configuration.py`
```python
# In your configuration.py
PLUGINS = ["netbox_topology_views"]
```
First run `source /opt/netbox/venv/bin/activate` to enter the Python virtual environment.
Then run
Then run:
```bash
cd /opt/netbox/netbox
pip3 install netbox-topology-views
@@ -46,6 +30,26 @@ python3 manage.py migrate netbox_topology_views
python3 manage.py collectstatic --no-input
```
To ensure NetBox Topology Views plugin is automatically re-installed during future upgrades, create a file named `local_requirements.txt` (if not already existing) in the NetBox root directory (alongside `requirements.txt`) and list the `netbox-topology-views` package:
```no-highlight
echo netbox-topology-views >> /opt/netbox/local_requirements.txt
```
Once installed, the plugin needs to be enabled in your `configuration.py`:
```no-highlight
vim /opt/netbox/netbox/netbox/configuration.py
```
```python
PLUGINS = ["netbox_topology_views"]
```
Finally, restart NetBox:
```no-highlight
systemctl restart netbox
```
### Versions
> [!NOTE]
@@ -53,6 +57,8 @@ python3 manage.py collectstatic --no-input
| netbox version | netbox-topology-views version |
| -------------- | ----------------------------- |
| = 4.3.X | = v4.3.0 |
| = 4.2.1 | = v4.2.1 |
| = 4.2.0 | = v4.2.0 |
| = 4.1.X | = v4.1.0 |
| = 4.0.X | = v4.0.0 |
+3
View File
@@ -1,6 +1,9 @@
version: 0.1
package_name: netbox-topology-views
compatibility:
- release: 4.3.0
netbox_min: 4.3.0
netbox_max: 4.3.1
- release: 4.2.0
netbox_min: 4.2.0
netbox_max: 4.2.1
+1 -1
View File
@@ -5,7 +5,7 @@ class TopologyViewsConfig(PluginConfig):
name = "netbox_topology_views"
verbose_name = "Topology views"
description = "An plugin to render topology maps"
version = "4.2.0"
version = "4.3.0"
author = "Mattijs Vanhaverbeke"
author_email = "author@example.com"
base_url = "netbox_topology_views"
-1
View File
@@ -516,7 +516,6 @@ class IndividualOptionsForm(NetBoxModelForm):
fieldsets = (
FieldSet
(
'user_id',
'ignore_cable_type',
'preselected_device_roles',
'preselected_tags',
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
(()=>{var l=(e,t,o)=>new Promise((n,r)=>{var c=s=>{try{i(o.next(s))}catch(a){r(a)}},m=s=>{try{i(o.throw(s))}catch(a){r(a)}},i=s=>s.done?n(s.value):Promise.resolve(s.value).then(c,m);i((o=o.apply(e,t)).next())});var d=e=>{if(!document.cookie)return;let t=null,o=document.cookie.split(";");for(let n=0;n<o.length;n++){let r=o[n].trim();if(r.substring(0,e.length+1)===e+"="){t=decodeURIComponent(r.substring(e.length+1));break}}return t};var u={success:e=>{let t=document.querySelector("#topology-plugin-success-toast");if(!t)return console.error("Could not find toast component!");let o=t.querySelector("span");o.textContent=e,new window.Toast(t).show()},error:e=>{let t=document.querySelector("#topology-plugin-error-toast");if(!t)return console.error("Could not find toast component!");let o=t.querySelector("span");o.textContent=e,new window.Toast(t).show()}};var g={},p=d("csrftoken");document.querySelector("form#images").addEventListener("submit",e=>l(void 0,null,function*(){e.preventDefault();try{let t=yield fetch("/"+basePath+"api/plugins/netbox_topology_views/images/save/",{method:"POST",body:JSON.stringify(g),headers:{"X-CSRFToken":p,"Content-Type":"application/json"}});if(!t.ok)throw new Error(yield t.text());u.success("Saved settings")}catch(t){console.dir(t),u.error(t.message)}}));document.querySelectorAll("form#images .dropdown-menu img").forEach(e=>{e.addEventListener("click",t=>{var c;if(!(t.currentTarget instanceof HTMLElement))return;let{dataset:{role:o,image:n}}=t.currentTarget;g[o]=n;let r=(c=t.currentTarget.closest(".dropdown"))==null?void 0:c.querySelector(`#dropdownMenuButton${o}`);r&&(r.innerHTML=`<img src="${n}" />`)})});})();
(()=>{var l=(o,t)=>()=>(o&&(t=o(o=0)),t);var k=(o,t)=>()=>(t||o((t={exports:{}}).exports,t),t.exports);var d=(o,t,e)=>new Promise((n,r)=>{var c=s=>{try{i(e.next(s))}catch(a){r(a)}},y=s=>{try{i(e.throw(s))}catch(a){r(a)}},i=s=>s.done?n(s.value):Promise.resolve(s.value).then(c,y);i((e=e.apply(o,t)).next())});var g,m=l(()=>{g=o=>{if(!document.cookie)return;let t=null,e=document.cookie.split(";");for(let n=0;n<e.length;n++){let r=e[n].trim();if(r.substring(0,o.length+1)===o+"="){t=decodeURIComponent(r.substring(o.length+1));break}}return t}});var u,p=l(()=>{u={success:o=>{let t=document.querySelector("#topology-plugin-success-toast");if(!t)return console.error("Could not find toast component!");let e=t.querySelector("span");e.textContent=o,new window.Toast(t).show()},error:o=>{let t=document.querySelector("#topology-plugin-error-toast");if(!t)return console.error("Could not find toast component!");let e=t.querySelector("span");e.textContent=o,new window.Toast(t).show()}}});var S=k(w=>{m();p();var f={},h=g("csrftoken");document.querySelector("form#images").addEventListener("submit",o=>d(null,null,function*(){o.preventDefault();try{let t=yield fetch("/"+basePath+"api/plugins/netbox_topology_views/images/save/",{method:"POST",body:JSON.stringify(f),headers:{"X-CSRFToken":h,"Content-Type":"application/json"}});if(!t.ok)throw new Error(yield t.text());u.success("Saved settings")}catch(t){console.dir(t),u.error(t.message)}}));document.querySelectorAll("form#images .dropdown-menu img").forEach(o=>{o.addEventListener("click",t=>{var c;if(!(t.currentTarget instanceof HTMLElement))return;let{dataset:{role:e,image:n}}=t.currentTarget;f[e]=n;let r=(c=t.currentTarget.closest(".dropdown"))==null?void 0:c.querySelector(`#dropdownMenuButton${e}`);r&&(r.innerHTML=`<img src="${n}" />`)})})});S();})();
@@ -1,6 +1,6 @@
{
"version": 3,
"sources": ["../../../static_dev/js/csrftoken.js", "../../../static_dev/js/toast.js", "../../../static_dev/js/images.js"],
"mappings": "mNAAO,GAAM,GAAY,AAAC,GAAS,CAC/B,GAAI,CAAC,SAAS,OAAQ,OAEtB,GAAI,GAAc,KACZ,EAAU,SAAS,OAAO,MAAM,KAEtC,OAAS,GAAI,EAAG,EAAI,EAAQ,OAAQ,IAAK,CACrC,GAAM,GAAS,EAAQ,GAAG,OAE1B,GAAI,EAAO,UAAU,EAAG,EAAK,OAAS,KAAO,EAAO,IAAK,CACrD,EAAc,mBAAmB,EAAO,UAAU,EAAK,OAAS,IAChE,OAIR,MAAO,ICfJ,GAAM,GAAQ,CACjB,QAAS,AAAC,GAAY,CAClB,GAAM,GAAK,SAAS,cAAc,kCAClC,GAAI,CAAC,EAAI,MAAO,SAAQ,MAAM,mCAC9B,GAAM,GAAU,EAAG,cAAc,QACjC,EAAQ,YAAc,EAEtB,AADc,GAAI,QAAO,MAAM,GACzB,QAEV,MAAO,AAAC,GAAY,CAChB,GAAM,GAAK,SAAS,cAAc,gCAClC,GAAI,CAAC,EAAI,MAAO,SAAQ,MAAM,mCAC9B,GAAM,GAAU,EAAG,cAAc,QACjC,EAAQ,YAAc,EAEtB,AADc,GAAI,QAAO,MAAM,GACzB,SCZd,GAAM,GAAU,GACV,EAAY,EAAU,aAE5B,SAAS,cAAc,eAAe,iBAAiB,SAAU,AAAO,GAAM,0BAC1E,EAAE,iBACF,GAAI,CACA,GAAM,GAAM,KAAM,OAAM,IAAM,SAAW,iDAAkD,CACvF,OAAQ,OACR,KAAM,KAAK,UAAU,GACrB,QAAS,CACL,cAAe,EACf,eAAgB,sBAIxB,GAAI,CAAC,EAAI,GAAI,KAAM,IAAI,OAAM,KAAM,GAAI,QACvC,EAAM,QAAQ,wBACT,EAAP,CACE,QAAQ,IAAI,GACZ,EAAM,MAAM,EAAI,aAIxB,SAAS,iBAAiB,kCAAkC,QAAQ,AAAC,GAAO,CACxE,EAAG,iBAAiB,QAAS,AAAC,GAAM,CA3BxC,MA4BQ,GAAI,CAAE,GAAE,wBAAyB,cAAc,OAC/C,GAAM,CACF,QAAS,CAAE,OAAM,UACjB,EAAE,cAEN,EAAQ,GAAQ,EAEhB,GAAM,GAAS,KAAE,cACZ,QAAQ,eADE,cAET,cAAc,sBAAsB,KAC1C,AAAI,GAAQ,GAAO,UAAY,aAAa",
"names": []
"mappings": "oTAAA,IAAaA,EAAbC,EAAAC,EAAA,KAAaF,EAAaG,GAAS,CAC/B,GAAI,CAAC,SAAS,OAAQ,OAEtB,IAAIC,EAAc,KACZC,EAAU,SAAS,OAAO,MAAM,GAAG,EAEzC,QAASC,EAAI,EAAGA,EAAID,EAAQ,OAAQC,IAAK,CACrC,IAAMC,EAASF,EAAQC,CAAC,EAAE,KAAK,EAE/B,GAAIC,EAAO,UAAU,EAAGJ,EAAK,OAAS,CAAC,IAAMA,EAAO,IAAK,CACrDC,EAAc,mBAAmBG,EAAO,UAAUJ,EAAK,OAAS,CAAC,CAAC,EAClE,KACJ,CACJ,CAEA,OAAOC,CACX,IChBA,IAAaI,EAAbC,EAAAC,EAAA,KAAaF,EAAQ,CACjB,QAAUG,GAAY,CAClB,IAAMC,EAAK,SAAS,cAAc,gCAAgC,EAClE,GAAI,CAACA,EAAI,OAAO,QAAQ,MAAM,iCAAiC,EAC/D,IAAMC,EAAUD,EAAG,cAAc,MAAM,EACvCC,EAAQ,YAAcF,EACR,IAAI,OAAO,MAAMC,CAAE,EAC3B,KAAK,CACf,EACA,MAAQD,GAAY,CAChB,IAAMC,EAAK,SAAS,cAAc,8BAA8B,EAChE,GAAI,CAACA,EAAI,OAAO,QAAQ,MAAM,iCAAiC,EAC/D,IAAMC,EAAUD,EAAG,cAAc,MAAM,EACvCC,EAAQ,YAAcF,EACR,IAAI,OAAO,MAAMC,CAAE,EAC3B,KAAK,CACf,CACJ,ICjBA,IAAAE,EAAAC,EAAAC,GAAA,CAAAC,IACAC,IAEA,IAAMC,EAAU,CAAC,EACXC,EAAYC,EAAU,WAAW,EAEvC,SAAS,cAAc,aAAa,EAAE,iBAAiB,SAAiBC,GAAMC,EAAA,sBAC1ED,EAAE,eAAe,EACjB,GAAI,CACA,IAAME,EAAM,MAAM,MAAM,IAAM,SAAW,iDAAkD,CACvF,OAAQ,OACR,KAAM,KAAK,UAAUL,CAAO,EAC5B,QAAS,CACL,cAAeC,EACf,eAAgB,kBACpB,CACJ,CAAC,EAED,GAAI,CAACI,EAAI,GAAI,MAAM,IAAI,MAAM,MAAMA,EAAI,KAAK,CAAC,EAC7CC,EAAM,QAAQ,gBAAgB,CAClC,OAASC,EAAK,CACV,QAAQ,IAAIA,CAAG,EACfD,EAAM,MAAMC,EAAI,OAAO,CAC3B,CACJ,EAAC,EAED,SAAS,iBAAiB,gCAAgC,EAAE,QAASC,GAAO,CACxEA,EAAG,iBAAiB,QAAUL,GAAM,CA3BxC,IAAAM,EA4BQ,GAAI,EAAEN,EAAE,yBAAyB,aAAc,OAC/C,GAAM,CACF,QAAS,CAAE,KAAAO,EAAM,MAAAC,CAAM,CAC3B,EAAIR,EAAE,cAENH,EAAQU,CAAI,EAAIC,EAEhB,IAAMC,GAASH,EAAAN,EAAE,cACZ,QAAQ,WAAW,IADT,YAAAM,EAET,cAAc,sBAAsBC,CAAI,IAC1CE,IAAQA,EAAO,UAAY,aAAaD,CAAK,OACrD,CAAC,CACL,CAAC",
"names": ["getCookie", "init_csrftoken", "__esmMin", "name", "cookieValue", "cookies", "i", "cookie", "toast", "init_toast", "__esmMin", "message", "el", "content", "require_images", "__commonJSMin", "exports", "init_csrftoken", "init_toast", "mapping", "csrftoken", "getCookie", "e", "__async", "res", "toast", "err", "el", "_a", "role", "image", "button"]
}
+14 -6
View File
@@ -333,7 +333,7 @@ const coordSaveCheckbox = document.querySelector('#id_save_coords')
// Extract node ids and node type ids from all nodes
for (let [key, value] of nodes._data) {
if (value[typeId] != undefined) {
nodesArray.push([value.id, value[typeId], value[type]]);
nodesArray.push([value.id, value[typeId], value[type], value.label_num_lines]);
}
}
// Split single array above into arrays grouped by node id
@@ -390,16 +390,24 @@ const coordSaveCheckbox = document.querySelector('#id_save_coords')
const rectangles = [];
const xValues = [];
const yValues = [];
const yValuesWithLabel = [];
const labelLineHeight = 16;
for(let val of value[1]) {
// Get coordinates for every node in a given group
for(let val of value[1]) {
xValues.push(graph.getPosition(val[0]).x);
yValues.push(graph.getPosition(val[0]).y);
yValuesWithLabel.push(yValues[yValues.length -1] + val[3] * labelLineHeight);
}
const rectX = Math.min(...xValues) - rectParams.paddingX;
const rectY = Math.min(...yValues) - rectParams.paddingY;
const rectSizeX = Math.max(...xValues) - Math.min(...xValues) + 2*rectParams.paddingX;
const rectSizeY = Math.max(...yValues) - Math.min(...yValues) + 2*rectParams.paddingY;
const minX = Math.min(...xValues);
const maxX = Math.max(...xValues);
const minY = Math.min(...yValues);
const maxY = Math.max(...yValuesWithLabel);
const rectX = minX - rectParams.paddingX;
const rectY = minY - rectParams.paddingY;
const rectSizeX = maxX - minX + 2*rectParams.paddingX;
const rectSizeY = maxY - minY + 2*rectParams.paddingY;
rectangles.push({
ctx: canvascontext,
File diff suppressed because it is too large Load Diff
@@ -1,7 +1,7 @@
{
"private": true,
"name": "netbox_topology_views",
"version": "4.2.0",
"version": "4.3.0",
"scripts": {
"bundle": "node bundle.js",
"bundle:styles": "node bundle.js --styles",
@@ -15,8 +15,8 @@
"devDependencies": {
"@egjs/hammerjs": "^2.0.0",
"component-emitter": "^1.3.0",
"esbuild": "^0.12.24",
"esbuild-sass-plugin": "^1.5.2",
"esbuild": "^0.25.0",
"esbuild-sass-plugin": "^3.3.1",
"keycharm": "^0.4.0",
"timsort": "^0.3.0",
"uuid": "^8.0.0"
+3 -3
View File
@@ -3,16 +3,16 @@ from netbox.plugins import PluginTemplateExtension
from django.conf import settings
from packaging import version
NETBOX_CURRENT_VERSION = version.parse(settings.VERSION)
NETBOX_CURRENT_VERSION = version.parse(settings.RELEASE.version)
class SiteButtons(PluginTemplateExtension):
model = 'dcim.site'
models = ('dcim.site', )
def buttons(self):
return self.render('netbox_topology_views/site_button.html')
class LocationButtons(PluginTemplateExtension):
model = 'dcim.location'
models = ('dcim.location', )
def buttons(self):
return self.render('netbox_topology_views/location_button.html')
+5 -4
View File
@@ -251,6 +251,7 @@ def create_node(
node["shape"] = "image"
node["href"] = device.get_absolute_url()
node["image"] = get_image_for_entity(device)
node["label_num_lines"] = len(label_items)
return node
@@ -443,14 +444,14 @@ def get_topology_data(
termination_b = create_circuit_termination(
circuit_termination.cable.b_terminations[0]
)
elif circuit_termination.provider_network is not None:
elif circuit_termination.termination is not None:
if (
circuit_termination.provider_network_id
circuit_termination.termination_id
not in nodes_provider_networks
):
nodes_provider_networks[
circuit_termination.provider_network.pk
] = circuit_termination.provider_network
circuit_termination.termination.pk
] = circuit_termination.termination
if bool(termination_a) and bool(termination_b):
circuit_model = {
+1 -1
View File
@@ -7,7 +7,7 @@ long_description = readme.read_text()
setup(
name="netbox-topology-views",
version="4.2.0",
version="4.3.0",
description="An NetBox plugin to create Topology maps",
long_description=long_description,
long_description_content_type="text/markdown",