netbox-plugin-network-map-yativilli 1.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. netbox_plugin_network_map_yativilli-1.0.1.dist-info/METADATA +380 -0
  2. netbox_plugin_network_map_yativilli-1.0.1.dist-info/RECORD +76 -0
  3. netbox_plugin_network_map_yativilli-1.0.1.dist-info/WHEEL +5 -0
  4. netbox_plugin_network_map_yativilli-1.0.1.dist-info/licenses/LICENSE +674 -0
  5. netbox_plugin_network_map_yativilli-1.0.1.dist-info/top_level.txt +1 -0
  6. network_map/__init__.py +28 -0
  7. network_map/api/__init__.py +0 -0
  8. network_map/api/urls.py +32 -0
  9. network_map/api/views.py +458 -0
  10. network_map/bulk.py +89 -0
  11. network_map/colors.py +251 -0
  12. network_map/coverage.py +683 -0
  13. network_map/defaults.py +63 -0
  14. network_map/floor_plan.py +667 -0
  15. network_map/geocoding.py +102 -0
  16. network_map/locale/de/LC_MESSAGES/django.mo +0 -0
  17. network_map/locale/fr/LC_MESSAGES/django.mo +0 -0
  18. network_map/lv03.py +148 -0
  19. network_map/map_tiles.py +403 -0
  20. network_map/migrations/0001_initial.py +24 -0
  21. network_map/migrations/__init__.py +0 -0
  22. network_map/models.py +103 -0
  23. network_map/navigation.py +30 -0
  24. network_map/places.py +48 -0
  25. network_map/png_render.py +122 -0
  26. network_map/static/network_map/data_coverage/coverage.css +28 -0
  27. network_map/static/network_map/shared/export_dialog.css +90 -0
  28. network_map/static/network_map/shared/export_dialog.js +192 -0
  29. network_map/static/network_map/shared/svg_export.js +1174 -0
  30. network_map/static/network_map/subnet_map/lv03_grid.js +47 -0
  31. network_map/static/network_map/subnet_map/subnet_map.css +417 -0
  32. network_map/static/network_map/subnet_map/subnet_map.js +1215 -0
  33. network_map/static/network_map/vendor/leaflet/images/layers-2x.png +0 -0
  34. network_map/static/network_map/vendor/leaflet/images/layers.png +0 -0
  35. network_map/static/network_map/vendor/leaflet/images/marker-icon-2x.png +0 -0
  36. network_map/static/network_map/vendor/leaflet/images/marker-icon.png +0 -0
  37. network_map/static/network_map/vendor/leaflet/images/marker-shadow.png +0 -0
  38. network_map/static/network_map/vendor/leaflet/leaflet.css +661 -0
  39. network_map/static/network_map/vendor/leaflet/leaflet.js +5 -0
  40. network_map/static/network_map/vlan_connection/vlan_connections.css +254 -0
  41. network_map/static/network_map/vlan_element_list/vlan_element_list.css +130 -0
  42. network_map/static/network_map/vlan_topology/vlan_topology.css +154 -0
  43. network_map/static/network_map/vlan_topology/vlan_topology.js +487 -0
  44. network_map/svg_render/__init__.py +179 -0
  45. network_map/svg_render/base.py +99 -0
  46. network_map/svg_render/logical_tree.py +124 -0
  47. network_map/svg_render/machine_list.py +160 -0
  48. network_map/svg_render/subnet_map.py +608 -0
  49. network_map/svg_render/topology.py +263 -0
  50. network_map/swisstopo.py +436 -0
  51. network_map/templates/network_map/data_coverage.html +146 -0
  52. network_map/templates/network_map/includes/_export_dialog.html +6 -0
  53. network_map/templates/network_map/includes/_info_item.html +10 -0
  54. network_map/templates/network_map/subnet_map.html +94 -0
  55. network_map/templates/network_map/vlan_connection.html +146 -0
  56. network_map/templates/network_map/vlan_element_list.html +89 -0
  57. network_map/templates/network_map/vlan_topology.html +49 -0
  58. network_map/templatetags/__init__.py +0 -0
  59. network_map/templatetags/network_map_export.py +26 -0
  60. network_map/templatetags/network_map_static.py +31 -0
  61. network_map/tests/__init__.py +1 -0
  62. network_map/tests/_helpers.py +67 -0
  63. network_map/tests/test_api_floor_plan.py +280 -0
  64. network_map/tests/test_api_svg.py +556 -0
  65. network_map/tests/test_bulk.py +220 -0
  66. network_map/tests/test_colors.py +176 -0
  67. network_map/tests/test_coverage.py +279 -0
  68. network_map/tests/test_floor_plan.py +214 -0
  69. network_map/tests/test_geo.py +791 -0
  70. network_map/tests/test_png.py +67 -0
  71. network_map/tests/test_templatetags.py +47 -0
  72. network_map/tests/test_views.py +228 -0
  73. network_map/tests/test_vlan.py +43 -0
  74. network_map/urls.py +18 -0
  75. network_map/views.py +730 -0
  76. network_map/web_mercator.py +64 -0
@@ -0,0 +1,380 @@
1
+ Metadata-Version: 2.4
2
+ Name: netbox-plugin-network-map-yativilli
3
+ Version: 1.0.1
4
+ Summary: A network_map plugin for netbox
5
+ Author-email: Yannick Wernle <yannick@wernle.net>
6
+ License-Expression: GPL-3.0
7
+ Project-URL: Homepage, https://github.com/yativilli/netbox_networkmap_plugin
8
+ Project-URL: Repository, https://github.com/yativilli/netbox_networkmap_plugin
9
+ Requires-Python: >=3.12.0
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Provides-Extra: png
13
+ Requires-Dist: cairosvg>=2.7; extra == "png"
14
+ Dynamic: license-file
15
+
16
+ # NetBox Network Map Plugin
17
+
18
+ Machines on a network map inside NetBox: a machine list, a VLAN connection tree,
19
+ a topology map and a geographic subnet map, each also handed out as a picture.
20
+ Some views are Swiss-centric - the map shows a canton and draws on swisstopo
21
+ tiles.
22
+
23
+ ## Install from a checkout
24
+
25
+ For a development install against the NetBox checkout this plugin lives next to:
26
+
27
+ ```bash
28
+ cd /opt/netbox/network_map_plugin
29
+ /opt/netbox/venv/bin/python -m pip install --editable . --no-deps
30
+ ```
31
+
32
+ The pages live under `/plugins/networkmap/` (`vlan-list/`, `vlan-topology/`,
33
+ `vlan-connections/`, `subnet-map/`, `coverage/`) and are linked from the NetBox
34
+ menu. In production, collect static files before restarting.
35
+
36
+ ## Build
37
+
38
+ ```bash
39
+ cd /opt/netbox/network_map_plugin
40
+ python -m pip install build
41
+ python -m build
42
+ ```
43
+
44
+ The sdist and the wheel land in `dist/`; CI builds the same way and attaches
45
+ both to the GitHub Release of the version in `network_map/__init__.py`, so the
46
+ wheel of any release can be downloaded from there instead of built.
47
+
48
+ ## Deploy on a NetBox instance
49
+
50
+ 1. Get the wheel, from the GitHub Release or from a local `dist/` after
51
+ [building](#build).
52
+ 2. Install it into NetBox's virtualenv - with the `png` extra if the instance
53
+ should answer `?format=png` through cairosvg (ImageMagick on the server
54
+ works instead and needs no extra):
55
+
56
+ ```bash
57
+ /opt/netbox/venv/bin/python -m pip install \
58
+ "./netbox_plugin_network_map_yativilli-<version>-py3-none-any.whl[png]"
59
+ ```
60
+
61
+ 3. Enable the plugin in `netbox/netbox/configuration.py`:
62
+
63
+ ```python
64
+ PLUGINS = ["network_map"]
65
+ ```
66
+
67
+ Options go in the same file under `PLUGINS_CONFIG`, see
68
+ [Configuration](#configuration).
69
+
70
+ 4. Apply the plugin's migrations and collect its static files:
71
+
72
+ ```bash
73
+ /opt/netbox/venv/bin/python /opt/netbox/netbox/manage.py migrate
74
+ /opt/netbox/venv/bin/python /opt/netbox/netbox/manage.py collectstatic --no-input
75
+ ```
76
+
77
+ 5. Restart NetBox (and its RQ workers, if it has them), then open any page
78
+ under `/plugins/networkmap/` from the NetBox menu.
79
+
80
+ ## Testing
81
+
82
+ ```bash
83
+ cd /opt/netbox/netbox
84
+ /opt/netbox/venv/bin/python manage.py test network_map
85
+ ```
86
+
87
+ Where the database role may not create databases, copy
88
+ `netbox/configuration.py` to `netbox/configuration_testing.py`, set
89
+ `DEBUG = False`, add `'TEST': {'NAME': 'netbox'}` to the default database entry,
90
+ and run the same command with `NETBOX_CONFIGURATION=netbox.configuration_testing`
91
+ and `--keepdb`.
92
+
93
+ ## Picture API
94
+
95
+ The kind of picture stands in the path, its format on the query string -
96
+ `?format=svg` (the default) or `?format=png`.
97
+
98
+ | Kind | URL |
99
+ | ------------ | -------------------------------------- |
100
+ | Machine list | `/api/plugins/networkmap/machine-list` |
101
+ | Logical map | `/api/plugins/networkmap/logical-map` |
102
+ | Subnet map | `/api/plugins/networkmap/subnet-map` |
103
+ | Topology map | `/api/plugins/networkmap/topology` |
104
+
105
+ ```bash
106
+ curl -H "Authorization: Token <token>" \
107
+ "https://netbox.example.com/api/plugins/networkmap/topology?format=png" \
108
+ -o topology.png
109
+ ```
110
+
111
+ - Needs the `network_map.view_vlanelement` permission.
112
+ - A PNG needs a rasteriser on the server: `cairosvg` (the plugin's `png` extra)
113
+ or ImageMagick. Without either the request answers 501; a picture too big for
114
+ the rasteriser comes out smaller instead of failing.
115
+ - The subnet map puts every map tile it fetches into the document as a data URL,
116
+ which is what makes it big: the canton of Bern as it stands in this NetBox
117
+ measures about 1.8 MB as SVG and 3.8 MB as PNG, against 0.5 MB and 0.4 MB with
118
+ `?background=0`, which leaves the ground out.
119
+ - Building the subnet map geocodes every Site that has no coordinates yet - one
120
+ Nominatim lookup each, whose result is saved back on the Site - so this GET
121
+ writes to the database.
122
+ - `/api/plugins/networkmap/` links these addresses, and the schema lists them
123
+ under the `network-map` tag.
124
+
125
+ ### Floor plans
126
+
127
+ A floor plan is asked for by its site, because several buildings stand in one
128
+ city and only the site is named uniquely. `/floor-plans/` lists what exists,
129
+ and each plan is then drawn by its own address:
130
+
131
+ | URL | Answers |
132
+ | -------------------------------------------------- | --------------------------------------------- |
133
+ | `/api/plugins/networkmap/floor-plans/` | JSON `{"count", "city", "site", "picture", "subnet-map", "plans"}`; the two filters are echoed back, `picture` links one drawing of every listed plan, `subnet-map` the whole network map those sites sit on, and each plan carries a `city` read off its address |
134
+ | `/api/plugins/networkmap/floor-plans/all/` | every listed plan in one picture, as SVG or PNG (same `city`/`site` filters) |
135
+ | `/api/plugins/networkmap/floor-plan/<site-slug>/` | the site's logical floor map, as SVG or PNG |
136
+
137
+ ```bash
138
+ curl -H "Authorization: Token <token>" \
139
+ "https://netbox.example.com/api/plugins/networkmap/floor-plans/?city=Bern"
140
+ curl -H "Authorization: Token <token>" \
141
+ "https://netbox.example.com/api/plugins/networkmap/floor-plan/sample-site/" \
142
+ -o floor-plan.svg
143
+ ```
144
+
145
+ - The plan of a site is its logical floor map, built from the site's locations
146
+ and named by the site's slug alone; a site whose machines the map places has
147
+ a plan, every other address is answered 404.
148
+ - `?city=` reads the place out of the site name, physical address, shipping
149
+ address or description, which is where NetBox keeps it; `?site=` takes the
150
+ site's slug.
151
+ - The answer echoes the `city` and `site` it filtered by (null when not given),
152
+ and every plan carries its own `city`, read off its address whether the place
153
+ stands before the street (`Bern, Musterweg 5`) or after a postal code
154
+ (`Musterweg 5, 3000 Bern`).
155
+ - Besides the list, `picture` carries the `svg` and `png` addresses of a single
156
+ drawing holding every listed plan one under the other, at `/floor-plans/all/`
157
+ with the same filters applied.
158
+ - `subnet-map` carries the `svg` and `png` addresses of the whole network map,
159
+ the geographic picture those sites stand on, at `/subnet-map/`.
160
+ - The plan carries its machines the way the browser export of the map does:
161
+ every dot is numbered in the machine's colour, virtual ones hollow, and under
162
+ the picture the machines are listed with their name, description and address.
163
+ - The map is built from the site's locations, floor by floor, and carries the
164
+ same colour key as the map page; the `network_map.floor_plan` module and
165
+ `subnet_map.js` hold the same measurements, which `FloorPlanParityTests`
166
+ checks.
167
+
168
+ ## Data Coverage
169
+
170
+ `/plugins/networkmap/coverage/` is a read-only map-readiness report. It checks
171
+ whether the NetBox data behind the existing maps is complete enough to draw
172
+ useful pictures, without geocoding or saving anything while the page is opened.
173
+
174
+ The first report covers:
175
+
176
+ - sites with machines but missing coordinates or an unknown city,
177
+ - VLANs without prefixes and prefixes without active/reserved IP addresses,
178
+ - IP addresses with missing or blank DNS names,
179
+ - machines without a site or without a room for floor plans,
180
+ - locations whose floor cannot be inferred from the room name,
181
+ - gateway-tag problems for the logical connection view.
182
+
183
+ ## Configuration
184
+
185
+ ```python
186
+ PLUGINS_CONFIG = {
187
+ "network_map": {
188
+ "gateway_search_tag": "GATEWAY-TAG",
189
+ "canton_boundary_code": "BE",
190
+ },
191
+ }
192
+ ```
193
+
194
+ - `gateway_search_tag`: the device tag the Vlan-Connections view uses to find the
195
+ central gateway object.
196
+ - `canton_boundary_code`: the canton whose border the Subnet-Map draws, as a
197
+ two-letter code (`"BE"`, which is also the default) or a BFS id; `"CH"` or
198
+ `"SW"` draws the national border in its place, and `""` draws none.
199
+
200
+ <details>
201
+ <summary>Advanced settings</summary>
202
+
203
+ All of these go in the same `PLUGINS_CONFIG` dictionary.
204
+
205
+ Geocoding, for a site that has no coordinates yet: `nominatim_url` (defaults to
206
+ OpenStreetMap's service), `country_codes` (`"ch"`), `request_interval_seconds`
207
+ (`1.0`), `request_timeout_seconds` (`10`).
208
+
209
+ Canton border, tried in that order until one answers - the geometry is fetched
210
+ live and cached, and the map renders without a border when none can be had:
211
+ `canton_boundary_url_template` (swisstopo WFS, `{id}` the BFS id; preferred
212
+ because it carries interior rings, which keeps canton pockets such as Steinhof
213
+ SO, so leave `srsName=EPSG%3A4326` in it),
214
+ `canton_boundary_fallback_url_template` (Nominatim, `{code}` as `CH-BE`, also
215
+ carrying holes), `canton_boundary_last_resort_url_template` (`map.geo.admin.ch`,
216
+ without holes), `canton_boundary_cache_seconds` (30 days) and
217
+ `canton_boundary_label` (legend text, the canton name by default). The geometry
218
+ is also served on its own at `/plugins/networkmap/subnet-map/canton-boundary/`.
219
+
220
+ </details>
221
+
222
+ ### Map background
223
+
224
+ The served subnet map stands by default on the same swisstopo tiles, in the same
225
+ LV03 grid, as the page's own map, which is why the two show the same ground;
226
+ `map_background_source` moves the ground to OpenStreetMap instead. Tiles are
227
+ fetched side by side and cached, so the first caller pays for a canton, and a
228
+ tile that cannot be had simply stays out of the picture.
229
+
230
+ | Setting | Default | Meaning |
231
+ | -------------------------------- | ----------------------- | ----------------------------------------------------- |
232
+ | `map_background` | `True` | Draw tiles behind the served subnet map. |
233
+ | `map_background_source` | `"SWISSTOPO"` | Page and pictures alike: `"SWISSTOPO"` or `"OSM"`. |
234
+ | `map_tile_url_template` | the named source's | Tile address, in the grid of whoever is named. |
235
+ | `map_tile_fallback` | empty | Second source asked for a tile the first lacks. |
236
+ | `map_tile_fallback_url_template` | the second source's | That source's tile address. |
237
+ | `map_tile_zoom` | chosen from the picture | Force one zoom instead of the fitting one. |
238
+ | `map_tile_max` | `64` | Tiles per picture; the zoom comes down to pay for it. |
239
+ | `map_tile_cache_seconds` | 30 days | How long a tile is kept. |
240
+ | `map_tile_budget_seconds` | `20` | What one picture waits for its tiles, all together. |
241
+ | `map_attribution` | what the source asks | Credit under the picture, naming the ground. |
242
+
243
+ `map_background_source` says who the ground comes from, for the page's own map
244
+ and for every picture the server draws. `SWISSTOPO` - the default - numbers its
245
+ tiles in the LV03 grid, so `{z}/{y}/{x}` are zoom, row and column and a mirror
246
+ has to answer in that grid; the page maps on that grid too, through the CRS its
247
+ script builds from `lv03_grid.js`. `OSM` numbers them as the rest of the mapping
248
+ world does, `{z}/{x}/{y}` on the Web Mercator pyramid, and the page falls back to
249
+ Leaflet's own Mercator projection. A served picture is still planned in the
250
+ Swiss grid and each of its patches is handed over to the pyramid step covering
251
+ it, so the same ground shows either way - only coarser where the pyramid's steps
252
+ are wider. A source's own address is a setting, which is how a mirror is asked
253
+ instead; `map_attribution` then names whoever the ground came from, which
254
+ OpenStreetMap asks for and swisstopo does not. A change needs a restart, because
255
+ both the page and its tiles are handed over when the page is built.
256
+
257
+ `map_tile_fallback` names the source asked for a tile the first one cannot give
258
+ - `"OSM"` behind a swisstopo mirror that covers only part of the country, or
259
+ `"SWISSTOPO"` behind OpenStreetMap, whose public server sometimes turns a caller
260
+ away. It is empty by default, because a tile the Swiss server lacks is usually
261
+ ground outside the country, and asking twice for every one of those costs time.
262
+ Naming the source that is already named, or handing out the same address twice,
263
+ asks once.
264
+
265
+ ### Room around the border
266
+
267
+ Two distances decide where a picture ends. The cut runs `map_border_cut` pixels
268
+ beyond the border, so a band of the neighbouring ground stays in the picture
269
+ instead of the picture ending on the line; `0` cuts on the line. And the border
270
+ keeps `map_border_room` pixels between itself and the edge of the picture, because
271
+ an area touches its own bounding box at a point - west of Geneva, south of Ticino
272
+ - which is where a tight frame used to cut the line off. The left and the bottom
273
+ edges add their own room to that.
274
+
275
+ | Setting | Default | Meaning |
276
+ | ----------------- | ------- | --------------------------------------------- |
277
+ | `map_border_cut` | `3` | How far beyond the border the picture is cut. |
278
+ | `map_border_room` | `22` | Room between the border and the edge. |
279
+ | `map_room_left` | `20` | Room the left edge adds to that. |
280
+ | `map_room_bottom` | `20` | Room the bottom edge adds to that. |
281
+
282
+ They are pixels of the picture and go in the same `PLUGINS_CONFIG` dictionary.
283
+ Both pictures obey them, the served one and the one the page's export button
284
+ makes, so the two never disagree about what belongs in them. A restart is needed,
285
+ and a page already open has to be reloaded, because it carries the numbers it was
286
+ served.
287
+
288
+ Should the canton come out as a plain rectangle roughly centred on Basel - Bern
289
+ squeezed into its own bounding box - the map is drawing a stale geometry, not the
290
+ canton. The border is fetched once and then kept in NetBox's cache for
291
+ `canton_boundary_cache_seconds`, so the map shows whatever that cache holds, not
292
+ what the services answer now.
293
+
294
+ A restart clears it only while the cache lives in memory (`LocMemCache`), which is
295
+ the usual development setup. Once NetBox caches in Redis - the usual production
296
+ choice - the entry is persisted to `dump.rdb` and reloaded when Redis starts, so
297
+ restarting NetBox, or even `redis-server`, brings the same rectangle back. Drop
298
+ the cache entry itself, and the next call fetches the true border:
299
+
300
+ ```
301
+ python /opt/netbox/netbox/manage.py shell -c \
302
+ 'from django.core.cache import cache; cache.delete("network_map:canton_boundary:2")'
303
+ ```
304
+
305
+ The key ends in the BFS id of the canton (`2` is Bern, `11` Solothurn, and so on;
306
+ `CH` for the national border). Removing it through the cache backend does the same
307
+ - `redis-cli UNLINK ':1:network_map:canton_boundary:2'` - while `FLUSHDB` on the
308
+ caching database also clears it, but discards every other cached value with it.
309
+
310
+ ## Code structure
311
+
312
+ | File | Purpose |
313
+ | -------------------------------------- | ------------------------------------------------------------------ |
314
+ | `models.py` | The `VlanElement` anchor model and the dataclasses the views fill. |
315
+ | `views.py` | The plugin pages: collecting what to show and handing it to the browser. |
316
+ | `urls.py`, `navigation.py` | The page routes and the NetBox menu entries. |
317
+ | `__init__.py`, `defaults.py` | Plugin registration, version, and the settings with their defaults. |
318
+ | `colors.py` | The location colour palette and the shades drawn from it. |
319
+ | `places.py` | Reading the city/place out of site names and addresses. |
320
+ | `coverage.py` | Read-only map-readiness checks for the Data Coverage page. |
321
+ | `lv03.py` | WGS84 to LV03 conversion and the swisstopo tile-grid maths. |
322
+ | `web_mercator.py` | The Web Mercator pyramid OpenStreetMap numbers its tiles in. |
323
+ | `geocoding.py` | Nominatim lookup for Sites that have no coordinates yet. |
324
+ | `swisstopo.py` | Fetching the canton border, with fallbacks, from public services. |
325
+ | `map_tiles.py` | Fetching and caching the tiles a served map stands on. |
326
+ | `svg_render/` | Package entry re-exporting the server-side SVG renderers. |
327
+ | `svg_render/base.py` | Shared SVG primitives: text helpers, wrapping, and shared styles. |
328
+ | `svg_render/machine_list.py` | Machine-list picture rendering. |
329
+ | `svg_render/logical_tree.py` | Logical VLAN connection tree rendering. |
330
+ | `svg_render/topology.py` | Topology map rendering. |
331
+ | `svg_render/subnet_map.py` | Geographic subnet map rendering, border framing, and tiles. |
332
+ | `floor_plan.py` | Drawing a site's logical floor plan. |
333
+ | `png_render.py` | Rasterising a served SVG to PNG with cairosvg or ImageMagick. |
334
+ | `api/views.py` | The picture and floor-plan endpoints and their permission gate. |
335
+ | `api/urls.py` | The API routes, with the path parts constrained by regex. |
336
+ | `templatetags/network_map_static.py` | The `static_url` tag keeping cached scripts honest. |
337
+ | `templatetags/network_map_export.py` | The `export_dialog_labels` tag behind the format dialog. |
338
+ | `templates/network_map/*.html` | The pages' markup, data included as JSON. |
339
+ | `templates/network_map/includes/` | `_info_item.html` rows and the `_export_dialog.html` dialog. |
340
+ | `static/network_map/shared/` | `svg_export.js` and the `export_dialog.js` format dialog. |
341
+ | `static/network_map/data_coverage/` | Styles for the Data Coverage page. |
342
+ | `static/network_map/subnet_map/` | Subnet-map script and styles, plus the generated LV03 grid. |
343
+ | `static/network_map/vlan_connection/` | Styles for the VLAN connection page. |
344
+ | `static/network_map/vlan_element_list/`| Styles for the machine list page. |
345
+ | `static/network_map/vlan_topology/` | Topology-map script and styles. |
346
+ | `static/network_map/vendor/leaflet/` | The vendored Leaflet library the subnet map runs on. |
347
+ | `migrations/` | Django's record of the model's state. |
348
+ | `locale/<lang>/LC_MESSAGES/` | The German and French catalogs, compiled `.mo` included. |
349
+ | `tests/` | The split test suite, run as described under Testing. |
350
+
351
+ ## Releases
352
+
353
+ `__version__` in `network_map/__init__.py` is the only place the version is
354
+ written; a new NetBox release needs `min_version`/`max_version` widened there, or
355
+ NetBox disables the plugin.
356
+
357
+ Bumping it and merging to `main` releases the plugin: once lint, types and tests
358
+ pass, CI opens the GitHub Release named `v<version>` - the tag must stay `v`
359
+ followed by the version, or the run cannot match the two. Every later run on
360
+ `main` finds the existing release and does nothing, so a release can also be
361
+ re-attempted by hand from the CI workflow's "Run workflow" button.
362
+
363
+ ## Translations
364
+
365
+ Catalogs are `network_map/locale/<lang>/LC_MESSAGES/django.po` (`de`, `fr`); the
366
+ compiled `.mo` is committed because this tree does not compile it. After changing
367
+ strings, fill in the new `msgstr` values and rebuild:
368
+
369
+ ```bash
370
+ cd /opt/netbox/network_map_plugin
371
+ DJANGO_SETTINGS_MODULE=netbox.settings PYTHONPATH=/opt/netbox/netbox \
372
+ /opt/netbox/venv/bin/django-admin makemessages -l de -l fr --keep-pot
373
+ for lang in de fr; do
374
+ msgfmt --check -o network_map/locale/$lang/LC_MESSAGES/django.mo \
375
+ network_map/locale/$lang/LC_MESSAGES/django.po
376
+ done
377
+ ```
378
+
379
+ `--keep-pot` keeps the master catalog `makemessages` would delete after merging;
380
+ `--all` is unusable here because it pulls in NetBox's own language list.
@@ -0,0 +1,76 @@
1
+ netbox_plugin_network_map_yativilli-1.0.1.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
2
+ network_map/__init__.py,sha256=z1xpQAYbHNPPPxSzQC2LVe7GYcZ5IqW96DNUc8u_PYM,826
3
+ network_map/bulk.py,sha256=ek37Y3H_Jx6Sz5zaMuB-caszXBTIDUps0jUPVo3dMg4,3036
4
+ network_map/colors.py,sha256=U9ZWC2zAn1y0P-kof9PGFVK6_SYqKErM18_L2Cihz2s,7359
5
+ network_map/coverage.py,sha256=rXtoyePWbofX-iXQd__zbu9ZpoDMjHVCZjsPwGiS3Wk,23231
6
+ network_map/defaults.py,sha256=8caZJOzo-yPB6JgJNwM1RNjvJ6av7sRn7iEqVreeYtI,2171
7
+ network_map/floor_plan.py,sha256=WlXl7KDARDEtzX8DZCddQJstWYOErQco5aKM3x9f0H8,24616
8
+ network_map/geocoding.py,sha256=TIbDf-Q2Q427NXj_NhT6DWin1To-f1xxLAfSya8U638,3178
9
+ network_map/lv03.py,sha256=m04KduDgoc6teuLSiwEPaQ--n9FiP8HImhasKLfKUPs,4912
10
+ network_map/map_tiles.py,sha256=voeFWbr4NPsLhR592TtHbWPkepu5QQTtpprnxJLg7Rk,15514
11
+ network_map/models.py,sha256=eO0gzKwJJAyOMBr6vPpaDqzuuBGfg8w5ZAkY2ih1uaQ,2333
12
+ network_map/navigation.py,sha256=t8uwg3esPw92VqzwTG6TNyYdiTtluWmogXTX449vW1w,954
13
+ network_map/places.py,sha256=qbRqABuQrU91kODyb1-ypqcRRtHXgMDyWlMVg8euXQI,1664
14
+ network_map/png_render.py,sha256=9T3jf5TUFy1vAn4PQjmxQl_rOdt7qSTRaWr7XCTUFaU,4212
15
+ network_map/swisstopo.py,sha256=yDCIElrZJlfZhfTDBtbssbbxHOezjaKCbDpkFkleDEg,13345
16
+ network_map/urls.py,sha256=CzH0ohTuzwF4c7OvWswPN6iIhQFWl6O9qe7GOXtve9Y,636
17
+ network_map/views.py,sha256=hS3dvyP7ScuZVkcOqkNpQSJwdr7_hI3qcSnT0oTnXpg,28383
18
+ network_map/web_mercator.py,sha256=cM6EEizP9pprA7rqNcmIJsDKay71rPtUh4nbXhpRccY,2405
19
+ network_map/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
+ network_map/api/urls.py,sha256=99rinQOWYZrV4o6v_cwoke3Vtrwg-tH_zvpixXQXAXw,1028
21
+ network_map/api/views.py,sha256=9Gn-u3qcWk9rvPrPRbZpxzGFCQ97zq2aTHm8KSf1914,16481
22
+ network_map/locale/de/LC_MESSAGES/django.mo,sha256=pcOqHhMqvQP4n92QCcPFUcX6d7gigjjSHQHJYOAaxnM,13349
23
+ network_map/locale/fr/LC_MESSAGES/django.mo,sha256=WvfjTGzkyt4_gIU3ISLJfl2TUZyMZTcm_t9ogiTaFm4,13551
24
+ network_map/migrations/0001_initial.py,sha256=YoAkeIZWqdv1KW0OUhsBpyrZ4DGogHdv1A-PYgxolLg,536
25
+ network_map/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
+ network_map/static/network_map/data_coverage/coverage.css,sha256=F5NPrnclj2gduJ7AroHEftEoXhfUnWbr-3S15E9HIzg,579
27
+ network_map/static/network_map/shared/export_dialog.css,sha256=P8fD6RPLlk6lGplxPQaydfRNRhXZikXpZeBHOvQ_juY,1932
28
+ network_map/static/network_map/shared/export_dialog.js,sha256=pGh-yyksBh9EuZum8iR_oLiDKHmkcTVpVGHOVKqjn6I,6991
29
+ network_map/static/network_map/shared/svg_export.js,sha256=hGed-iISrhMDHMIhS97N3xdgzgNsx3Ys2mnHXFwuXns,55968
30
+ network_map/static/network_map/subnet_map/lv03_grid.js,sha256=Zu1om3Y3ICSoJhLCjaBMIeBPqZqV3qVquCmlNeQWSaw,170611
31
+ network_map/static/network_map/subnet_map/subnet_map.css,sha256=aEwfIgMoHzfRamFUtUp_QvHk47kdX8H09dAcrbcOOYE,7836
32
+ network_map/static/network_map/subnet_map/subnet_map.js,sha256=gc1ct2WpdI-C-8H4ueKHJVV8DafNceR74Q0KUEYwUt4,51620
33
+ network_map/static/network_map/vendor/leaflet/leaflet.css,sha256=p4NxAoJBhIIN-hmNHrzRCf9tD_miZyoHS5obTRR9BMY,14806
34
+ network_map/static/network_map/vendor/leaflet/leaflet.js,sha256=3HH4pogLw8ob2fqNxfGvSMcC3FELCngkCgfF_u186TU,147517
35
+ network_map/static/network_map/vendor/leaflet/images/layers-2x.png,sha256=Bm2sqFDY_77wB68AsG6sABVyje4nnFHzy2xxbffELt8,1259
36
+ network_map/static/network_map/vendor/leaflet/images/layers.png,sha256=Hbvp0CjikvNvy6j4s6KNXokydU_CIVuaxp5M3s9RB8Y,696
37
+ network_map/static/network_map/vendor/leaflet/images/marker-icon-2x.png,sha256=ABecTB7oMNOhCEEq4NKU9Vd2z-sIXGASmjmqb8SuJSg,2464
38
+ network_map/static/network_map/vendor/leaflet/images/marker-icon.png,sha256=V0w6XMqF9BFAhbaEFZbWLwDXyJLHsD8oy_owHesdxDc,1466
39
+ network_map/static/network_map/vendor/leaflet/images/marker-shadow.png,sha256=Jk9cZAM58ELdcpBiz8BMF_jqDymIK1OOOEjtjxDttNo,618
40
+ network_map/static/network_map/vlan_connection/vlan_connections.css,sha256=HSfNNLYlY5OJNToUGPLVaytZtLzitthiC6VScUmjLTA,4703
41
+ network_map/static/network_map/vlan_element_list/vlan_element_list.css,sha256=-k8QeTarEffiS7QxoTV-ZMCW9n1HCTiOGYxHuTPdxGk,3058
42
+ network_map/static/network_map/vlan_topology/vlan_topology.css,sha256=tifODfrhXIHUmfKWzT5zLu8S-70rBsm61-eQwNBFTl8,2400
43
+ network_map/static/network_map/vlan_topology/vlan_topology.js,sha256=ezA0f_h7jmVD2bEcw4uaEsk5OD1qzEhsW1K7KKbRSj8,20073
44
+ network_map/svg_render/__init__.py,sha256=1bdCGrbE9ePn3qxIXpSAIxYHn9Na5W_tXPqo5WbkGsg,3039
45
+ network_map/svg_render/base.py,sha256=ZBAi_MhGCabMMX-zbFaslTX11cFV3Fp1BbeGEWqnWPg,3790
46
+ network_map/svg_render/logical_tree.py,sha256=pwJOVbLi-QDutiNrIPqEppbxyjKgzBJNc4OvY6w8_cA,4176
47
+ network_map/svg_render/machine_list.py,sha256=n1yn_T8M7sDh9sno9sLc2j00cVtwP12-6Kr_psA0xEY,5761
48
+ network_map/svg_render/subnet_map.py,sha256=0LTPgcEIvZWqEbcBValOmuMGJgOmkBUnNIxZO9j7730,22387
49
+ network_map/svg_render/topology.py,sha256=2cHNfh2wWG_g45KU2cVVOkEIE0KRZ5QL01jXfwqQQws,9153
50
+ network_map/templates/network_map/data_coverage.html,sha256=y6zAcf6LxO1qtusmRaqElLV56lqbaWd64Pt7TVvhE08,6809
51
+ network_map/templates/network_map/subnet_map.html,sha256=RNMWiiS0sMdwWVInVuZ4IQwDk6hynjBOSsU_XZqS92w,4909
52
+ network_map/templates/network_map/vlan_connection.html,sha256=hxxNgLYarqcA1N_MQq7Fb1fBrISuF43dTbQDWdNnDnk,9454
53
+ network_map/templates/network_map/vlan_element_list.html,sha256=u1A9ZrNI26UwtwWNz6g_25ORznIuoGK4wqdtsCW_wg4,4656
54
+ network_map/templates/network_map/vlan_topology.html,sha256=w-u-9rS0cA1DHty5GJmd-JsDUf-rLIBLCDbOsPxJ2XU,2475
55
+ network_map/templates/network_map/includes/_export_dialog.html,sha256=oZBRvuJlFglo48jMIcODJPuWKzCOrwlo6TeXjOgOVKQ,256
56
+ network_map/templates/network_map/includes/_info_item.html,sha256=zuM-6LkFz-qNp8ejfhxrbgrJ7Ay5fvDRuUYhx4z5lnk,318
57
+ network_map/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
58
+ network_map/templatetags/network_map_export.py,sha256=g80x708r6apKmMLrxIQaKPiNoUxAA0S-xXEcRwvWDSc,795
59
+ network_map/templatetags/network_map_static.py,sha256=8ESnPWlFS0ZNHoYX3ackkQbzSpBPPBpYvt1nblKFGN0,835
60
+ network_map/tests/__init__.py,sha256=2dpJPO_sVQ9hbqwDaTCULLCVZJBhV8cvf91ntNZAi-M,40
61
+ network_map/tests/_helpers.py,sha256=pWiesX3_3IoMqJd0OrOxVTvRNVkugW8heLCrGu84i48,1630
62
+ network_map/tests/test_api_floor_plan.py,sha256=fJ9Drn_FooQOLuqQy0yIx2suRvvvfwnrvlA1lywhaJ8,12314
63
+ network_map/tests/test_api_svg.py,sha256=nplz6hy7UTMh0NGMcCThluZddtyTOCwMgBoOUQ7ILFM,22625
64
+ network_map/tests/test_bulk.py,sha256=Xzs4qcNjlDp_kfU36DLx-uHZKYXPEMNrelhMEWGfHqU,8185
65
+ network_map/tests/test_colors.py,sha256=Q_gx7VEDyYON3pxYUAib9gDFbQSH_4WImjQMjKAHUi0,6517
66
+ network_map/tests/test_coverage.py,sha256=Esl5BkgcK9Wv85QVNoH-iUZMgXSCI_24jaLDHfo1fFY,9286
67
+ network_map/tests/test_floor_plan.py,sha256=YZuKIt5jWYSSiBhpHE0IgJwpzISX7G2BH_zaNXSH2CQ,9700
68
+ network_map/tests/test_geo.py,sha256=RIe7LX_Xx9sIhhOhbVn29C-24wl2BwGYRwCj5iEektU,35384
69
+ network_map/tests/test_png.py,sha256=SlMbWWq4sgfP87qQQ5oZ5DFGdLC3qLnIrk1OkY3SrhQ,2854
70
+ network_map/tests/test_templatetags.py,sha256=uvwkydOps9t2ydjqHIPQTTQtS57RKStnLkj8EfagCTI,1785
71
+ network_map/tests/test_views.py,sha256=0d-6OsIT5q2q7-EWbxV-UB7M7dAXoRIA__5YSDiKfDA,8501
72
+ network_map/tests/test_vlan.py,sha256=Wablm_EPp64zwjPAcYowBLA_cpddZnIAIS1LpzG9Xqo,1659
73
+ netbox_plugin_network_map_yativilli-1.0.1.dist-info/METADATA,sha256=MH_JIUlMYG-RE3oIU0-XzerI7nLAx9rdTe3YdbteWw4,20885
74
+ netbox_plugin_network_map_yativilli-1.0.1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
75
+ netbox_plugin_network_map_yativilli-1.0.1.dist-info/top_level.txt,sha256=N6-pVw0lv81uCsCaTJZNVdB2S840KP7IzZBb7rQiNno,12
76
+ netbox_plugin_network_map_yativilli-1.0.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+