Compare commits

...

390 Commits

Author SHA1 Message Date
Arthur Hanson 7f8629bfe5
Merge b7a7e01e24 into 195dbaed00 2024-05-08 15:56:50 +04:00
Jeremy Stretch 195dbaed00 Fixes #16017: Bump Django to 5.0.6 2024-05-07 21:33:13 -04:00
Jeremy Stretch a9a012daf0 Fixes #16011: Fix site tenant assignment by PK via REST API 2024-05-07 16:35:11 -04:00
Jeremy Stretch 4d40699f2c Fixes #15995: Permit nullable fields referenced by unique constraints to be omitted from REST API requests 2024-05-07 15:33:14 -04:00
Jeremy Stretch ccf32244d3 Fixes #16003: Enable cache busting on upgrade for setmode.js 2024-05-07 11:10:19 -04:00
Jeremy Stretch 9316f48a20 Fixes #15982: Restore the "assign IP" tab 2024-05-07 10:43:49 -04:00
Jeremy Stretch acc2add845
Fixes #15977: Hide all admin menu items for non-authenticated users (#15978)
* Fixes #15977: Hide all admin menu items for non-authenticated users

* Account for absence of auth_required on PluginMenuItem
2024-05-07 10:37:42 -04:00
Tobias Genannt b4486b4d30 Fix #15992: Removed integrations for sentry-sdk
According to the Sentry Python SDK documentation setting the
integrations manually is only needed when the integration configuration
needs to be changed.

See: https://docs.sentry.io/platforms/python/integrations/django/#options
2024-05-07 09:11:36 -04:00
Jeremy Stretch d7592744d4 Update supported Python versions for v4.0 2024-05-06 16:41:16 -04:00
Jeremy Stretch fbcec97328 PRVB 2024-05-06 15:28:43 -04:00
Jeremy Stretch fce54f3733 Bump PR 2024-05-06 15:06:51 -04:00
Jeremy Stretch f12b2fad1f Release v4.0.0 2024-05-06 14:40:31 -04:00
transifex-integration[bot] 0f7e207674
Updates for file netbox/translations/en/LC_MESSAGES/django.po (#15974)
* Translate django.po in fr

100% translated source file: 'django.po'
on 'fr'.

* Translate django.po in ja

100% translated source file: 'django.po'
on 'ja'.

* Translate django.po in pt

100% translated source file: 'django.po'
on 'pt'.

* Translate django.po in ru

100% translated source file: 'django.po'
on 'ru'.

* Translate django.po in es

100% translated source file: 'django.po'
on 'es'.

* Translate django.po in tr

100% translated source file: 'django.po'
on 'tr'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-05-06 14:24:48 -04:00
Jeremy Stretch 840dcaa509 Update source translation strings 2024-05-06 13:42:27 -04:00
Jeremy Stretch 32b3961802
Merge pull request #15972 from netbox-community/feature
Prep for v4.0 release
2024-05-06 13:38:13 -04:00
Arthur Hanson e6a7971110
15934 screenshots (#15935)
* 15934 update documentation screenshots

* 15934 update documentation screenshots

* 15934 update documentation screenshots

* Update cable trace screenshot

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-05-06 13:08:55 -04:00
Jeremy Stretch 51bd98bdfc Merge branch 'develop' into feature 2024-05-06 12:59:24 -04:00
Jeremy Stretch f00eceb1eb Merge branch 'master' into develop 2024-05-06 12:55:19 -04:00
Jeremy Stretch be903a64a2 Release v3.7.8 2024-05-06 12:54:53 -04:00
transifex-integration[bot] 0d7bac433e Translate django.po in ja
100% translated source file: 'django.po'
on 'ja'.
2024-05-06 12:54:53 -04:00
Jeremy Stretch b1cfbbc472 Fixes #15960: Use internal ManyToManyColumn to ensure proper export behavior 2024-05-06 12:54:53 -04:00
Jeremy Stretch 6dd311f600 Fixes #15961: Fix secret toggle button by avoiding duplicate event handler 2024-05-06 12:54:53 -04:00
Daniel Sheppard 85d250014f Fixes: #15948 - Fixes cable fanin/fanout when both are required (#15953)
* Preliminary fix for #15948

* Tweaking of line height
2024-05-06 12:54:53 -04:00
Arthur 552c81509a 12127 enable cable add button 2024-05-06 12:54:53 -04:00
Jeremy Stretch ed7a0a32cc Changelog for #15877, #15917, #15925 2024-05-06 12:54:53 -04:00
Nancy Yang a544b55e9e Fixes #15917: slim-select-pagination-bug-fix : fixed several bugs related to slim select (#15918)
* slim-select-pagination-bug-fix : fixed several bugs related to slim
select search box gui element
1. If user enters a search text in the filter text box, the user will
   not be able to scroll to the next page. That is the user will only be
   able to see the first page of returned item with a none empty search
   string.
2. User will not be able to select an item returned from search query
   if user clicks reload after a dynami search. When the user is able
   to load a second page, the user will be able to select an item from
   the third+ page if previous bug is fixed.

* Recompile static assets

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-05-06 12:54:53 -04:00
Jeremy Stretch 53e1ab5fc5 Fixes #15877: Consider VC membership when assigning LAG interfaces via bulk edit 2024-05-06 12:54:53 -04:00
Jeremy Stretch 2c1a9ae455 Fixes #15925: Fix rendering of cable traces to circuit terminations 2024-05-06 12:54:53 -04:00
Jeremy Stretch 1afa476a19 PRVB 2024-05-06 12:54:53 -04:00
Jeremy Stretch c02bd0ab19 Release v3.7.8 2024-05-06 12:43:46 -04:00
transifex-integration[bot] c7d53ed8eb Translate django.po in ja
100% translated source file: 'django.po'
on 'ja'.
2024-05-06 12:34:29 -04:00
Jeremy Stretch 15cc50fc1d Fixes #15960: Use internal ManyToManyColumn to ensure proper export behavior 2024-05-06 10:32:29 -04:00
Jeremy Stretch 60aee6f5e1 Fixes #15961: Fix secret toggle button by avoiding duplicate event handler 2024-05-06 10:31:29 -04:00
Daniel Sheppard 56e0449ebc
Fixes: #15948 - Fixes cable fanin/fanout when both are required (#15953)
* Preliminary fix for #15948

* Tweaking of line height
2024-05-06 09:48:14 -04:00
Arthur 4cc5079ecb 12127 enable cable add button 2024-05-06 08:37:22 -04:00
Jeremy Stretch c6f833e83b Changelog for #15877, #15917, #15925 2024-05-03 17:34:45 -04:00
Jeremy Stretch b91741dd75 Changelog for #15630, #15802, #15831, #15852, #15915, #15942, #15944 2024-05-03 17:31:43 -04:00
Jeremy Stretch 8e1c2ecd92
Closes #15915: Replace plugins list with an overall system status view (#15950)
* Replace plugins list with an overall system status view

* Enable export of system status data
2024-05-03 17:26:19 -04:00
Jeremy Stretch a9b311b100 Fixes #15944: Extend paginator template to be aware of placement 2024-05-03 11:56:37 -04:00
Jeremy Stretch 41504425ac Closes #15942: Refactor settings_and_registry() context processor 2024-05-03 10:58:03 -04:00
Jeremy Stretch f8cf2a3786 Closes #15932: Update embedded documentation for generic templates 2024-05-03 10:57:05 -04:00
Jeremy Stretch d824e90e0a Extend release checklist to include updating UI resources 2024-05-02 17:07:41 -04:00
Jeremy Stretch f8eee45ba3 #15852: Hide count element for non-HTMX requests 2024-05-02 16:11:50 -04:00
Arthur Hanson 3d4bb209ee
15802 change table anchor color (#15841)
* 15802 change table anchor color

* 15802 make link color lighter

* 15802 lighten table color

* 15802 add comment

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-05-02 16:06:05 -04:00
Arthur Hanson 8f2eba24bb
15831 monkeypatch LDAP _mirror_group function for NB4 (#15902)
* 15831 monkeypatch LDAP _mirror_group function for NB4

* 15831 monkeypatch LDAP _mirror_group function for NB4

* 15831 monkeypatch LDAP _mirror_group function for NB4

* Move the modified _mirror_groups() method to a separate module to retain license

* 15831 fix import

* 15831 fix import

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-05-02 16:02:21 -04:00
Jeremy Stretch 8f92b8519c Update front end dependencies 2024-05-02 15:31:43 -04:00
Jeremy Stretch 0bc2bffb81 Remove obsolete dependencies 2024-05-02 14:31:39 -04:00
Jeremy Stretch 3d3c2e9e1f Delete extraneous lock file 2024-05-02 13:57:07 -04:00
Jeremy Stretch 17e6d1076a
Fixes #15852: Update total object counts when filtering object lists (#15909)
* Fixes #15852: Update total object counts when filtering object lists

* Misc cleanup
2024-05-02 10:43:53 -04:00
Julio Oliveira at Encora 4c93a2d084
Feature 15832 - Multiselect has no "delete" option on the values (#15883)
* Added remove_button in config.ts

* Fixed linter issues

* Fixed linter issues

* Fixed linter issues

* Enable remove_button plugin only for multi-select fields

* Enable remove_button plugin only for multi-select fields

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-05-02 09:39:10 -04:00
Jeremy Stretch 6530051958 Closes #15630: Remove server-side color mode preference & simplify toggling 2024-05-01 18:59:42 -04:00
Arthur b7a7e01e24 Merge branch 'develop' into 15404-openapi-2 2024-05-01 15:38:42 -07:00
Jeremy Stretch 44a7cd9876 Update changelog with beta2 bug fixes 2024-05-01 16:15:08 -04:00
Jeremy Stretch 312291b010 Merge branch 'develop' into feature 2024-05-01 16:09:14 -04:00
Arthur 1add918d31 15833 make navbar sticky at top 2024-05-01 10:44:28 -04:00
Arthur 778b8b9b48 15853 fix background color for cable trace svg in dark mode 2024-05-01 10:30:01 -04:00
Arthur Hanson d0e0dcb652
15855 fix adding script as event rule (#15861)
* 15855 fix adding script as event rule

* 15855 fix adding script as event rule

* 15855 fix adding script as event rule

* 15855 fix adding script as event rule
2024-05-01 10:24:17 -04:00
Arthur Hanson 209f596397
15815 convert dashboard widgets for users/groups (#15839)
* 15815 convert dashboard widgets for users/groups

* 15815 review fixes

* 15815 catch DoesNotExist for widget content type

* 15815 add logging
2024-05-01 09:56:46 -04:00
Arthur 0a7d1e29b4 15823 remove openid from social-auth-core requirement 2024-05-01 09:45:42 -04:00
Arthur 835012f2ed 15838 use naturalday for date not naturaltime 2024-04-26 16:19:21 -04:00
Tobias Genannt 5af3c659a5 Fix #15826: Added new group and user models 2024-04-25 09:23:27 -04:00
Arthur Hanson 4923025fec
15541 Add component selector to InventoryItemTemplate (#15691)
* 15541 update InventoryItemTemplateForm

* 15541 update InventoryItemTemplateForm

* Remove custom template

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-04-25 09:22:32 -04:00
Arthur Hanson ded2fe9471
15809 Mark unions as nullable in GraphQL where appropriate (#15824)
* 15809 mark unions as nullable where appropriate

* 15809 fix tests

* 15809 fix tests
2024-04-25 09:19:19 -04:00
Jeremy Stretch e05ca710ae Flag HTMX navigation as an experimental feature 2024-04-23 10:38:49 -04:00
Jeremy Stretch c32dff5649 Release v4.0-beta2 2024-04-22 15:35:34 -04:00
Jeremy Stretch 8364e632b7 Remove obsolete type definitions 2024-04-22 15:10:37 -04:00
Jeremy Stretch c43b929542 Fixes #15580: Fix rendering of modals with HTMX enabled 2024-04-22 15:10:28 -04:00
Jeremy Stretch e3c418263e Fixes #15778: Fix bulk edit/delete functionality when HTMX is enabled 2024-04-22 14:31:39 -04:00
Jeremy Stretch 46bd62fdc9 Merge branch 'develop' into feature 2024-04-22 13:23:42 -04:00
Arthur 781d932b2a 15789 make sure job completed before including config_form 2024-04-21 13:57:15 -04:00
Jeremy Stretch 781409b5ae Fixes #15787: Convert User ID column to 64-bit integer 2024-04-21 13:55:39 -04:00
Jeremy Stretch f42d0336c2 Clean up layout of global search results 2024-04-19 15:27:25 -04:00
Jeremy Stretch db87fe96b7 Clean up bulk import view 2024-04-19 15:23:09 -04:00
Jeremy Stretch 0f0ab1a3be Closes #15547: Add comments field to CustomField model 2024-04-19 15:10:06 -04:00
Jeremy Stretch 824d66a54c Dissuade non-superusers from creating API tokens via the admin view 2024-04-19 14:34:25 -04:00
Jeremy Stretch 3551f3e021 Remove the is_staff restriction for admin menu items 2024-04-19 14:34:25 -04:00
Florian Derler 1a1300716c #15712: add imageattachments to vms 2024-04-19 14:15:50 -04:00
Arthur 4b83b5d0e1 15764 change vc_position from PositiveSmallInteger to PositiveInteger 2024-04-19 13:22:44 -04:00
Jeremy Stretch 174865b9aa Fixes #15760: Permit breaking of long words for wrap within object attribute tables 2024-04-19 13:18:25 -04:00
Jeremy Stretch c9bd59ab02 Fixes #15641: Fix adding/removing filters on advanced object selector widget 2024-04-19 13:15:57 -04:00
Jeremy Stretch 1efd80954e Formatting cleanup 2024-04-19 10:50:00 -04:00
Jeremy Stretch 480b36d65e Fixes #15698: Drop and recreate FK constraints on ObjectPermission M2M tables 2024-04-19 08:17:19 -04:00
Jeremy Stretch d0f0782bc0 Update changelog 2024-04-17 16:24:04 -04:00
Arthur e303ccfd12 15636 change content_type_id to object_type_id for imageattachment 2024-04-17 16:13:29 -04:00
Jeremy Stretch 75d6bfe42f Closes #15736: Remove annotated_date() template filter and annotated_now() template tag 2024-04-17 14:09:07 -04:00
Jeremy Stretch 95cc29d898 Closes #15752: Remove the ENABLE_LOCALIZATION configuration parameter 2024-04-17 11:54:29 -04:00
Jeremy Stretch 157df069e8 Closes #15738: Remove configuration parameters date & time formatting 2024-04-17 11:50:14 -04:00
Jeremy Stretch 77a4300888
Closes #15618: Always use ISO 8601 date & time formatting (#15737)
* Introduce the isodate(), isotime(), and isodatetime() template filters

* Display the relative time on mouse hover

* Render journal entry times in ISO 8601 format

* Use ISO 8601 format when displaying dates & times in a table

* Standardize the use of DateTimeColumn across all tables
2024-04-17 11:46:47 -04:00
Jeremy Stretch f0aca5bac1 Remove notes referencing past releases 2024-04-17 08:41:51 -04:00
Jeremy Stretch c858aa33cc Fix broken link in installation guide 2024-04-17 08:37:38 -04:00
Markku Leiniö 21db54ae2f
Closes #15740: Fix typos and deprecated List in docs (#15741)
* Fix typos in migration-v4.md

* Replace typing.List with list

typing.List is deprecated since Python 3.9

* Also replace typing.List with list in graphql-api.md
2024-04-17 08:28:03 -04:00
Jeremy Stretch 2a8876846f Fixes #15695: Clear any legacy group permission associations during migration 2024-04-16 16:20:00 -04:00
Jeremy Stretch 4562e347fd Fixes #15613: Show login button/user menu on mobile view 2024-04-16 16:17:50 -04:00
Jeremy Stretch 4e4c277711 Fixes #15652: Fix the display of error messages after attempting to delete an object 2024-04-15 15:47:43 -04:00
Arthur Hanson 0da8164600
15684 strawberry filter (#15686)
* 15579 update requirements

* 15684 add USE_DEPRECATED_FILTERS to strawberry
2024-04-15 13:29:29 -04:00
Arthur Hanson 5e05041b8b
15671 save module before sync_classes (#15675)
* 15671 save module before sync_classes

* 15671 don't return save
2024-04-15 13:22:56 -04:00
Arthur 815cab5c9a 15532 fix autotype_decorator for method fields 2024-04-15 13:05:55 -04:00
Jeremy Stretch 379fe7c160 Changelog for #15605, #15616, #15617, #15619, #15637, #15638 2024-04-11 10:46:52 -04:00
Arthur Hanson 89dd423080
15676 update python versions in pyproject.toml (#15687)
* 15676 update python versions in pyproject.toml

* 15676 update formatting
2024-04-10 16:36:04 -04:00
Arthur dfae19ca1c 15688 remove USE_L10N setting 2024-04-10 16:23:53 -04:00
Jeremy Stretch 89453a49d6 Fixes #15605: Account for older sequence name in migration 2024-04-10 16:17:01 -04:00
Jeremy Stretch c7f6c206cf Fixes #15638: Correct parameter used to retrieve saved filters for a model 2024-04-05 14:56:16 -04:00
Jeremy Stretch 9f16f1466a Fixes #15620: Limit width of user preferences form 2024-04-05 14:38:32 -04:00
Jeremy Stretch d34f188d40
Fixes #15637: Fix rendering of links from within embedded tables w/HTMX enabled (#15642)
* Add htmx_table to __all__

* Fix dropdown menu clipping

* Fix loading links from within embedded tables

* Fix rendering of object deletion warning
2024-04-05 14:22:09 -04:00
Jeremy Stretch ccca0580f7 Fixes #15619: Enforce a minimum width for progress bars 2024-04-05 14:20:05 -04:00
Jeremy Stretch 99fe63569d Fixes #15617: Fix rack elevation styling under dark mode 2024-04-05 14:19:30 -04:00
Jeremy Stretch 25c39ce480 Fixes #15616: Tweak button class for invalid custom links 2024-04-05 14:17:58 -04:00
Jeremy Stretch 0abd0948b6 Closes #15607: Update upgrade path diagram 2024-04-03 14:25:32 -04:00
Jeremy Stretch 933fdf3ce9 Bump OS & Python for docs build 2024-04-03 09:32:48 -04:00
Jeremy Stretch 8d773b1b93 Add warning to beta release notes 2024-04-03 09:02:25 -04:00
Jeremy Stretch 97b9117982 Release v4.0-beta1 2024-04-03 08:44:34 -04:00
Jeremy Stretch 489e956d83 Merge branch 'develop' into feature 2024-04-03 08:43:12 -04:00
Jeremy Stretch d9949b2de1 Update API test for Group model 2024-04-02 16:33:12 -04:00
Jeremy Stretch 53ff85df21 Prevent the stock Django Group model from appearing in the admin UI (if enabled) 2024-04-02 15:48:40 -04:00
Jeremy Stretch 8b8d63db2e Include description field on Group serializer 2024-04-02 15:41:44 -04:00
Jeremy Stretch abc949a015 Refactor models under users app 2024-04-02 15:06:05 -04:00
Jeremy Stretch fb129579c1 #12795: Complete support for description field on custom Group model 2024-04-02 14:44:26 -04:00
Jeremy Stretch 7fe2e4849d Update custom validation docs for #14279, #15490 2024-04-02 14:14:58 -04:00
Jeremy Stretch dda9381880 Remove old feature notifications 2024-04-02 14:14:58 -04:00
Arthur ecf22bff4d update readme for strawbery 2024-04-02 11:12:33 -07:00
Jeremy Stretch 7422605831 Fleshed out v4.0 release notes 2024-04-02 13:30:26 -04:00
Jeremy Stretch f0291aa60f Fix ordering of Groups 2024-04-02 08:31:53 -04:00
Jeremy Stretch 3ab2f25ee1
Closes #15043: Add v4.0 migration guide for plugins (#15477)
* Add v4.0 migration guide for plugins

* Note Python 3.12 support

* 15403 update GraphQL plugin conversion docs

* Add section on FieldSets

---------

Co-authored-by: Arthur <worldnomad@gmail.com>
2024-04-01 09:08:28 -04:00
Jeremy Stretch f6da3f8006 Fixes #15581: Disable HTMX when logging out 2024-03-29 15:26:43 -04:00
Jeremy Stretch c8d9d9358e
Closes #15464: Move permission assignments to user & group models (#15554)
* Move user & group M2M assignments for ObjectPermission

* Restore users & groups fields on ObjectPermission serializer
2024-03-29 14:57:16 -04:00
Arthur Hanson 8767577ecd
15553 change graphql sub-queries from functions to types (#15557)
* 15553 change graphql list to types

* 15553 review changes
2024-03-29 14:54:31 -04:00
Arthur Hanson c8d288671e
15552 graphql docs (#15578)
* 15552 update query

* 15552 update query

* 15552 update query

* Update docs/integrations/graphql-api.md

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-03-29 14:50:11 -04:00
Arthur Hanson 56dca86e9b
15574 Backport strawberry-graphql-django library (#15577)
* 15574 backport strawberry-django

* Restore strawberry-graphql version

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-03-29 13:23:33 -04:00
Jeremy Stretch 3be3bbe534 Upgrade DRF to v3.15 2024-03-29 13:13:41 -04:00
Jeremy Stretch 0dedf602e8 Update dependencies 2024-03-29 09:20:29 -04:00
Jeremy Stretch d707c65637 Fixes #15536: Fix clipping of dropdown menus inside responsive tables 2024-03-28 16:40:34 -04:00
Jeremy Stretch eaa3bc6576 Fixes #15570: Remove omission of M2M relations when generating writable serializers for OpenAPI schema 2024-03-28 16:17:12 -04:00
Arthur Hanson 99508150d3
15154 Add uWSGI as option to gunicorn (#15550)
* 15154 uwsgi docs

* 15154 uwsgi contrib files

* 15154 review comments - merge nginx conf

* Restructure gunicorn/uWSGI installation docs

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-03-28 15:24:08 -04:00
Jeremy Stretch 744be59a4d
Closes #14736: Enable HTMX navigation globally (#15158)
* Enable HTMX boosting

* Refactor HTMX properties for tables

* Fix dashboard object list widget

* Disable scrolling to page content

* Fix initialization of TomSelect dropdowns after HTMX loading

* Replace formaction properties with hx-post

* Fix quick search field on object list view

* Reinitialize copy-to-clipboard buttons upon HTMX load

* Disable scrolling effect for intra-page navigation

* Introduce user preference for toggling HTMX navigation

* Enable HTMX navigation only when selected by user

* Pass htmx_navigation context

* Fix display of confirmation form when deleting an object

* Disable HTMX boosting for rack elevation SVG downloads

* Fix dyanmic form rendering

* Introduce htmx_boost template tag; enable HTMX for user menu

* Use out-of-band sap to update footer stamp

* Fix display of toasts after form submission

* Fix user preference selection

* Misc cleanup

* Rename render_partial() to htmx_partial()

* Add docstring to htmx_boost template tag

* Disable HTMX for user preferences form to force a full page refresh on changes
2024-03-28 11:51:38 -04:00
Jeremy Stretch 04d8db7c52
Closes #15236: Clean up unused static resources (#15539)
* Remove unused images

* Remove unused stylesheets
2024-03-26 15:27:25 -04:00
Arthur Hanson 0cff4c9795
14799 Fix sync of scripts from data source (#15303)
* 14799 fix script creation from data-source

* 14799 dont cache module_scripts

* 14799 fix sync_classes call
2024-03-26 08:36:36 -04:00
Jeremy Stretch 817e009e4f Closes #15490: CustomValidator support for accessing related object attribute via dotted path 2024-03-25 11:57:58 -04:00
Arthur 74444da7b8 Merge branch '9856-strawberry-2' into feature 2024-03-22 12:16:16 -07:00
Jeremy Stretch 6973228825 Closes #15465: Clean up settings.py 2024-03-22 15:15:37 -04:00
Arthur 43e7dd3685 9856 move static files 2024-03-22 12:10:53 -07:00
Arthur Hanson 45c99e4477
9856 Replace graphene with Strawberry (#15141)
* 9856 base strawberry integration

* 9856 user and group

* 9856 user and circuits base

* 9856 extras and mixins

* 9856 fk

* 9856 update strawberry version

* 9856 update imports

* 9856 compatability fixes

* 9856 compatability fixes

* 9856 update strawberry types

* 9856 update strawberry types

* 9856 core schema

* 9856 dcim schema

* 9856 extras schema

* 9856 ipam and tenant schema

* 9856 virtualization, vpn, wireless schema

* 9856 fix old decorator

* 9856 cleanup

* 9856 cleanup

* 9856 fixes to circuits type specifiers

* 9856 fixes to circuits type specifiers

* 9856 update types

* 9856 GFK working

* 9856 GFK working

* 9856 _name

* 9856 misc fixes

* 9856 type updates

* 9856 _name to types

* 9856 update types

* 9856 update types

* 9856 update types

* 9856 update types

* 9856 update types

* 9856 update types

* 9856 update types

* 9856 update types

* 9856 update types

* 9856 GraphQLView

* 9856 GraphQLView

* 9856 fix OrganizationalObjectType

* 9856 single item query for schema

* 9856 circuits graphql tests working

* 9856 test fixes

* 9856 test fixes

* 9856 test fixes

* 9856 test fix vpn

* 9856 test fixes

* 9856 test fixes

* 9856 test fixes

* 9856 circuits test sans DjangoModelType

* 9856 core test sans DjangoModelType

* 9856 temp checkin

* 9856 fix extas FK

* 9856 fix tenancy FK

* 9856 fix virtualization FK

* 9856 fix vpn FK

* 9856 fix wireless FK

* 9856 fix ipam FK

* 9856 fix partial dcim FK

* 9856 fix dcim FK

* 9856 fix virtualization FK

* 9856 fix tests / remove debug code

* 9856 fix test imagefield

* 9856 cleanup graphene

* 9856 fix plugin schema

* 9856 fix requirements

* 9856 fix requirements

* 9856 fix docs

* 9856 fix docs

* 9856 temp fix tests

* 9856 first filterset

* 9856 first filterset

* 9856 fix tests

* 9856 fix tests

* 9856 working auto filter generation

* 9856 filter types

* 9856 filter types

* 9856 filter types

* 9856 fix graphiql test

* 9856 fix counter fields and merge feature

* 9856 temp fix tests

* 9856 fix tests

* 9856 fix tenancy, ipam filter definitions

* 9856 cleanup

* 9856 cleanup

* 9856 cleanup

* 9856 review changes

* 9856 review changes

* 9856 review changes

* 9856 fix base-requirements

* 9856 add wrapper to graphiql

* 9856 remove old graphiql debug toolbar

* 9856 review changes

* 9856 update strawberry

* 9856 remove superfluous check

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-03-22 12:56:30 -04:00
Arthur 55725ee114 9856 remove superfluous check 2024-03-22 08:05:27 -07:00
Arthur d40bf63b2f 9856 update strawberry 2024-03-22 07:51:32 -07:00
Arthur d027a1c26d 9856 review changes 2024-03-22 07:48:56 -07:00
Arthur be522467ab Merge branch 'feature' into 9856-strawberry-2 2024-03-22 07:29:45 -07:00
Jeremy Stretch 423c9813a2 Fix import statement 2024-03-22 09:32:57 -04:00
Jeremy Stretch eb3aa7cb36 Move utilities.utils.get_related_models() to utilities.relations 2024-03-22 08:59:52 -04:00
Jeremy Stretch b86c8a9524 Remove utilities.utils.dynamic_import() 2024-03-22 08:59:52 -04:00
Jeremy Stretch 7b1a08a187 Move local_now() to utilities.datetime 2024-03-22 08:59:52 -04:00
Jeremy Stretch 6ac700e43f Move object_type_identifier() & object_type_name() to utilities.object_types 2024-03-22 08:59:52 -04:00
Jeremy Stretch 8ad73e3f90 Rename content_type_identifier() and content_type_name() 2024-03-22 08:59:52 -04:00
Jeremy Stretch ae8df77cc8 Move count_related() & dict_to_filter_params() to utilities.query 2024-03-22 08:59:52 -04:00
Jeremy Stretch 3b4898adea Move prepare_cloned_fields() to utilities.querydict 2024-03-22 08:59:52 -04:00
Jeremy Stretch a49eb80f9e Move dict_to_querydict() and normalize_querydict() to utilities.querydict 2024-03-22 08:59:52 -04:00
Jeremy Stretch 81ca455fef Move array_to_range(), array_to_string(), deepmerge(), drange(), flatten_dict(), and shallow_compare_dict() to utilities.data 2024-03-22 08:59:52 -04:00
Jeremy Stretch 1d3efc90c0 Move utilities.utils.render_jinja2() to utilities.jinja2 2024-03-22 08:59:52 -04:00
Jeremy Stretch c30d22335a Move extras.jinja2.ConfigTemplateLoader to utilities.jinja2.DataFileLoader 2024-03-22 08:59:52 -04:00
Jeremy Stretch b2e03805ab Remove obsolete function utilities.utils.csv_format() 2024-03-22 08:59:52 -04:00
Jeremy Stretch 2a3b85a32f Move clean_html() & foreground_color() to utilities.html 2024-03-22 08:59:52 -04:00
Jeremy Stretch b92d3245c8 Move serialize_object() & deserialize_object() to utilities.serialization 2024-03-22 08:59:52 -04:00
Jeremy Stretch bbb8b7d010 Move to_grams() & to_meters() from utilities.utils to utilities.conversion 2024-03-22 08:59:52 -04:00
Jeremy Stretch ef774319f4 Move NetBoxFakeRequest and copy_safe_request() from utilities.utils to utilities.request 2024-03-22 08:59:52 -04:00
Jeremy Stretch 3547ea376c Move utilities.utils.get_viewname() to utilities.views 2024-03-22 08:59:52 -04:00
Jeremy Stretch 2719fa3b5a Move utilities.utils.highlight_string() to utilities.html.highlight() 2024-03-22 08:59:52 -04:00
Jeremy Stretch 950954a3db Move title() from utilities.utils to utilities.string 2024-03-22 08:59:52 -04:00
Jeremy Stretch 73bb175afa Rename resolve_permission_ct() to resolve_permission_type() 2024-03-22 08:59:52 -04:00
Jeremy Stretch f48d1c9410 custom_deconstruct() should not ignore TimeZoneField 2024-03-22 08:59:52 -04:00
Jeremy Stretch f49819ebc2 Move ConfigTemplateLoader from utilities.jinja2 to extras.jinja2 2024-03-22 08:59:52 -04:00
Jeremy Stretch aa9a40f268 Remove unused MACAddressFilter 2024-03-22 08:59:52 -04:00
Jeremy Stretch d924eaf4da Remove the sha256_hash() utility function 2024-03-22 08:59:52 -04:00
Jeremy Stretch 19bb808936 Move utilities.api.rest_api_server_error() to utilities.error_handlers.handle_rest_api_exception() 2024-03-22 08:59:52 -04:00
Jeremy Stretch a9bb4c5c3e Move choice sets from utilities.choices to netbox.choices 2024-03-22 08:59:52 -04:00
Jeremy Stretch 99144031b7 Refactor get_view_name() 2024-03-22 08:59:52 -04:00
Jeremy Stretch 78b4fa5196
Closes #14279: Pass current request to custom validators (#15491)
* Closes #14279: Pass current request to custom validators

* Update custom validation docs

* Check that validator is a subclass of CustomValidator
2024-03-21 21:19:53 -04:00
Arthur bbccb8787d 9856 remove old graphiql debug toolbar 2024-03-20 14:01:55 -07:00
Arthur f456731929 9856 add wrapper to graphiql 2024-03-20 13:54:26 -07:00
Jeremy Stretch a83b233341
Closes #15339: Consume entire viewport (#15480)
* Closes #15339: Consume entire viewport, except for object detail views

* Use fluid containers for all views
2024-03-20 08:26:04 -04:00
Arthur 371a2a29ca 9856 fix base-requirements 2024-03-19 13:38:42 -07:00
Jeremy Stretch a3ce14ad3c Update release notes 2024-03-19 14:18:15 -04:00
Jeremy Stretch 849a9d32d1
Fixes #15340: Fix flicker on page load with dark mode enabled (#15475) 2024-03-19 14:06:24 -04:00
Jeremy Stretch 32edb8dfe6 Misc cleanup & documentation for FieldSets 2024-03-19 13:34:13 -04:00
Jeremy Stretch 89150f4b27 Add form rendering utilities to plugins dev docs 2024-03-19 13:34:13 -04:00
Jeremy Stretch 708d93c9e0 Use render_fieldset() for bulk edit & filter forms 2024-03-19 13:34:13 -04:00
Jeremy Stretch 72d3c17b48 Use FieldSet instances for all forms 2024-03-19 13:34:13 -04:00
Jeremy Stretch 3b28e8e615 Refactor form rendering components & add docstrings 2024-03-19 13:34:13 -04:00
Jeremy Stretch 2aaa552067 Replace custom form templates with TabbedFieldGroups 2024-03-19 13:34:13 -04:00
Jeremy Stretch 8f03a19b5f Introduce ObjectAttribute for displaying read-only instance attributes on forms 2024-03-19 13:34:13 -04:00
Jeremy Stretch 33b9ebb201 Ignore fields which are not included on the form (dynamic rendering) 2024-03-19 13:34:13 -04:00
Jeremy Stretch 4c7b6fcec0 Enable tabbed group fields in fieldsets 2024-03-19 13:34:13 -04:00
Jeremy Stretch f585c36d86 Introduce InlineFields for rendering fields side-by-side 2024-03-19 13:34:13 -04:00
Arthur 783c4f2edc 9856 review changes 2024-03-19 10:11:13 -07:00
Arthur 908150f9a1 9856 review changes 2024-03-19 10:10:36 -07:00
Arthur b75b9e01eb 9856 review changes 2024-03-19 10:04:33 -07:00
Arthur 93c9f8cc04 15193 use psycopg compiled 2024-03-19 08:26:05 -04:00
Jeremy Stretch 9c29f45c1a Merge branch 'feature' into 9856-strawberry-2 2024-03-18 10:12:06 -04:00
Daniel Sheppard 19f577ccaf
Closes: #13918 - Add facility field (#15456)
* Fixes: #13918 - Add facilities field to Location model.

* Stupidly forgot to `git add`

* Fix errant reference to site.

* Misc cleanup

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-03-18 10:09:50 -04:00
Arthur 397d475159 15404 schema map for SerializedPKRelatedField 2024-03-15 12:02:19 -07:00
Jeremy Stretch 78bd7dec48 Merge branch 'develop' into feature 2024-03-15 12:32:54 -04:00
Jeremy Stretch b58c85cdb0
Merge pull request #15421 from netbox-community/15413-search-cache-attrs
Closes #15413: Enable caching of object attributes in search index
2024-03-15 11:38:06 -04:00
Jeremy Stretch 9062d99bfa Closes #14454: Include member devices for virtual chassis in REST API 2024-03-13 19:11:28 -04:00
Arthur 3b30aa965f 9856 cleanup 2024-03-13 15:21:18 -07:00
Arthur 6090d41b34 9856 cleanup 2024-03-13 14:50:41 -07:00
Arthur da0c23bc0c 9856 cleanup 2024-03-13 14:21:02 -07:00
Arthur 9c53f76d9e 9856 fix tenancy, ipam filter definitions 2024-03-13 13:37:17 -07:00
Arthur b47c5ee1b8 9856 fix tests 2024-03-13 13:13:20 -07:00
Jeremy Stretch 7357f953eb Closes #15413: Enable caching of object attributes in search index 2024-03-13 16:13:06 -04:00
Arthur 151717545a 9856 temp fix tests 2024-03-13 11:51:37 -07:00
Arthur 1da5219563 9856 fix counter fields and merge feature 2024-03-13 11:37:13 -07:00
Arthur 2b7c1d1845 Merge branch 'feature' into 9856-strawberry-2 2024-03-13 11:20:33 -07:00
Arthur 634f35a972 9856 fix graphiql test 2024-03-13 11:19:54 -07:00
Arthur 347e453b7d 9856 filter types 2024-03-13 10:48:46 -07:00
Arthur 21b585e5e3 9856 filter types 2024-03-13 10:39:08 -07:00
Arthur 2c9bea9ab9 9856 filter types 2024-03-13 10:07:28 -07:00
Arthur fe3f2c8958 9856 working auto filter generation 2024-03-12 16:35:17 -07:00
Arthur 960f3407f5 9856 fix tests 2024-03-12 10:52:20 -07:00
Arthur d6fd0b88af 9856 fix tests 2024-03-12 10:39:00 -07:00
Arthur c2a3275c79 9856 merge feature 2024-03-12 10:12:09 -07:00
Jeremy Stretch 2d4295e2ed
Merge pull request #15369 from netbox-community/15237-audit-filtersets
Closes #15237: Add tests for missing filters
2024-03-12 13:09:14 -04:00
Arthur 8aca8f84b4 9856 first filterset 2024-03-12 10:01:34 -07:00
Arthur a36cc0abb6 9856 first filterset 2024-03-12 09:35:53 -07:00
Jeremy Stretch 572efeb987 Ensure all filter labels are translated 2024-03-12 12:14:13 -04:00
Jeremy Stretch bea32aef71 Declare FilterSet fields as a tuple 2024-03-12 12:08:11 -04:00
Jeremy Stretch 52bda9c0e6
Closes #15401: Rename PostgreSQL tables & indexes for L2VPN models (#15405)
* Closes #15401: Rename PostgreSQL tables & indexes for L2VPN models

* Account for alternate index name
2024-03-12 11:20:23 -04:00
Jeremy Stretch f8744a6659 Clean up exemption logic 2024-03-11 15:54:35 -04:00
Jeremy Stretch 313e63622b Extend logic for validating filter class 2024-03-11 15:35:40 -04:00
Jeremy Stretch a136030094 Validate filter class for foreign key fields 2024-03-11 14:39:34 -04:00
Jeremy Stretch b36a70d236 Add missing filters for reverse many-to-many relationships 2024-03-11 14:39:34 -04:00
Jeremy Stretch 6085e0bb0b Test for missing ManyToManyField filters 2024-03-11 14:39:34 -04:00
Jeremy Stretch 0a0dae3d35 Inspect many-to-many fields 2024-03-11 14:39:34 -04:00
Jeremy Stretch 5cb7af88d4 Fix remaining tests 2024-03-11 14:39:34 -04:00
Jeremy Stretch 16b422cbac Add missing filters 2024-03-11 14:39:34 -04:00
Jeremy Stretch 6af12b1814 Add tests for missing FilterSet filters 2024-03-11 14:39:34 -04:00
Jeremy Stretch d6acc18c29 Closes #15383: Standardize filtering logic for the parents of recursively-nested models 2024-03-11 13:27:57 -04:00
Arthur be2a814b38 9856 temp fix tests 2024-03-11 09:17:58 -07:00
Arthur 02fbea53a7 9856 fix docs 2024-03-11 08:57:41 -07:00
Arthur 916722780c 9856 fix docs 2024-03-11 08:50:56 -07:00
Jeremy Stretch 21de3f954f #15357: Rename CustomField object_type to related_object_type 2024-03-11 11:49:04 -04:00
Arthur 36e6f0d28e 9856 fix requirements 2024-03-11 08:39:01 -07:00
Arthur ba79078378 9856 fix requirements 2024-03-11 08:38:06 -07:00
Arthur f960d5a482 9856 fix plugin schema 2024-03-11 08:33:32 -07:00
Arthur ccc81e73d1 9856 cleanup graphene 2024-03-11 07:31:38 -07:00
Jeremy Stretch 78dd65219f
Closes #15357: Rename CustomField.object_type to related_object_type (#15366) 2024-03-09 06:16:17 -05:00
Arthur 7fa36cada5 9856 fix test imagefield 2024-03-07 12:59:56 -08:00
Arthur 7b6a603111 9856 fix tests / remove debug code 2024-03-07 12:48:57 -08:00
Arthur 133d6bfcb3 9856 fix virtualization FK 2024-03-07 11:55:35 -08:00
Arthur a32f4b82e9 9856 fix dcim FK 2024-03-07 11:40:50 -08:00
Arthur 676764a661 9856 fix partial dcim FK 2024-03-07 10:48:50 -08:00
Arthur e53475a63f 9856 fix ipam FK 2024-03-07 10:11:28 -08:00
Arthur 5f56e2daff 9856 fix wireless FK 2024-03-07 09:58:26 -08:00
Arthur 38c7d76646 9856 fix vpn FK 2024-03-07 09:53:11 -08:00
Arthur 26c8ee36fa 9856 fix virtualization FK 2024-03-07 09:42:52 -08:00
Arthur 4b56f0b000 9856 fix tenancy FK 2024-03-07 09:07:31 -08:00
Arthur 12cca5d0a0 9856 fix extas FK 2024-03-07 08:58:56 -08:00
Arthur Hanson 663bd32464
10587 script pagination (#15343)
* 10587 temp commit

* 10587 temp commit

* 10587 fix migrations

* 10587 pagination

* 10587 pagination

* 10587 pagination

* 10587 review changes
2024-03-07 11:41:34 -05:00
Arthur 005a339745 9856 temp checkin 2024-03-07 07:39:15 -08:00
Arthur 4784829477 9856 core test sans DjangoModelType 2024-03-06 15:48:02 -08:00
Arthur 28ac66b0fb 9856 circuits test sans DjangoModelType 2024-03-06 15:44:40 -08:00
Arthur 7c66a6aba8 9856 test fixes 2024-03-06 15:25:06 -08:00
Arthur 77eb1018e8 9856 test fixes 2024-03-06 15:00:48 -08:00
Arthur 5ff2c1806d 9856 test fixes 2024-03-06 14:34:51 -08:00
Arthur 1052ea5dd4 9856 test fix vpn 2024-03-06 14:00:53 -08:00
Jeremy Stretch 7567c9d281 Changelog for #12795, #15277, #15292 2024-03-06 16:47:38 -05:00
Arthur 0ca46e349f 9856 test fixes 2024-03-06 13:43:40 -08:00
Jeremy Stretch 7c63d0c500
Merge pull request #15327 from netbox-community/15277-object-types
Closes #15277: Standardize names & model for ContentType ForeignKeys
2024-03-06 16:32:47 -05:00
Arthur 888d9ecec6 9856 test fixes 2024-03-06 13:27:01 -08:00
Jeremy Stretch e5ee8523ef Misc cleanup 2024-03-06 16:16:29 -05:00
Jeremy Stretch 0c22e38006 Re-enable error handling in middleware (disabled for testing) 2024-03-06 16:16:29 -05:00
Jeremy Stretch 40a654b21e Use singular names for M2M field filters 2024-03-06 15:43:33 -05:00
Jeremy Stretch 5552f2a7dd Rename ContentTypes REST API endpoint & resources 2024-03-06 15:30:59 -05:00
Arthur a5aad5359d 9856 test fixes 2024-03-06 10:35:44 -08:00
Arthur aa7c00ec32 9856 circuits graphql tests working 2024-03-06 10:21:23 -08:00
Arthur 2f719269e8 9856 single item query for schema 2024-03-06 09:53:46 -08:00
Arthur 7c289aebc7 9856 fix OrganizationalObjectType 2024-03-06 07:52:42 -08:00
Arthur 0312ec3249 Merge branch 'feature' into 9856-strawberry-2 2024-03-06 07:43:16 -08:00
Arthur f8748011f3 9856 GraphQLView 2024-03-05 16:27:34 -08:00
Arthur 14f04453bb 9856 GraphQLView 2024-03-05 08:30:34 -08:00
Jeremy Stretch 6f6d483ca5 Merge branch 'feature' into 15277-object-types 2024-03-05 08:52:07 -05:00
Jeremy Stretch 9000e125e3
Merge pull request #15281 from netbox-community/15278-primary-nested-serializers
Closes #15278: Use primary serializers when representing nested objects
2024-03-05 08:22:54 -05:00
Arthur 13bf2c1940 9856 merge feature 2024-03-04 14:18:58 -08:00
Jeremy Stretch cd74e040c1 Merge branch 'feature' into 15278-primary-nested-serializers 2024-03-04 16:42:36 -05:00
Jeremy Stretch 239d21870b
Closes #14871: Complete work on UI cleanup (#15341)
* Fix left padding of login button in top menu

* Relocate "add" buttons for embedded object tables

* Remove unused data template block & getNetboxData() utility function

* Remove bottom margin from last <p> element in rendered Markdown inside a table cell

* Prevent TomSelect from initializing on <select> elements with a size

* Fix styling of dropdown menu button for circuit commit rate

* Change .color-block to display: inline-block

* Delete unused static asset

* Improve contrast between menu group headings & items

* Remove custom color for attr-table row headings

* Fix border color of copy-to-clipboard button

* Fix toast text color in dark mode

* Fix rack elevation label/image toggles

* Increase border radius for small buttons

* Fix object selector
2024-03-04 15:55:01 -05:00
Jeremy Stretch 4533c8dae0 Rename sequences for ObjectType M2M tables 2024-03-04 12:17:32 -05:00
Jeremy Stretch 0e89f46601 #15277: Clean up references to object types in templates 2024-03-04 11:49:39 -05:00
Jeremy Stretch 0419a69ae8 Clean up outdated references to ContentType 2024-03-04 10:46:34 -05:00
Jeremy Stretch d538010069 Add GraphQL type for ObjectType 2024-03-04 10:06:28 -05:00
Jeremy Stretch 01ee9c87b8 Update ObjectPermission.object_types to reference ObjectType 2024-03-04 09:52:45 -05:00
Jeremy Stretch 570f64784f Update Tag.object_types to reference ObjectType 2024-03-04 09:51:32 -05:00
Jeremy Stretch 5f43eabab1 Rename ContactAssignment.content_type to object_type 2024-03-04 08:33:44 -05:00
Jeremy Stretch e0165539b3 Rename ImageAttachment.content_type to object_type 2024-03-04 08:33:44 -05:00
Jeremy Stretch ce6b2666a9 Rename SavedFilter.content_types to object_types & use ObjectType proxy 2024-03-04 08:33:44 -05:00
Jeremy Stretch bef17e5a95 Rename ExportTemplate.content_types to object_types & use ObjectType proxy 2024-03-04 08:33:44 -05:00
Jeremy Stretch e51d71d7e6 Rename EventRule.content_types to object_types & use ObjectType proxy 2024-03-04 08:33:44 -05:00
Jeremy Stretch ba514aceac Rename CustomLink.content_types to object_types & use ObjectType proxy 2024-03-04 08:33:44 -05:00
Jeremy Stretch 54b9d1b3f2 Disconnect search backend during test to avoid discrepancy with ContentTypes on transaction rollback 2024-03-04 08:33:44 -05:00
Jeremy Stretch aeeec284a5 Rename CustomField.content_types to object_types & use ObjectType proxy 2024-03-04 08:33:44 -05:00
Jeremy Stretch 0df68bf291 Rename ContentType proxy model to ObjectType 2024-03-04 08:33:44 -05:00
Jeremy Stretch c6a3fc2407
#12795: Introduce a custom Group model (#15304)
* Rename sequences & indexes after renaming users table

* Migrate from auth.Group to a custom group model

* Delete original groups from auth_group table

* Update object & multi-object custom fields referencing the Group model

* Fix ContentType resolution

* Clean up obsolete logic for view/serializer resolution
2024-03-04 08:29:53 -05:00
Jeremy Stretch 709eac6b98 Closes #15292: Remove obsolete device_role attribute from Device model 2024-03-01 11:31:47 -05:00
Jeremy Stretch 3bd28e2efe Improve serializer initialization performance 2024-03-01 10:18:58 -05:00
Jeremy Stretch 125a493dc6 Changelog for #14438, #15042, #15087, #15131, #15238 2024-02-29 11:37:23 -05:00
Jeremy Stretch 7008ffe6d8 Rename 'requested_fields' kwarg to 'fields' on BaseSerializer 2024-02-27 17:01:29 -05:00
Jeremy Stretch 78e284c14f Initialize dynamically-resolved serializers with nested=True 2024-02-27 14:56:36 -05:00
Jeremy Stretch ca56c8b9ef Add dynamic nesting support to SerializedPKRelatedField 2024-02-27 14:00:42 -05:00
Jeremy Stretch c382ba0ae0 Refactor REST API serializers to avoid circular imports 2024-02-27 13:26:43 -05:00
Arthur 497de46ad9 9856 update types 2024-02-26 14:13:44 -08:00
Jeremy Stretch c146f5e1b5 Replace nested serializers with primary serializers where possible 2024-02-26 16:49:30 -05:00
Arthur 44f4d60f5d 9856 update types 2024-02-26 12:57:53 -08:00
Arthur 3e284c59d8 9856 update types 2024-02-26 11:26:50 -08:00
Arthur 4d0d19bb76 9856 update types 2024-02-26 11:18:03 -08:00
Arthur 0387cb0a48 9856 update types 2024-02-26 11:04:29 -08:00
Arthur 82c08d9820 9856 update types 2024-02-26 09:46:03 -08:00
Jeremy Stretch d042e6f69d Closes #15238: Include description field in brief mode 2024-02-26 11:39:31 -05:00
Arthur ce003b2b1c 9856 update types 2024-02-26 08:20:32 -08:00
Arthur 6bb9d68f60 9856 update types 2024-02-23 15:56:21 -08:00
Arthur 69134dbb50 9856 update types 2024-02-23 14:36:41 -08:00
Jeremy Stretch 3f3bcc5eb5
Closes #15235: Use primary serializers for REST API "brief" mode (#15246)
* Use primary serializers for brief mode

* Remove BriefModeMixin

* Correct ModuleBayTest brief_fields (see #15243)
2024-02-23 14:21:25 -05:00
Arthur Hanson ca2ee436a0
Closes #14438: Database representation of scripts
- Introduces the Script model to represent individual Python classes within a ScriptModule file
- Automatically migrates jobs & event rules

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-02-23 08:27:37 -05:00
Arthur a5445bb61a 9856 _name to types 2024-02-22 16:56:54 -08:00
Arthur d4812b28fd 9856 type updates 2024-02-22 16:35:24 -08:00
Jeremy Stretch 7e7e5d5eb0 #14917: Nullify maxOptions for static select fields 2024-02-22 15:49:41 -05:00
Jeremy Stretch 3ddacf4b88 Merge branch 'develop' into feature 2024-02-21 16:24:23 -05:00
Jeremy Stretch 5f159795dd
Closes #15042: Move model registration logic to AppConfigs (#15203)
* Closes #15042: Move model registration logic to AppConfigs

* Refactor register_model() to accept multiple models
2024-02-21 14:22:13 -05:00
Arthur 3c24cf97b6 Merge branch 'feature' into 9856-strawberry-2 2024-02-15 16:04:45 -08:00
Arthur a0d0ab1e78 9856 misc fixes 2024-02-15 16:03:47 -08:00
Arthur ff03abf23e 9856 _name 2024-02-15 15:33:51 -08:00
Arthur c3cbefc625 9856 GFK working 2024-02-15 14:54:29 -08:00
Arthur 1aa5b0d5a1 9856 GFK working 2024-02-15 14:54:04 -08:00
Jeremy Stretch 7abb2b2ab5
Closes #15131: Dynamic queryset annotations for REST API endpoints (#15152)
* Introduce RelatedObjectCountField

* Introduce get_annotations_for_serializer() and enable dynamic annotations

* Add RelatedObjectCountFields to serializers; remove static annotations from querysets

* Remove annotations cleanup logic from BriefModeMixin

* Annotate type for RelatedObjectCountField

* Remove redundant field on TagSerializer

* Add missing reverse relationship for power feeds to rack

* Refactor RelatedObjectCountField to take a single relationship name
2024-02-15 14:49:27 -05:00
Arthur d37414d69a 9856 update types 2024-02-15 11:06:41 -08:00
Arthur cc5703c9dd Merge branch 'feature' into 9856-strawberry-2 2024-02-14 10:43:21 -08:00
Jeremy Stretch b3f25a400b
Closes #15087: Support for specifying a subset of API serializer fields (#15122)
* Enable dynamic field inclusion for REST API serializers

* Recurse through nested serializer when resolving prefetches

* Remove obsolete calls to prefetch_related() for API views

* Remove support for brief_prefetch_fields viewset attribute

* Rename query parameter

* Fixes #15133: Fix FHRP group representation on assignments endpoint under brief mode (#15134)

* Fixes #15133: Fix FHRP group representation on assignments endpoint under brief mode

* Update API test

* Restore get_queryset() on BriefModeMixin, minus prefetch logic

* get_prefetches_for_serializer() should reference serializer field source if set
2024-02-14 09:28:37 -05:00
Jeremy Stretch 72720354df Update v4.0 release notes 2024-02-13 16:43:51 -05:00
Jeremy Stretch 20824ceb25
Closes #13283: Add context to dropdown options (#15104)
* Initial work on #13283

* Enable passing TomSelect HTML template attibutes on DynamicModelChoiceField

* Merge disabled_indicator into option_attrs

* Add support for annotating a numeric count on dropdown options

* Annotate parent object on relevant fields

* Improve rendering of color options

* Improve rendering of color options

* Rename option_attrs to context

* Expose option context on ObjectVar for custom scripts

* Document dropdown context variables
2024-02-13 16:31:17 -05:00
Arthur 4fab68a138 9856 fixes to circuits type specifiers 2024-02-13 10:28:26 -08:00
Arthur 99b01981d4 9856 fixes to circuits type specifiers 2024-02-13 08:32:21 -08:00
Arthur eca0966d92 9856 cleanup 2024-02-12 13:05:57 -08:00
Arthur 7779e87ff3 9856 cleanup 2024-02-12 13:01:27 -08:00
Arthur a171a02844 Merge branch 'feature' into 9856-strawberry-2 2024-02-12 11:24:10 -08:00
Jeremy Stretch f41105d5e3 Remove unused Javascript 2024-02-09 16:30:14 -05:00
Jeremy Stretch 25723aebe0 Closes #15100: Remove obsolete NullableCharField class 2024-02-09 15:57:42 -05:00
Jeremy Stretch d8c6dad9d9 Closes #15099: Remove legacy device_role & device_role_id filters for devices 2024-02-09 15:55:03 -05:00
Jeremy Stretch 64039a8833 #14917: Remove obsolete slim-select styling 2024-02-09 14:51:21 -05:00
Jeremy Stretch d63e1dacbf
Closes #14917: Replace slim-select with tom-select (#15080)
* Experimenting

* Remove testing resources

* Replace ApiSelect with TomSelect

* Add color support

* Add clear button

* Clear cached options when searching dynamic selects

* Add support for static parameters

* Refactor TomSelect implementation

* Add dynamic parameter support

* Limit number of options to 100

* Remove redundant api_url definitions for user model

* Add support for disabled indicator

* Remove obsolete value-field attr on dynamic select widgets

* Remove obsolete fetch_trigger kwarg from dynamic model choice widgets

* Remove obsolete empty_label kwarg from dynamic model choice widgets

* Add support for API path variables

* Add support for setting a 'null' option

* Annotate depth for recursive hierarchies

* Misc cleanup

* Remove obsolete APISelect code

* Remove slim-select & just-debounce-it

* Clean up type validation

* Closes #14237: Clear child selections on change to parent selection

* Use an MD icon for the clear button

* Use an MD icon for the clear button

* Explain why noUnusedParameters is disabled
2024-02-08 15:07:04 -05:00
Arthur 6d7678f017 9856 fix old decorator 2024-02-07 16:45:13 -08:00
Arthur 48b0cdd04a 9856 virtualization, vpn, wireless schema 2024-02-07 16:07:15 -08:00
Arthur d3fc026b5d 9856 ipam and tenant schema 2024-02-07 15:43:29 -08:00
Arthur ed1e1ae939 9856 extras schema 2024-02-07 09:03:22 -08:00
Arthur Hanson 11697d19a6
12510 Merge Scripts and Reports (#14976)
* 12510 move reports to use BaseScript

* 12510 merge report into script view

* 12510 add migration for job report to script

* 12510 update templates

* 12510 remove reports

* 12510 cleanup

* 12510 legacy jobs

* 12510 legacy jobs

* 12510 fixes

* 12510 review changes

* 12510 review changes

* 12510 update docs

* 12510 review changes

* 12510 review changes

* 12510 review changes

* 12510 review changes

* 12510 main log results to empty string

* 12510 move migration

* Introduce an internal log level for debug to simplify Script logging

* Misc cleanup

* Remove obsolete is_valid() method

* Reformat script job data (log, output, tests)

* Remove ScriptLogMessageSerializer

* Fix formatting of script logs

* Record a timestamp with script logs

* Rename _current_method to _current_test

* Clean up template

* Remove obsolete runreport management command

* Misc cleanup & refactoring

* Clean up template

* Clean up migration

* Clean up docs

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-02-07 12:02:09 -05:00
Arthur 460b57dbf7 9856 dcim schema 2024-02-07 07:32:10 -08:00
Arthur cdcaa9055e 9856 core schema 2024-02-06 14:52:29 -08:00
Arthur fb4d63f8a2 9856 update strawberry types 2024-02-06 14:02:55 -08:00
Arthur 663af64ec1 9856 update strawberry types 2024-02-06 13:52:41 -08:00
Arthur 5e40ebd331 9856 merge feature 2024-02-06 11:07:38 -08:00
Jeremy Stretch f63d23872f Update release notes 2024-02-05 15:00:20 -05:00
Jeremy Stretch 74e67afa41 Merge branch 'develop' into feature 2024-02-05 14:38:26 -05:00
Arthur Hanson 317bef6796
12795 custom user model (#15005)
* 12795 users.User migration

* 12795 users.User migration

* 12795 review changes

* 12795 fix user model registration

* 12795 fix user model registration

* 12795 update migration

* 12795 update migration

* 12795 update migration

* 12795 add comment to migration db_table

* Tweak import to avoid class name collision

* 12795 add comment for _register_features requirement

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-02-05 13:24:03 -05:00
Jeremy Stretch 5d9311eecf
Closes #12325: Disable the Django admin UI by default (#15008)
* Disable the Django admin UI by default

* Remove outdated references to the admin UI

* Update tests
2024-02-05 11:44:52 -05:00
Arthur Hanson 93b77cb4f0
14729 Move background tasks list from admin UI to Primary UI (#14825)
* 14729 rq table

* 14729 rq table

* 14729 rq table

* 14729 rq table

* 14729 jobs table

* 14729 jobs detail

* 14729 formatting fixup

* 14729 formatting fixup

* 14729 format datetime in tables

* 14729 display job id

* Update templates for #12128

* 14729 review fixes

* 14729 review fixes

* 14729 review fixes

* 14729 review fixes

* 14729 merge feature

* 14729 add modal

* 14729 review changes

* 14729 url fixup

* 14729 no queue param on task

* 14729 queue pages

* 14729 job status handling

* 14729 worker list

* 14729 exec detail and common view

* 14729 worker detail

* 14729 background task delete

* 14729 background task delete

* 14729 background task requeue

* 14729 background task enqueue stop

* 14729 review changes

* 14729 remove rq from admin

* 14729 add tests

* 14729 add tests

* Clean up HTML templates

* Clean up tables

* Clean up views

* Fix tests

* Clean up tests

* Move navigation menu entry for background tasks

* Remove custom deletion form

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-02-01 11:44:07 -05:00
Jeremy Stretch 694a7d243a Relocate "add" buttons for embedded object tables 2024-01-31 17:59:14 -05:00
Jeremy Stretch d302982a88 Fix left padding of login button in top menu
Clean up spacing for nav pills

Markdown fields should default to using monospace font

Wrap action buttons in object page header

Fix page link style for non-HTMX paginators

Clean up styling of Markdown preview widget

Fix spacing around placeholder text for empty panel tables

Remove obsolete templates

Tweak checkbox input spacing

Fix toggling of clear button for quick search

Fix positioning of quick search filter dropdown

Fix positioning of 'highlight device' button

Fix styling for custom field group names

Widen buttons on nav menu items

Restyle the login page

Fix active nav-pill background color in dark mode

Fix spacing around 'map' button for sites
2024-01-31 17:59:14 -05:00
Jeremy Stretch 780ce77aed Closes #12776: Utilize the htmx_table tag for all embedded object tables 2024-01-30 17:17:08 -05:00
Jeremy Stretch b554e70eda Squash migrations 2024-01-25 12:35:04 -05:00
Jeremy Stretch 222388b988 Revert "Closes #13647: Squash all migrations prior to v3.7 (#14853)"
This reverts commit 874685fd6f.
2024-01-22 13:15:43 -05:00
Jeremy Stretch 1d41a8ace5
Closes #14735: Implement django-htmx (#14873)
* Install django-htmx

* Replace is_htmx() function with request.htmx

* Remove is_embedded() HTMX utility

* Include django-htmx debug error handler
2024-01-22 12:09:15 -05:00
Jeremy Stretch da085e60c2
Closes #14740: Remove BootstrapMixin (#14841)
* Introduce custom form widget templates to apply CSS classes

* Apply both mandatory and optional CSS classes to form widgets

* Omit required & placeholder attrs

* Move annotation of field validation failures to CSS

* Remove BootstrapMixin class

* Remove obsolete ComponentTemplateImportForm class

* Remove obsolete custom forms for login & password change

* Clean up obsolete accommodations for 'required' widget attr
2024-01-19 14:02:33 -05:00
Jeremy Stretch 874685fd6f
Closes #13647: Squash all migrations prior to v3.7 (#14853)
* Regenerate pre-v3.7 migrations

* Annotate replaced migrations

* Rename dependencies; remove FeatureQuery references

* Add missed replacement
2024-01-19 13:55:22 -05:00
Arthur Hanson ef5e10d360
14728 Move installed plugins list from admin UI to NetBox UI (#14768)
* 14728 move plugins view from admin

* 14728 move plugins view from admin

* 14728 remove plugins view from admin

* Update template for #12128

* 14728 review fixes

* 14728 review fixes

* 14728 review fixes

* 14728 review fixes

* 14728 configure table

* Clean up table columns

* Fix app config lookup for plugins referenced by dotted path

* Move template; fix table display

* Fix user table configuration

* Remove nonfunctional quick search

* Limit PluginListView to staff users

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-01-19 11:27:15 -05:00
Jeremy Stretch 073c2dc8ca
Closes #12128: Refresh the web UI to employ the Tabler CSS framework (#14833)
* Remove dark mode styling

* Condense & rename light mode stylesheet

* Upgrade to Bootstrap 5.3.2

* Swap out Bootstrap for Tabler; remove custom styling

* Update base page layout for Tabler

* Update login page

* Bump node to v18

* Update button styles

* Update object list view

* Tweak navbar size

* Clean up dashboard widgets

* Ditch separate stylesheet for print media

* Remove simplebar

* Remove obsolete sidebar styling

* Clean up object view template

* Clean up object edit template

* Standardize primary button sizing

* Clean up object list styling

* Add buttons for add & import to navigation menu

* Fix global search bar

* Fix slim-select form widget styling

* Fix toast styling

* Set base fonts

* Clean up paginator styling

* Clean up navigation menu group headings

* Clean up footer links

* Clean up card styles

* Move SVG styles to a designated directory

* Restructure SCSS files

* Remove obsolete/redundant dependencies

* Fix icon spacing

* Update background color classes

* Tweak banner & footer styling and spacing

* Fix badge background colors in table content

* Bump @types/bootstrap to 5.2.10

* Clean up form layouts

* Fix object selector button style

* Fix icon padding inside small buttons

* Fix icon & badge spacing inside buttons and tabs

* Hide paginator for empty pages

* Fix hover color for list items (Tabler bug #1694)

* Fix width of checkbox column in empty tables

* Clean up bulk edit template

* Fix border color of reslug button

* Package & serve Google fonts locally

* Fix tab styling

* Reduce vetical space at top of dashboard

* Remove obsolete content-wrapper template block

* Fix icon spacing in dropdown menu items

* Fix color label sizing

* Separate bulk delete form & object list into tabs

* Fix styling of filter group headings

* Fix styling for object changelog & journal views

* Standardize ordering & styling of action buttons

* Fix designation of active menu item

* Automatically expand menu section containing the active link

* Clean up nav menu styling

* Remove button colors; hide buttons except on hover/active

* Highlight menu group containing the active item

* Update & standardize alert styling

* Refactor base templates to ensure consistent display of header content

* Tweak styling for links inside badges

* Clean up top menu

* Fix JSON/YAML toggles for config context data

* Fix object template header

* Constrain tabs to container-xl; tweak header margins

* Fix object identifier styling

* Fix positioning of card header buttons

* Remove padding from HTMX tables inside cards

* Ensure consistent use of row headings in attribute tables

* Remove padding surrounding tables inside cards

* Remove obsolete CSS classes

* Misc cleanup of old styling

* Refactor 'controls' template block; ditch old classes

* Fix login button sizing

* Limit object edit form width

* Append asterisk to required form field labels

* Remove obsolete styling

* Remove obsolete styling

* Fix position of progress bar outside label

* Fix alignment of delete button in report/script lists

* Fix <pre> styling

* Clean up page headers

* Replace SVG icons with Material Design icons

* Restore dark mode togle functionality

* Fix top navbar background color under dark mode

* Rebuild static assets
2024-01-17 16:25:42 -05:00
Arthur Hanson 8254e707b6
12851 replace bleach with nh3 (#14767)
* 12851 replace bleach with nh3

* Move tags & attributes lists to constants.py

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-01-11 09:31:32 -05:00
Arthur 2d4fbea8ff 9856 compatability fixes 2024-01-10 12:46:55 -08:00
Jeremy Stretch f8199339f5 Changelog for #14092, #14637, #14638, #14657, #14658, #14672 2024-01-10 14:33:01 -05:00
Arthur 28c48b3d6c 9856 compatability fixes 2024-01-09 14:23:10 -08:00
Arthur Hanson 58227293f3
14637 update to Django 5 (#14675)
* 14637 update to Django 5

* 14637 fix tests

* 14637 remove extra assignment

* Syntax tweak

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-01-05 13:30:04 -05:00
Jeremy Stretch 8e20581da7 PEP8 cleanup 2024-01-05 13:08:37 -05:00
Jeremy Stretch 1f865af559 Closes #14672: Add support for Python 3.12 2024-01-05 13:08:37 -05:00
Jeremy Stretch fb7d483dbe #14638: Check for Python 3.10 or later 2024-01-04 09:47:55 -05:00
Arthur 7a222a6501 9856 update imports 2024-01-03 17:20:13 -08:00
Arthur de2d9edbd4 9856 update strawberry version 2024-01-03 15:35:59 -08:00
Arthur dbe4097a78 Merge branch 'feature' into 9856-strawberry-2 2024-01-03 15:33:08 -08:00
Jeremy Stretch 7bedf48a97 Closes #14638: Drop support for Python 3.8 and 3.9 2024-01-03 13:33:00 -05:00
Arthur 34d9b78d19 9856 merge feature 2024-01-03 10:18:29 -08:00
Jeremy Stretch cd7d038fc0 Closes #14657: Remove backward compatibility for old permissions mapping under ActionsMixin 2024-01-03 12:01:10 -05:00
Jeremy Stretch a5fa30e851 Closes #14092: Remove backward compatibility for importing extras.plugins 2024-01-03 12:00:44 -05:00
Jeremy Stretch f09658b9c3 Closes #14658: Remove backward compatibility for process_webhook() 2024-01-03 12:00:20 -05:00
Jeremy Stretch 2d19c5068f #14132: Annotate WebhooksMixin renaming under breaking changes 2024-01-03 10:57:29 -05:00
Arthur 36f57f8f08 9856 fk 2023-08-25 13:52:24 -07:00
Arthur 46b0df43f9 9856 extras and mixins 2023-08-25 13:31:10 -07:00
Arthur 700f015942 9856 user and circuits base 2023-08-24 14:54:24 -07:00
Arthur eceac90b1c 9856 user and group 2023-08-24 09:11:23 -07:00
Arthur 3f2c21f005 9856 base strawberry integration 2023-08-22 16:39:04 -07:00
1151 changed files with 229597 additions and 58478 deletions

View File

@ -26,7 +26,7 @@ body:
attributes:
label: NetBox Version
description: What version of NetBox are you currently running?
placeholder: v3.7.7
placeholder: v4.0.0
validations:
required: true
- type: dropdown
@ -34,10 +34,9 @@ body:
label: Python Version
description: What version of Python are you currently running?
options:
- "3.8"
- "3.9"
- "3.10"
- "3.11"
- "3.12"
validations:
required: true
- type: textarea

View File

@ -14,7 +14,7 @@ body:
attributes:
label: NetBox version
description: What version of NetBox are you currently running?
placeholder: v3.7.7
placeholder: v4.0.0
validations:
required: true
- type: dropdown

View File

@ -9,8 +9,8 @@ jobs:
NETBOX_CONFIGURATION: netbox.configuration_testing
strategy:
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11']
node-version: ['14.x']
python-version: ['3.10', '3.11', '3.12']
node-version: ['18.x']
services:
redis:
image: redis

View File

@ -1,8 +1,8 @@
version: 2
build:
os: ubuntu-20.04
os: ubuntu-22.04
tools:
python: "3.9"
python: "3.12"
mkdocs:
configuration: mkdocs.yml
python:

View File

@ -1,10 +1,6 @@
# HTML sanitizer
# https://github.com/mozilla/bleach/blob/main/CHANGES
bleach
# The Python web framework on which NetBox is built
# https://docs.djangoproject.com/en/stable/releases/
Django<5.0
Django<5.1
# Django middleware which permits cross-domain API requests
# https://github.com/adamchainz/django-cors-headers/blob/main/CHANGELOG.rst
@ -18,14 +14,13 @@ django-debug-toolbar
# https://github.com/carltongibson/django-filter/blob/main/CHANGES.rst
django-filter
# Django debug toolbar extension with support for GraphiQL
# https://github.com/flavors/django-graphiql-debug-toolbar/blob/main/CHANGES.rst
django-graphiql-debug-toolbar
# HTMX utilities for Django
# https://django-htmx.readthedocs.io/en/latest/changelog.html
django-htmx
# Modified Preorder Tree Traversal (recursive nesting of objects)
# Pinned to 0.14.0; 0.15.0 requires Python 3.9+
# https://github.com/django-mptt/django-mptt/blob/main/CHANGELOG.rst
django-mptt==0.14.0
django-mptt
# Context managers for PostgreSQL advisory locks
# https://github.com/Xof/django-pglocks/blob/master/CHANGES.txt
@ -61,8 +56,7 @@ django-timezone-field
# A REST API framework for Django projects
# https://www.django-rest-framework.org/community/release-notes/
# Pinned to 3.14 for NetBox v3.7
djangorestframework<3.15
djangorestframework
# Sane and flexible OpenAPI 3 schema generation for Django REST framework.
# https://github.com/tfranzel/drf-spectacular/blob/master/CHANGELOG.rst
@ -76,11 +70,6 @@ drf-spectacular-sidecar
# https://github.com/kurtmckee/feedparser/blob/develop/CHANGELOG.rst
feedparser
# Django wrapper for Graphene (GraphQL support)
# https://github.com/graphql-python/graphene-django/releases
# Pinned to v3.0.0 for GraphiQL UI issue (see #12762)
graphene_django==3.0.0
# WSGI HTTP server
# https://docs.gunicorn.org/en/latest/news.html
gunicorn
@ -109,6 +98,10 @@ mkdocstrings[python-legacy]
# https://github.com/netaddr/netaddr/blob/master/CHANGELOG.rst
netaddr
# Python bindings to the ammonia HTML sanitization library.
# https://github.com/messense/nh3
nh3
# Fork of PIL (Python Imaging Library) for image processing
# https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst
Pillow
@ -133,8 +126,16 @@ social-auth-core
# https://github.com/python-social-auth/social-app-django/blob/master/CHANGELOG.md
social-auth-app-django
# Strawberry GraphQL
# https://github.com/strawberry-graphql/strawberry/blob/main/CHANGELOG.md
strawberry-graphql
# Strawberry GraphQL Django extension
# https://github.com/strawberry-graphql/strawberry-django/blob/main/CHANGELOG.md
strawberry-graphql-django
# SVG image rendering (used for rack elevations)
# hhttps://github.com/mozman/svgwrite/blob/master/NEWS.rst
# https://github.com/mozman/svgwrite/blob/master/NEWS.rst
svgwrite
# Tabular dataset library (for table-based exports)

View File

@ -12,8 +12,12 @@ Group=netbox
PIDFile=/var/tmp/netbox.pid
WorkingDirectory=/opt/netbox
# Remove the following line if using uWSGI instead of Gunicorn
ExecStart=/opt/netbox/venv/bin/gunicorn --pid /var/tmp/netbox.pid --pythonpath /opt/netbox/netbox --config /opt/netbox/gunicorn.py netbox.wsgi
# Uncomment the following line if using uWSGI instead of Gunicorn
#ExecStart=/opt/netbox/venv/bin/uwsgi --ini /opt/netbox/uwsgi.ini
Restart=on-failure
RestartSec=30
PrivateTmp=true

View File

@ -14,10 +14,20 @@ server {
}
location / {
# Remove these lines if using uWSGI instead of Gunicorn
proxy_pass http://127.0.0.1:8001;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
# Uncomment these lines if using uWSGI instead of Gunicorn
# include uwsgi_params;
# uwsgi_pass 127.0.0.1:8001;
# uwsgi_param Host $host;
# uwsgi_param X-Real-IP $remote_addr;
# uwsgi_param X-Forwarded-For $proxy_add_x_forwarded_for;
# uwsgi_param X-Forwarded-Proto $http_x_forwarded_proto;
}
}

18
contrib/uwsgi.ini Normal file
View File

@ -0,0 +1,18 @@
[uwsgi]
; bind to the specified UNIX/TCP socket and port (usually localhost)
socket = 127.0.0.1:8001
; fail to start if any parameter in the configuration file isnt explicitly understood by uWSGI.
strict = true
; re-spawn and pre-fork workers
master = true
; clear environment on exit
vacuum = true
; exit if no app can be loaded
need-app = true
; do not use multiple interpreters
single-interpreter = true

View File

@ -73,7 +73,7 @@ You should be redirected to Microsoft's authentication portal. Enter the usernam
If successful, you will be redirected back to the NetBox UI, and will be logged in as the AD user. You can verify this by navigating to your profile (using the button at top right).
This user account has been replicated locally to NetBox, and can now be assigned groups and permissions by navigating to Admin > Permissions.
This user account has been replicated locally to NetBox, and can now be assigned groups and permissions.
## Troubleshooting

View File

@ -67,4 +67,4 @@ You should be redirected to Okta's authentication portal. Enter the username/ema
If successful, you will be redirected back to the NetBox UI, and will be logged in as the Okta user. You can verify this by navigating to your profile (using the button at top right).
This user account has been replicated locally to NetBox, and can now be assigned groups and permissions by navigating to Admin > Permissions.
This user account has been replicated locally to NetBox, and can now be assigned groups and permissions.

View File

@ -4,7 +4,7 @@
Local user accounts and groups can be created in NetBox under the "Authentication" section in the "Admin" menu. This section is available only to users with the "staff" permission enabled.
At a minimum, each user account must have a username and password set. User accounts may also denote a first name, last name, and email address. [Permissions](../permissions.md) may also be assigned to users and/or groups under Admin > Permissions.
At a minimum, each user account must have a username and password set. User accounts may also denote a first name, last name, and email address. [Permissions](../permissions.md) may also be assigned to individual users and/or groups as needed.
## Remote Authentication

View File

@ -70,8 +70,6 @@ The `$user` token can be used only as a constraint value, or as an item within a
### Default Permissions
!!! info "This feature was introduced in NetBox v3.6."
While permissions are typically assigned to specific groups and/or users, it is also possible to define a set of default permissions that are applied to _all_ authenticated users. This is done using the [`DEFAULT_PERMISSIONS`](../configuration/security.md#default_permissions) configuration parameter. Note that statically configuring permissions for specific users or groups is **not** supported.
### Example Constraint Definitions

View File

@ -1,23 +0,0 @@
# Date & Time Parameters
## TIME_ZONE
Default: UTC
The time zone NetBox will use when dealing with dates and times. It is recommended to use UTC time unless you have a specific need to use a local time zone. Please see the [list of available time zones](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).
## Date and Time Formatting
You may define custom formatting for date and times. For detailed instructions on writing format strings, please see [the Django documentation](https://docs.djangoproject.com/en/stable/ref/templates/builtins/#date). Default formats are listed below.
!!! note
These system defaults will be overridden by a user's selected language/locale when [localization](./system.md#enable_localization) is enabled.
```python
DATE_FORMAT = 'N j, Y' # June 26, 2016
SHORT_DATE_FORMAT = 'Y-m-d' # 2016-06-26
TIME_FORMAT = 'g:i a' # 1:23 p.m.
SHORT_TIME_FORMAT = 'H:i:s' # 13:23:00
DATETIME_FORMAT = 'N j, Y g:i a' # June 26, 2016 1:23 p.m.
SHORT_DATETIME_FORMAT = 'Y-m-d H:i' # 2016-06-26 13:23
```

View File

@ -33,9 +33,6 @@ This defines custom content to be displayed on the login page above the login fo
!!! tip "Dynamic Configuration Parameter"
!!! note
This parameter was added in NetBox v3.5.
This adds a banner to the top of every page when maintenance mode is enabled. HTML is allowed.
---
@ -99,6 +96,14 @@ The maximum size (in bytes) of an incoming HTTP request (i.e. `GET` or `POST` da
---
## DJANGO_ADMIN_ENABLED
Default: False
Setting this to True installs the `django.contrib.admin` app and enables the [Django admin UI](https://docs.djangoproject.com/en/5.0/ref/contrib/admin/). This may be necessary to support older plugins which do not integrate with the native NetBox interface.
---
## ENFORCE_GLOBAL_UNIQUE
!!! tip "Dynamic Configuration Parameter"
@ -107,9 +112,6 @@ Default: True
By default, NetBox will prevent the creation of duplicate prefixes and IP addresses in the global table (that is, those which are not assigned to any VRF). This validation can be disabled by setting `ENFORCE_GLOBAL_UNIQUE` to False.
!!! info "Changed in v3.7"
The default value for this parameter was changed from False to True in NetBox v3.7.
---
## FILE_UPLOAD_MAX_MEMORY_SIZE
@ -134,9 +136,6 @@ Setting this to False will disable the GraphQL API.
!!! tip "Dynamic Configuration Parameter"
!!! note
This parameter was renamed from `JOBRESULT_RETENTION` in NetBox v3.5.
Default: 90
The number of days to retain job results (scripts and reports). Set this to `0` to retain job results in the database indefinitely.
@ -231,9 +230,6 @@ The maximum execution time of a background task (such as running a custom script
## RQ_RETRY_INTERVAL
!!! note
This parameter was added in NetBox v3.5.
Default: `60`
This parameter controls how frequently a failed job is retried, up to the maximum number of times specified by `RQ_RETRY_MAX`. This must be either an integer specifying the number of seconds to wait between successive attempts, or a list of such values. For example, `[60, 300, 3600]` will retry the task after 1 minute, 5 minutes, and 1 hour.
@ -242,9 +238,6 @@ This parameter controls how frequently a failed job is retried, up to the maximu
## RQ_RETRY_MAX
!!! note
This parameter was added in NetBox v3.5.
Default: `0` (retries disabled)
The maximum number of times a background task will be retried before being marked as failed.

View File

@ -92,8 +92,6 @@ CSRF_TRUSTED_ORIGINS = (
## DEFAULT_PERMISSIONS
!!! info "This parameter was introduced in NetBox v3.6."
Default:
```python

View File

@ -62,14 +62,6 @@ Email is sent from NetBox only for critical events or if configured for [logging
---
## ENABLE_LOCALIZATION
Default: False
Determines if localization features are enabled or not. This should only be enabled for development or testing purposes as netbox is not yet fully localized. Turning this on will localize numeric and date formats (overriding any configured [system defaults](./date-time.md#date-and-time-formatting)) based on the browser locale as well as translate certain strings from third party modules.
---
## HTTP_PROXIES
Default: None
@ -200,3 +192,9 @@ A dictionary of configuration parameters for the storage backend configured as `
If `STORAGE_BACKEND` is not defined, this setting will be ignored.
---
## TIME_ZONE
Default: UTC
The time zone NetBox will use when dealing with dates and times. It is recommended to use UTC time unless you have a specific need to use a local time zone. Please see the [list of available time zones](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).

View File

@ -42,8 +42,6 @@ This parameter has no effect on the API representation of custom field data.
### Visibility & Editing
!!! info "This feature was improved in NetBox v3.7."
When creating a custom field, users can control the conditions under which it may be displayed and edited within the NetBox user interface. The following choices are available for controlling the display of a custom field on an object:
* **Always** (default): The custom field is included when viewing an object.

View File

@ -5,8 +5,17 @@ Custom scripting was introduced to provide a way for users to execute custom log
* Automatically populate new devices and cables in preparation for a new site deployment
* Create a range of new reserved prefixes or IP addresses
* Fetch data from an external source and import it to NetBox
* Update objects with invalid or incomplete data
Custom scripts are Python code and exist outside of the official NetBox code base, so they can be updated and changed without interfering with the core NetBox installation. And because they're completely custom, there is no inherent limitation on what a script can accomplish.
They can also be used as a mechanism for validating the integrity of data within NetBox. Script authors can define test to check object against specific rules and conditions. For example, you can write script to check that:
* All top-of-rack switches have a console connection
* Every router has a loopback interface with an IP address assigned
* Each interface description conforms to a standard format
* Every site has a minimum set of VLANs defined
* All IP addresses have a parent prefix
Custom scripts are Python code which exists outside the NetBox code base, so they can be updated and changed without interfering with the core NetBox installation. And because they're completely custom, there is no inherent limitation on what a script can accomplish.
## Writing Custom Scripts
@ -135,13 +144,73 @@ These two methods will load data in YAML or JSON format, respectively, from file
The Script object provides a set of convenient functions for recording messages at different severity levels:
* `log_debug`
* `log_success`
* `log_info`
* `log_warning`
* `log_failure`
* `log_debug(message, object=None)`
* `log_success(message, object=None)`
* `log_info(message, object=None)`
* `log_warning(message, object=None)`
* `log_failure(message, object=None)`
Log messages are returned to the user upon execution of the script. Markdown rendering is supported for log messages.
Log messages are returned to the user upon execution of the script. Markdown rendering is supported for log messages. A message may optionally be associated with a particular object by passing it as the second argument to the logging method.
## Test Methods
A script can define one or more test methods to report on certain conditions. All test methods must have a name beginning with `test_` and accept no arguments beyond `self`.
These methods are detected and run automatically when the script is executed, unless its `run()` method has been overridden. (When overriding `run()`, `run_tests()` can be called to run all test methods present in the script.)
!!! info
This functionality was ported from [legacy reports](./reports.md) in NetBox v4.0.
### Example
```
from dcim.choices import DeviceStatusChoices
from dcim.models import ConsolePort, Device, PowerPort
from extras.scripts import Script
class DeviceConnectionsReport(Script):
description = "Validate the minimum physical connections for each device"
def test_console_connection(self):
# Check that every console port for every active device has a connection defined.
active = DeviceStatusChoices.STATUS_ACTIVE
for console_port in ConsolePort.objects.prefetch_related('device').filter(device__status=active):
if not console_port.connected_endpoints:
self.log_failure(
f"No console connection defined for {console_port.name}",
console_port.device,
)
elif not console_port.connection_status:
self.log_warning(
f"Console connection for {console_port.name} marked as planned",
console_port.device,
)
else:
self.log_success("Passed", console_port.device)
def test_power_connections(self):
# Check that every active device has at least two connected power supplies.
for device in Device.objects.filter(status=DeviceStatusChoices.STATUS_ACTIVE):
connected_ports = 0
for power_port in PowerPort.objects.filter(device=device):
if power_port.connected_endpoints:
connected_ports += 1
if not power_port.path.is_active:
self.log_warning(
f"Power connection for {power_port.name} marked as planned",
device,
)
if connected_ports < 2:
self.log_failure(
f"{connected_ports} connected power supplies found (2 needed)",
device,
)
else:
self.log_success("Passed", device)
```
## Change Logging
@ -235,6 +304,7 @@ A particular object within NetBox. Each ObjectVar must specify a particular mode
* `model` - The model class
* `query_params` - A dictionary of query parameters to use when retrieving available options (optional)
* `context` - A custom dictionary mapping template context variables to fields, used when rendering `<option>` elements within the dropdown menu (optional; see below)
* `null_option` - A label representing a "null" or empty choice (optional)
To limit the selections available within the list, additional query parameters can be passed as the `query_params` dictionary. For example, to show only devices with an "active" status:
@ -262,6 +332,22 @@ site = ObjectVar(
)
```
#### Context Variables
Custom context variables can be passed to override the default attribute names or to display additional information, such as a parent object.
| Name | Default | Description |
|---------------|-----------------|------------------------------------------------------------------------------|
| `value` | `"id"` | The attribute which contains the option's value |
| `label` | `"display"` | The attribute used as the option's human-friendly label |
| `description` | `"description"` | The attribute to use as a description |
| `depth`[^1] | `"_depth"` | The attribute which indicates an object's depth within a recursive hierarchy |
| `disabled` | -- | The attribute which, if true, signifies that the option should be disabled |
| `parent` | -- | The attribute which represents the object's parent object |
| `count`[^1] | -- | The attribute which contains a numeric count of related objects |
[^1]: The value of this attribute must be a positive integer
### MultiObjectVar
Similar to `ObjectVar`, but allows for the selection of multiple objects.
@ -398,7 +484,7 @@ class NewBranchScript(Script):
name=f'{site.slug}-switch{i}',
site=site,
status=DeviceStatusChoices.STATUS_PLANNED,
device_role=switch_role
role=switch_role
)
switch.full_clean()
switch.save()

View File

@ -4,7 +4,7 @@ NetBox validates every object prior to it being written to the database to ensur
## Custom Validation Rules
Custom validation rules are expressed as a mapping of model attributes to a set of rules to which that attribute must conform. For example:
Custom validation rules are expressed as a mapping of object attributes to a set of rules to which that attribute must conform. For example:
```json
{
@ -17,6 +17,8 @@ Custom validation rules are expressed as a mapping of model attributes to a set
This defines a custom validator which checks that the length of the `name` attribute for an object is at least five characters long, and no longer than 30 characters. This validation is executed _after_ NetBox has performed its own internal validation.
### Validation Types
The `CustomValidator` class supports several validation types:
* `min`: Minimum value
@ -36,14 +38,14 @@ The `min` and `max` types should be defined for numeric values, whereas `min_len
### Custom Validation Logic
There may be instances where the provided validation types are insufficient. NetBox provides a `CustomValidator` class which can be extended to enforce arbitrary validation logic by overriding its `validate()` method, and calling `fail()` when an unsatisfactory condition is detected.
There may be instances where the provided validation types are insufficient. NetBox provides a `CustomValidator` class which can be extended to enforce arbitrary validation logic by overriding its `validate()` method, and calling `fail()` when an unsatisfactory condition is detected. The `validate()` method should accept an instance (the object being saved) as well as the current request effecting the change.
```python
from extras.validators import CustomValidator
class MyValidator(CustomValidator):
def validate(self, instance):
def validate(self, instance, request):
if instance.status == 'active' and not instance.description:
self.fail("Active sites must have a description set!", field='status')
```
@ -82,7 +84,42 @@ CUSTOM_VALIDATORS = {
}
```
### Dotted Path
#### Referencing Related Object Attributes
!!! info "This feature was introduced in NetBox v4.0."
The attributes of a related object can be referenced by specifying a dotted path. For example, to reference the name of a region to which a site is assigned, use `region.name`:
```python
CUSTOM_VALIDATORS = {
"dcim.site": [
{
"region.name": {
"neq": "New York"
}
}
]
}
```
#### Validating Request Parameters
!!! info "This feature was introduced in NetBox v4.0."
In addition to validating object attributes, custom validators can also match against parameters of the current request (where available). For example, the following rule will permit only the user named "admin" to modify an object:
```json
{
"request.user.username": {
"eq": "admin"
}
}
```
!!! tip
Custom validation should generally not be used to enforce permissions. NetBox provides a robust [object-based permissions](../administration/permissions.md) mechanism which should be used for this purpose.
### Dotted Path to Class
In instances where a custom validator class is needed, it can be referenced by its Python path (relative to NetBox's working directory):

View File

@ -1,167 +1,63 @@
# NetBox Reports
A NetBox report is a mechanism for validating the integrity of data within NetBox. Running a report allows the user to verify that the objects defined within NetBox meet certain arbitrary conditions. For example, you can write reports to check that:
* All top-of-rack switches have a console connection
* Every router has a loopback interface with an IP address assigned
* Each interface description conforms to a standard format
* Every site has a minimum set of VLANs defined
* All IP addresses have a parent prefix
...and so on. Reports are completely customizable, so there's practically no limit to what you can test for.
## Writing Reports
Reports must be saved as files in the [`REPORTS_ROOT`](../configuration/system.md#reports_root) path (which defaults to `netbox/reports/`). Each file created within this path is considered a separate module. Each module holds one or more reports (Python classes), each of which performs a certain function. The logic of each report is broken into discrete test methods, each of which applies a small portion of the logic comprising the overall test.
!!! warning
The reports path includes a file named `__init__.py`, which registers the path as a Python module. Do not delete this file.
Reports are deprecated beginning with NetBox v4.0, and their functionality has been merged with [custom scripts](./custom-scripts.md). While backward compatibility has been maintained, users are advised to convert legacy reports into custom scripts soon, as support for legacy reports will be removed in a future release.
For example, we can create a module named `devices.py` to hold all of our reports which pertain to devices in NetBox. Within that module, we might define several reports. Each report is defined as a Python class inheriting from `extras.reports.Report`.
## Converting Reports to Scripts
```
### Step 1: Update Class Definition
Change the parent class from `Report` to `Script`:
```python title="Old code"
from extras.reports import Report
class DeviceConnectionsReport(Report):
description = "Validate the minimum physical connections for each device"
class DeviceIPsReport(Report):
description = "Check that every device has a primary IP address assigned"
class MyReport(Report):
```
Within each report class, we'll create a number of test methods to execute our report's logic. In DeviceConnectionsReport, for instance, we want to ensure that every live device has a console connection, an out-of-band management connection, and two power connections.
```python title="New code"
from extras.scripts import Script
```
from dcim.choices import DeviceStatusChoices
from dcim.models import ConsolePort, Device, PowerPort
from extras.reports import Report
class DeviceConnectionsReport(Report):
description = "Validate the minimum physical connections for each device"
def test_console_connection(self):
# Check that every console port for every active device has a connection defined.
active = DeviceStatusChoices.STATUS_ACTIVE
for console_port in ConsolePort.objects.prefetch_related('device').filter(device__status=active):
if not console_port.connected_endpoints:
self.log_failure(
console_port.device,
"No console connection defined for {}".format(console_port.name)
)
elif not console_port.connection_status:
self.log_warning(
console_port.device,
"Console connection for {} marked as planned".format(console_port.name)
)
else:
self.log_success(console_port.device)
def test_power_connections(self):
# Check that every active device has at least two connected power supplies.
for device in Device.objects.filter(status=DeviceStatusChoices.STATUS_ACTIVE):
connected_ports = 0
for power_port in PowerPort.objects.filter(device=device):
if power_port.connected_endpoints:
connected_ports += 1
if not power_port.path.is_active:
self.log_warning(
device,
"Power connection for {} marked as planned".format(power_port.name)
)
if connected_ports < 2:
self.log_failure(
device,
"{} connected power supplies found (2 needed)".format(connected_ports)
)
else:
self.log_success(device)
class MyReport(Script):
```
As you can see, reports are completely customizable. Validation logic can be as simple or as complex as needed. Also note that the `description` attribute support markdown syntax. It will be rendered in the report list page.
### Step 2: Update Logging Calls
!!! warning
Reports should never alter data: If you find yourself using the `create()`, `save()`, `update()`, or `delete()` methods on objects within reports, stop and re-evaluate what you're trying to accomplish. Note that there are no safeguards against the accidental alteration or destruction of data.
Reports and scripts both provide logging methods, however their signatures differ. All script logging methods accept a message as the first parameter, and accept an object as an optional second parameter.
## Report Attributes
Additionally, the Report class' generic `log()` method is **not** available on Script. Users are advised to replace calls of this method with `log_info()`.
### `description`
Use the table below as a reference when updating these methods.
A human-friendly description of what your report does.
| Report (old) | Script (New) |
|-------------------------------|-----------------------------|
| `log(message)` | `log_info(message)` |
| `log_debug(obj, message)`[^1] | `log_debug(message, obj)` |
| `log_info(obj, message)` | `log_info(message, obj)` |
| `log_success(obj, message)` | `log_success(message, obj)` |
| `log_warning(obj, message)` | `log_warning(message, obj)` |
| `log_failure(obj, message)` | `log_failure(message, obj)` |
### `scheduling_enabled`
[^1]: `log_debug()` was added to the Report class in v4.0 to avoid confusion with the same method on Script
By default, a report can be scheduled for execution at a later time. Setting `scheduling_enabled` to False disables this ability: Only immediate execution will be possible. (This also disables the ability to set a recurring execution interval.)
### `job_timeout`
Set the maximum allowed runtime for the report. If not set, `RQ_DEFAULT_TIMEOUT` will be used.
## Logging
The following methods are available to log results within a report:
* log(message)
* log_success(object, message=None)
* log_info(object, message)
* log_warning(object, message)
* log_failure(object, message)
The recording of one or more failure messages will automatically flag a report as failed. It is advised to log a success for each object that is evaluated so that the results will reflect how many objects are being reported on. (The inclusion of a log message is optional for successes.) Messages recorded with `log()` will appear in a report's results but are not associated with a particular object or status. Log messages also support using markdown syntax and will be rendered on the report result page.
To perform additional tasks, such as sending an email or calling a webhook, before or after a report is run, extend the `pre_run()` and/or `post_run()` methods, respectively.
By default, reports within a module are ordered alphabetically in the reports list page. To return reports in a specific order, you can define the `report_order` variable at the end of your module. The `report_order` variable is a tuple which contains each Report class in the desired order. Any reports that are omitted from this list will be listed last.
```
from extras.reports import Report
class DeviceConnectionsReport(Report)
pass
class DeviceIPsReport(Report)
pass
report_order = (DeviceIPsReport, DeviceConnectionsReport)
```python title="Old code"
self.log_failure(
console_port.device,
f"No console connection defined for {console_port.name}"
)
```
Once you have created a report, it will appear in the reports list. Initially, reports will have no results associated with them. To generate results, run the report.
## Running Reports
!!! note
To run a report, a user must be assigned permissions for `Extras > Report`, `Extras > Report Module`, and `Core > Managed File` objects. They must also be assigned the `extras.run_report` permission. This is achieved by assigning the user (or group) a permission on the Report object and specifying the `run` action in "Permissions" as shown below.
![Adding the run action to a permission](../media/run_permission.png)
### Via the Web UI
Reports can be run via the web UI by navigating to the report and clicking the "run report" button at top right. Once a report has been run, its associated results will be included in the report view. It is possible to schedule a report to be executed at specified time in the future. A scheduled report can be canceled by deleting the associated job result object.
### Via the API
To run a report via the API, simply issue a POST request to its `run` endpoint. Reports are identified by their module and class name.
```
POST /api/extras/reports/<module>.<name>/run/
```python title="New code"
self.log_failure(
f"No console connection defined for {console_port.name}",
obj=console_port.device,
)
```
Our example report above would be called as:
### Other Notes
```
POST /api/extras/reports/devices.DeviceConnectionsReport/run/
```
Existing reports will be converted to scripts automatically upon upgrading to NetBox v4.0, and previous job history will be retained. However, users are advised to convert legacy reports into custom scripts at the earliest opportunity, as support for legacy reports will be removed in a future release.
Optionally `schedule_at` can be passed in the form data with a datetime string to schedule a script at the specified date and time.
The `pre_run()` and `post_run()` Report methods have been carried over to Script. These are called automatically by Script's `run()` method. (Note that if you opt to override this method, you are responsible for calling `pre_run()` and `post_run()` where applicable.)
### Via the CLI
Reports can be run on the CLI by invoking the management command:
```
python3 manage.py runreport <module>
```
where ``<module>`` is the name of the python file in the ``reports`` directory without the ``.py`` extension. One or more report modules may be specified.
The `is_valid()` method on Report is no longer needed and has been removed.

View File

@ -7,7 +7,7 @@ Getting started with NetBox development is pretty straightforward, and should fe
* A Linux system or compatible environment
* A PostgreSQL server, which can be installed locally [per the documentation](../installation/1-postgresql.md)
* A Redis server, which can also be [installed locally](../installation/2-redis.md)
* Python 3.8 or later
* Python 3.10 or later
### 1. Fork the Repo

View File

@ -62,10 +62,11 @@ class Circuit(PrimaryModel):
1. Import `gettext_lazy` as `_`.
2. All form fields must specify a `label` wrapped with `gettext_lazy()`.
3. All headers under a form's `fieldsets` property must be wrapped with `gettext_lazy()`.
3. The name of each FieldSet on a form must be wrapped with `gettext_lazy()`.
```python
from django.utils.translation import gettext_lazy as _
from utilities.forms.rendering import FieldSet
class CircuitBulkEditForm(NetBoxModelBulkEditForm):
description = forms.CharField(
@ -74,7 +75,7 @@ class CircuitBulkEditForm(NetBoxModelBulkEditForm):
)
fieldsets = (
(_('Circuit'), ('provider', 'type', 'status', 'description')),
FieldSet('provider', 'type', 'status', 'description', name=_('Circuit')),
)
```

View File

@ -59,7 +59,7 @@ Notify the [`netbox-docker`](https://github.com/netbox-community/netbox-docker)
* Increases in minimum versions for service dependencies (PostgreSQL, Redis, etc.)
* Any changes to the reference installation
### Update Requirements
### Update Python Dependencies
Before each release, update each of NetBox's Python dependencies to its most recent stable version. These are defined in `requirements.txt`, which is updated from `base_requirements.txt` using `pip`. To do this:
@ -70,6 +70,10 @@ Before each release, update each of NetBox's Python dependencies to its most rec
In cases where upgrading a dependency to its most recent release is breaking, it should be constrained to its current minor version in `base_requirements.txt` with an explanatory comment and revisited for the next major NetBox release (see the [Address Constrained Dependencies](#address-constrained-dependencies) section above).
### Update UI Dependencies
Check whether any UI dependencies (JavaScript packages, fonts, etc.) need to be updated by running `yarn outdated` from within the `project-static/` directory. [Upgrade these dependencies](http://0.0.0.0:9000/development/web-ui/#updating-dependencies) as necessary, then run `yarn bundle` to generate the necessary files for distribution.
### Rebuild the Device Type Definition Schema
Run the following command to update the device type definition validation schema:

View File

@ -11,4 +11,3 @@ The `users.UserConfig` model holds individual preferences for each user in the f
| pagination.placement | Where to display the paginator controls relative to the table |
| tables.${table}.columns | The ordered list of columns to display when viewing the table |
| tables.${table}.ordering | A list of column names by which the table should be ordered |
| ui.colormode | Light or dark mode in the user interface |

View File

@ -1,25 +1,37 @@
# Web UI Development
## Code Structure
Most static resources for the NetBox UI are housed within the `netbox/project-static/` directory.
| Path | Description |
|-----------|----------------------------------------------------|
| `dist/` | Destination path for installed dependencies |
| `docs/` | Local build path for documentation |
| `img/` | Image files |
| `js/` | Miscellaneous JavaScript resources served directly |
| `src/` | TypeScript resources (to be compiled into JS) |
| `styles/` | Sass resources (to be compiled into CSS) |
## Front End Technologies
The NetBox UI is built on languages and frameworks:
Front end scripting is written in [TypeScript](https://www.typescriptlang.org/), which is a strongly-typed extension to JavaScript. TypeScript is "transpiled" into JavaScript resources which are served to and executed by the client web browser.
### Styling & HTML Elements
All UI styling is written in [Sass](https://sass-lang.com/), which is an extension to browser-native [Cascading Stylesheets (CSS)](https://developer.mozilla.org/en-US/docs/Web/CSS). Similar to how TypeScript content is transpiled to JavaScript, Sass resources (`.scss` files) are compiled to CSS.
#### [Bootstrap](https://getbootstrap.com/) 5
## Dependencies
The majority of the NetBox UI is made up of stock Bootstrap components, with some styling modifications and custom components added on an as-needed basis. Bootstrap uses [Sass](https://sass-lang.com/), and NetBox extends Bootstrap's core Sass files for theming and customization.
The following software is employed by the NetBox user interface.
### Client-side Scripting
#### [TypeScript](https://www.typescriptlang.org/)
All client-side scripting is transpiled from TypeScript to JavaScript and served by Django. In development, TypeScript is an _extremely_ effective tool for accurately describing and checking the code, which leads to significantly fewer bugs, a better development experience, and more predictable/readable code.
As part of the [bundling](#bundling) process, Bootstrap's JavaScript plugins are imported and bundled alongside NetBox's front-end code.
!!! danger "NetBox is jQuery-free"
Following the Bootstrap team's deprecation of jQuery in Bootstrap 5, NetBox also no longer uses jQuery in front-end code.
* [Bootstrap 5](https://getbootstrap.com/) - A popular CSS & JS framework
* [clipboard.js](https://clipboardjs.com/) - A lightweight package for enabling copy-to-clipboard functionality
* [flatpickr](https://flatpickr.js.org/) - A lightweight date & time selection widget
* [gridstack.js](https://gridstackjs.com/) - Enables interactive grid layouts (for the dashboard)
* [HTMX](https://htmx.org/) - Enables dynamic web interfaces through the use of HTML element attributes
* [Material Design Icons](https://pictogrammers.com/library/mdi/) - An extensive open source collection of graphical icons, delivered as a web font
* [query-string](https://www.npmjs.com/package/query-string) - Assists with parsing URL query strings
* [Tabler](https://tabler.io/) - A web application UI toolkit & theme based on Bootstrap 5
* [Tom Select](https://tom-select.js.org/) - Provides dynamic selection form fields
## Guidance
@ -54,6 +66,41 @@ $ yarn
!!! warning "Check Your Working Directory"
You need to be in the `netbox/project-static` directory to run the below `yarn` commands.
### Updating Dependencies
Run `yarn outdated` to identify outdated dependencies.
```
$ yarn outdated
yarn outdated v1.22.19
info Color legend :
"<red>" : Major Update backward-incompatible updates
"<yellow>" : Minor Update backward-compatible features
"<green>" : Patch Update backward-compatible bug fixes
Package Current Wanted Latest Workspace Package Type URL
bootstrap 5.3.1 5.3.1 5.3.3 netbox dependencies https://getbootstrap.com/
```
Run `yarn upgrade --latest` to automatically upgrade these packages to their most recent versions.
```
$ yarn upgrade bootstrap --latest
yarn upgrade v1.22.19
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Rebuilding all packages...
success Saved lockfile.
success Saved 1 new dependency.
info Direct dependencies
└─ bootstrap@5.3.3
info All dependencies
└─ bootstrap@5.3.3
Done in 0.95s.
```
`package.json` will be updated to reflect the new package versions automatically.
### Bundling
In order for the TypeScript and Sass (SCSS) source files to be usable by a browser, they must first be transpiled (TypeScript → JavaScript, Sass → CSS), bundled, and minified. After making changes to TypeScript or Sass source files, run `yarn bundle`.

View File

@ -20,8 +20,6 @@ GET /api/dcim/devices/?tag=monitored&tag=deprecated
## Bookmarks
!!! info "This feature was introduced in NetBox v3.6."
Users can bookmark their most commonly visited objects for convenient access. Bookmarks are listed under a user's profile, and can be displayed with custom filtering and ordering on the user's personal dashboard.
## Custom Fields

View File

@ -28,4 +28,4 @@ For more detail, see the reference documentation for NetBox's [conditional logic
## Event Rule Processing
When a change is detected, any resulting events are placed into a Redis queue for processing. This allows the user's request to complete without needing to wait for the outgoing event(s) to be processed. The events are then extracted from the queue by the `rqworker` process. The current event queue and any failed events can be inspected in the admin UI under System > Background Tasks.
When a change is detected, any resulting events are placed into a Redis queue for processing. This allows the user's request to complete without needing to wait for the outgoing event(s) to be processed. The events are then extracted from the queue by the `rqworker` process. The current event queue and any failed events can be inspected under System > Background Tasks.

View File

@ -6,8 +6,8 @@ This section of the documentation discusses installing and configuring the NetBo
Begin by installing all system packages required by NetBox and its dependencies.
!!! warning "Python 3.8 or later required"
NetBox requires Python 3.8, 3.9, 3.10 or 3.11.
!!! warning "Python 3.10 or later required"
NetBox supports Python 3.10, 3.11, and 3.12.
=== "Ubuntu"
@ -21,7 +21,7 @@ Begin by installing all system packages required by NetBox and its dependencies.
sudo yum install -y gcc libxml2-devel libxslt-devel libffi-devel libpq-devel openssl-devel redhat-rpm-config
```
Before continuing, check that your installed Python version is at least 3.8:
Before continuing, check that your installed Python version is at least 3.10:
```no-highlight
python3 -V
@ -255,10 +255,10 @@ Once NetBox has been configured, we're ready to proceed with the actual installa
sudo /opt/netbox/upgrade.sh
```
Note that **Python 3.8 or later is required** for NetBox v3.2 and later releases. If the default Python installation on your server is set to a lesser version, pass the path to the supported installation as an environment variable named `PYTHON`. (Note that the environment variable must be passed _after_ the `sudo` command.)
Note that **Python 3.10 or later is required** for NetBox v4.0 and later releases. If the default Python installation on your server is set to a lesser version, pass the path to the supported installation as an environment variable named `PYTHON`. (Note that the environment variable must be passed _after_ the `sudo` command.)
```no-highlight
sudo PYTHON=/usr/bin/python3.8 /opt/netbox/upgrade.sh
sudo PYTHON=/usr/bin/python3.10 /opt/netbox/upgrade.sh
```
!!! note

View File

@ -1,10 +1,13 @@
# Gunicorn
Like most Django applications, NetBox runs as a [WSGI application](https://en.wikipedia.org/wiki/Web_Server_Gateway_Interface) behind an HTTP server. This documentation shows how to install and configure [gunicorn](http://gunicorn.org/) (which is automatically installed with NetBox) for this role, however other WSGI servers are available and should work similarly well. [uWSGI](https://uwsgi-docs.readthedocs.io/en/latest/) is a popular alternative.
!!! tip
This page provides instructions for setting up the [gunicorn](http://gunicorn.org/) WSGI server. If you plan to use [uWSGI](https://uwsgi-docs.readthedocs.io/en/latest/) instead, go [here](./4b-uwsgi.md).
NetBox runs as a [WSGI application](https://en.wikipedia.org/wiki/Web_Server_Gateway_Interface) behind an HTTP server. This documentation shows how to install and configure [gunicorn](http://gunicorn.org/) (which is automatically installed with NetBox) for this role, however other WSGI servers are available and should work similarly well.
## Configuration
NetBox ships with a default configuration file for gunicorn. To use it, copy `/opt/netbox/contrib/gunicorn.py` to `/opt/netbox/gunicorn.py`. (We make a copy of this file rather than pointing to it directly to ensure that any local changes to it do not get overwritten by a future upgrade.)
NetBox ships with a default configuration file for gunicorn. To use it, copy `/opt/netbox/contrib/gunicorn.py` to `/opt/netbox/gunicorn.py`. (We make a copy of this file rather than pointing to it directly to ensure that any local changes to it do not get overwritten during a future NetBox upgrade.)
```no-highlight
sudo cp /opt/netbox/contrib/gunicorn.py /opt/netbox/gunicorn.py

View File

@ -0,0 +1,104 @@
# uWSGI
!!! tip
This page provides instructions for setting up the [uWSGI](https://uwsgi-docs.readthedocs.io/) WSGI server. If you plan to use [gunicorn](http://gunicorn.org/) instead, go [here](./4a-gunicorn.md).
NetBox runs as a [WSGI application](https://en.wikipedia.org/wiki/Web_Server_Gateway_Interface) behind an HTTP server. This documentation shows how to install and configure [uWSGI](https://uwsgi-docs.readthedocs.io/en/latest/) for this role, however other WSGI servers are available and should work similarly well.
## Installation
Activate the Python virtual environment and install the `pyuwsgi` package using pip:
```no-highlight
source /opt/netbox/venv/bin/activate
pip3 install pyuwsgi
```
Once installed, add the package to `local_requirements.txt` to ensure it is re-installed during future rebuilds of the virtual environment:
```no-highlight
sudo sh -c "echo 'pyuwgsi' >> /opt/netbox/local_requirements.txt"
```
## Configuration
NetBox ships with a default configuration file for uWSGI. To use it, copy `/opt/netbox/contrib/uwsgi.ini` to `/opt/netbox/uwsgi.ini`. (We make a copy of this file rather than pointing to it directly to ensure that any local changes to it do not get overwritten during a future NetBox upgrade.)
```no-highlight
sudo cp /opt/netbox/contrib/uwsgi.ini /opt/netbox/uwsgi.ini
```
While the provided configuration should suffice for most initial installations, you may wish to edit this file to change the bound IP address and/or port number, or to make performance-related adjustments. See [the uWSGI documentation](https://uwsgi-docs-additions.readthedocs.io/en/latest/Options.html) for the available configuration parameters and take a minute to review the [Things to know](https://uwsgi-docs.readthedocs.io/en/latest/ThingsToKnow.html) page. Django also provides [additional documentation](https://docs.djangoproject.com/en/5.0/howto/deployment/wsgi/uwsgi/) on configuring uWSGI with a Django app.
## systemd Setup
We'll use systemd to control both uWSGI and NetBox's background worker process. First, copy `contrib/netbox.service` and `contrib/netbox-rq.service` to the `/etc/systemd/system/` directory.
```no-highlight
sudo cp -v /opt/netbox/contrib/*.service /etc/systemd/system/
sudo systemctl daemon-reload
```
The reference configuration assumes that gunicorn is in use, so we need to update it. Edit the `netbox.service` file to remove the line beginning with `ExecStart=/opt/netbox/venv/bin/gunicorn` and uncomment the line below it.
!!! warning "Check user & group assignment"
The stock service configuration files packaged with NetBox assume that the service will run with the `netbox` user and group names. If these differ on your installation, be sure to update the service files accordingly.
Once the configuration file has been saved, reload the service:
```no-highlight
sudo systemctl daemon-reload
```
Then, start the `netbox` and `netbox-rq` services and enable them to initiate at boot time:
```no-highlight
sudo systemctl enable --now netbox netbox-rq
```
You can use the command `systemctl status netbox` to verify that the WSGI service is running:
```no-highlight
systemctl status netbox.service
```
You should see output similar to the following:
```no-highlight
● netbox.service - NetBox WSGI Service
Loaded: loaded (/etc/systemd/system/netbox.service; enabled; vendor preset: enabled)
Active: active (running) since Mon 2021-08-30 04:02:36 UTC; 14h ago
Docs: https://docs.netbox.dev/
Main PID: 1140492 (uwsgi)
Tasks: 19 (limit: 4683)
Memory: 666.2M
CGroup: /system.slice/netbox.service
├─1061 /opt/netbox/venv/bin/python3 /opt/netbox/venv/bin/uwsgi --ini /opt/netbox/uwsgi.ini
├─1976 /opt/netbox/venv/bin/python3 /opt/netbox/venv/bin/uwsgi --ini /opt/netbox/uwsgi.ini
...
```
!!! note
If the NetBox service fails to start, issue the command `journalctl -eu netbox` to check for log messages that may indicate the problem.
Once you've verified that the WSGI workers are up and running, move on to HTTP server setup.
## HTTP Server Installation
For server installation, you will want to follow the NetBox [HTTP Server Setup](5-http-server.md) guide, however after copying the configuration file, you will need to edit the file and change the `location` section to uncomment the uWSGI parameters:
```no-highlight
location / {
# proxy_pass http://127.0.0.1:8001;
# proxy_set_header X-Forwarded-Host $http_host;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-Proto $scheme;
# comment the lines above and uncomment the lines below if using uWSGI
include uwsgi_params;
uwsgi_pass 127.0.0.1:8001;
uwsgi_param Host $host;
uwsgi_param X-Real-IP $remote_addr;
uwsgi_param X-Forwarded-For $proxy_add_x_forwarded_for;
uwsgi_param X-Forwarded-Proto $http_x_forwarded_proto;
}
```

View File

@ -35,6 +35,9 @@ Once nginx is installed, copy the nginx configuration file provided by NetBox to
sudo cp /opt/netbox/contrib/nginx.conf /etc/nginx/sites-available/netbox
```
!!! tip "gunicorn vs. uWSGI"
The reference nginx configuration file assumes that gunicorn is in use. If using uWSGI instead, you'll need to remove the gunicorn-specific configuration (lines beginning with `proxy_pass` and `proxy_set_header`) and uncomment the uWSGI section below them before proceeding.
Then, delete `/etc/nginx/sites-enabled/default` and create a symlink in the `sites-enabled` directory to the configuration file you just created.
```no-highlight

View File

@ -12,17 +12,17 @@ The following sections detail how to set up a new instance of NetBox:
1. [PostgreSQL database](1-postgresql.md)
1. [Redis](2-redis.md)
3. [NetBox components](3-netbox.md)
4. [Gunicorn](4-gunicorn.md)
4. [Gunicorn](4a-gunicorn.md) or [uWSGI](4b-uwsgi.md)
5. [HTTP server](5-http-server.md)
6. [LDAP authentication](6-ldap.md) (optional)
## Requirements
| Dependency | Minimum Version |
|------------|-----------------|
| Python | 3.8 |
| PostgreSQL | 12 |
| Redis | 4.0 |
| Dependency | Supported Versions |
|------------|--------------------|
| Python | 3.10, 3.11, 3.12 |
| PostgreSQL | 12+ |
| Redis | 4.0+ |
Below is a simplified overview of the NetBox application stack for reference:

View File

@ -17,11 +17,11 @@ Prior to upgrading your NetBox instance, be sure to carefully review all [releas
NetBox requires the following dependencies:
| Dependency | Minimum Version |
|------------|-----------------|
| Python | 3.8 |
| PostgreSQL | 12 |
| Redis | 4.0 |
| Dependency | Supported Versions |
|------------|--------------------|
| Python | 3.10, 3.11, 3.12 |
| PostgreSQL | 12+ |
| Redis | 4.0+ |
## 3. Install the Latest Release
@ -108,10 +108,10 @@ sudo ./upgrade.sh
```
!!! warning
If the default version of Python is not at least 3.8, you'll need to pass the path to a supported Python version as an environment variable when calling the upgrade script. For example:
If the default version of Python is not at least 3.10, you'll need to pass the path to a supported Python version as an environment variable when calling the upgrade script. For example:
```no-highlight
sudo PYTHON=/usr/bin/python3.8 ./upgrade.sh
sudo PYTHON=/usr/bin/python3.10 ./upgrade.sh
```
This script performs the following actions:

View File

@ -54,7 +54,11 @@ For more detail on constructing GraphQL queries, see the [Graphene documentation
The GraphQL API employs the same filtering logic as the UI and REST API. Filters can be specified as key-value pairs within parentheses immediately following the query name. For example, the following will return only sites within the North Carolina region with a status of active:
```
{"query": "query {site_list(region:\"north-carolina\", status:\"active\") {name}}"}
query {
site_list(filters: {region: "us-nc", status: "active"}) {
name
}
}
```
In addition, filtering can be done on list of related objects as shown in the following query:
@ -63,7 +67,7 @@ In addition, filtering can be done on list of related objects as shown in the fo
device_list {
id
name
interfaces(enabled: true) {
interfaces(filters: {enabled: true}) {
name
}
}

View File

@ -73,9 +73,9 @@ If no body template is specified, the request body will be populated with a JSON
## Webhook Processing
Using [Event Rules](../features/event-rules.md), when a change is detected, any resulting webhooks are placed into a Redis queue for processing. This allows the user's request to complete without needing to wait for the outgoing webhook(s) to be processed. The webhooks are then extracted from the queue by the `rqworker` process and HTTP requests are sent to their respective destinations. The current webhook queue and any failed webhooks can be inspected in the admin UI under System > Background Tasks.
Using [Event Rules](../features/event-rules.md), when a change is detected, any resulting webhooks are placed into a Redis queue for processing. This allows the user's request to complete without needing to wait for the outgoing webhook(s) to be processed. The webhooks are then extracted from the queue by the `rqworker` process and HTTP requests are sent to their respective destinations. The current webhook queue and any failed webhooks can be inspected under System > Background Tasks.
A request is considered successful if the response has a 2XX status code; otherwise, the request is marked as having failed. Failed requests may be retried manually via the admin UI.
A request is considered successful if the response has a 2XX status code; otherwise, the request is marked as having failed. Failed requests may be requeued manually under System > Background Tasks.
## Troubleshooting

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 207 KiB

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 316 KiB

After

Width:  |  Height:  |  Size: 422 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 309 KiB

After

Width:  |  Height:  |  Size: 433 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 356 KiB

After

Width:  |  Height:  |  Size: 510 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 235 KiB

After

Width:  |  Height:  |  Size: 341 KiB

View File

@ -18,9 +18,9 @@ When a device has one or more interfaces with IP addresses assigned, a primary I
The device's configured name. This field is optional; devices can be unnamed. However, if set, the name must be unique to the assigned site and tenant.
### Device Role
### Role
The functional [role](./devicerole.md) assigned to this device.
The functional [device role](./devicerole.md) assigned to this device.
### Device Type

View File

@ -26,3 +26,7 @@ The location's operational status.
!!! tip
Additional statuses may be defined by setting `Location.status` under the [`FIELD_CHOICES`](../../configuration/data-validation.md#field_choices) configuration parameter.
### Facility
Data center or facility designation for identifying the location.

View File

@ -1,7 +1,5 @@
# Bookmarks
!!! info "This feature was introduced in NetBox v3.6."
A user can bookmark individual objects for convenient access. Bookmarks are listed under a user's profile and can be displayed using a dashboard widget.
## Fields

View File

@ -38,7 +38,7 @@ The type of data this field holds. This must be one of the following:
| Object | A single NetBox object of the type defined by `object_type` |
| Multiple object | One or more NetBox objects of the type defined by `object_type` |
### Object Type
### Related Object Type
For object and multiple-object fields only. Designates the type of NetBox object being referenced.

View File

@ -1,7 +1,5 @@
# Custom Field Choice Sets
!!! info "This feature was introduced in NetBox v3.6."
Single- and multi-selection [custom fields](../../customization/custom-fields.md) must define a set of valid choices from which the user may choose when defining the field value. These choices are defined as sets that may be reused among multiple custom fields.
A choice set must define a base choice set and/or a set of arbitrary extra choices.

View File

@ -18,8 +18,6 @@ The color to use when displaying the tag in the NetBox UI.
### Object Types
!!! info "This feature was introduced in NetBox v3.6."
The assignment of a tag may be limited to a prescribed set of objects. For example, it may be desirable to limit the application of a specific tag to only devices and virtual machines.
If no object types are specified, the tag will be assignable to any type of object.

View File

@ -15,16 +15,18 @@ NetBox provides several base form classes for use by plugins.
This is the base form for creating and editing NetBox models. It extends Django's ModelForm to add support for tags and custom fields.
| Attribute | Description |
|-------------|-------------------------------------------------------------|
| `fieldsets` | A tuple of two-tuples defining the form's layout (optional) |
| Attribute | Description |
|-------------|---------------------------------------------------------------------------------------|
| `fieldsets` | A tuple of `FieldSet` instances which control how form fields are rendered (optional) |
**Example**
```python
from django.utils.translation import gettext_lazy as _
from dcim.models import Site
from netbox.forms import NetBoxModelForm
from utilities.forms.fields import CommentField, DynamicModelChoiceField
from utilities.forms.rendering import FieldSet
from .models import MyModel
class MyModelForm(NetBoxModelForm):
@ -33,8 +35,8 @@ class MyModelForm(NetBoxModelForm):
)
comments = CommentField()
fieldsets = (
('Model Stuff', ('name', 'status', 'site', 'tags')),
('Tenancy', ('tenant_group', 'tenant')),
FieldSet('name', 'status', 'site', 'tags', name=_('Model Stuff')),
FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
)
class Meta:
@ -52,6 +54,7 @@ This form facilitates the bulk import of new objects from CSV, JSON, or YAML dat
**Example**
```python
from django.utils.translation import gettext_lazy as _
from dcim.models import Site
from netbox.forms import NetBoxModelImportForm
from utilities.forms import CSVModelChoiceField
@ -77,16 +80,18 @@ This form facilitates editing multiple objects in bulk. Unlike a model form, thi
| Attribute | Description |
|-------------------|---------------------------------------------------------------------------------------------|
| `model` | The model of object being edited |
| `fieldsets` | A tuple of two-tuples defining the form's layout (optional) |
| `fieldsets` | A tuple of `FieldSet` instances which control how form fields are rendered (optional) |
| `nullable_fields` | A tuple of fields which can be nullified (set to empty) using the bulk edit form (optional) |
**Example**
```python
from django import forms
from django.utils.translation import gettext_lazy as _
from dcim.models import Site
from netbox.forms import NetBoxModelImportForm
from utilities.forms import CommentField, DynamicModelChoiceField
from utilities.forms.rendering import FieldSet
from .models import MyModel, MyModelStatusChoices
@ -106,7 +111,7 @@ class MyModelEditForm(NetBoxModelImportForm):
model = MyModel
fieldsets = (
('Model Stuff', ('name', 'status', 'site')),
FieldSet('name', 'status', 'site', name=_('Model Stuff')),
)
nullable_fields = ('site', 'comments')
```
@ -115,10 +120,10 @@ class MyModelEditForm(NetBoxModelImportForm):
This form class is used to render a form expressly for filtering a list of objects. Its fields should correspond to filters defined on the model's filter set.
| Attribute | Description |
|-------------------|-------------------------------------------------------------|
| `model` | The model of object being edited |
| `fieldsets` | A tuple of two-tuples defining the form's layout (optional) |
| Attribute | Description |
|-------------|---------------------------------------------------------------------------------------|
| `model` | The model of object being edited |
| `fieldsets` | A tuple of `FieldSet` instances which control how form fields are rendered (optional) |
**Example**
@ -206,3 +211,13 @@ In addition to the [form fields provided by Django](https://docs.djangoproject.c
::: utilities.forms.fields.CSVMultipleContentTypeField
options:
members: false
## Form Rendering
::: utilities.forms.rendering.FieldSet
::: utilities.forms.rendering.InlineFields
::: utilities.forms.rendering.TabbedGroups
::: utilities.forms.rendering.ObjectAttribute

View File

@ -8,23 +8,31 @@ A plugin can extend NetBox's GraphQL API by registering its own schema class. By
```python
# graphql.py
import graphene
from netbox.graphql.types import NetBoxObjectType
from netbox.graphql.fields import ObjectField, ObjectListField
from . import filtersets, models
import strawberry
import strawberry_django
class MyModelType(NetBoxObjectType):
from . import models
class Meta:
model = models.MyModel
fields = '__all__'
filterset_class = filtersets.MyModelFilterSet
class MyQuery(graphene.ObjectType):
mymodel = ObjectField(MyModelType)
mymodel_list = ObjectListField(MyModelType)
@strawberry_django.type(
models.MyModel,
fields='__all__',
)
class MyModelType:
pass
schema = MyQuery
@strawberry.type
class MyQuery:
@strawberry.field
def dummymodel(self, id: int) -> DummyModelType:
return None
dummymodel_list: list[DummyModelType] = strawberry_django.field()
schema = [
MyQuery,
]
```
## GraphQL Objects
@ -38,15 +46,3 @@ NetBox provides two object type classes for use by plugins.
::: netbox.graphql.types.NetBoxObjectType
options:
members: false
## GraphQL Fields
NetBox provides two field classes for use by plugins.
::: netbox.graphql.fields.ObjectField
options:
members: false
::: netbox.graphql.fields.ObjectListField
options:
members: false

View File

@ -138,7 +138,7 @@ Any additional apps must be installed within the same Python environment as NetB
## Create setup.py
`setup.py` is the [setup script](https://docs.python.org/3.8/distutils/setupscript.html) used to package and install our plugin once it's finished. The primary function of this script is to call the setuptools library's `setup()` function to create a Python distribution package. We can pass a number of keyword arguments to control the package creation as well as to provide metadata about the plugin. An example `setup.py` is below:
`setup.py` is the [setup script](https://docs.python.org/3.10/distutils/setupscript.html) used to package and install our plugin once it's finished. The primary function of this script is to call the setuptools library's `setup()` function to create a Python distribution package. We can pass a number of keyword arguments to control the package creation as well as to provide metadata about the plugin. An example `setup.py` is below:
```python
from setuptools import find_packages, setup
@ -173,7 +173,7 @@ python3 -m venv ~/.virtualenvs/my_plugin
You can make NetBox available within this environment by creating a path file pointing to its location. This will add NetBox to the Python path upon activation. (Be sure to adjust the command below to specify your actual virtual environment path, Python version, and NetBox installation.)
```shell
echo /opt/netbox/netbox > $VENV/lib/python3.8/site-packages/netbox.pth
echo /opt/netbox/netbox > $VENV/lib/python3.10/site-packages/netbox.pth
```
## Development Installation

View File

@ -0,0 +1,347 @@
# Migrating Your Plugin to NetBox v4.0
This document serves as a handbook for maintainers of plugins that were written prior to the release of NetBox v4.0. It serves to capture all the changes recommended to ensure a plugin is compatible with NetBox v4.0 and later releases.
## General
### Python support
NetBox v4.0 drops support for Python 3.8 and 3.9, and introduces support for Python 3.12. You may need to update your CI/CD processes and/or packaging to reflect this.
### Plugin resources relocated
All plugin Python resources were moved from `extras.plugins` to `netbox.plugins` in NetBox v3.7 (see [#14036](https://github.com/netbox-community/netbox/issues/14036)), and support for importing these resources from their old locations has been removed.
```python title="Old"
from extras.plugins import PluginConfig
```
```python title="New"
from netbox.plugins import PluginConfig
```
### ContentType renamed to ObjectType
NetBox's proxy model for Django's [ContentType model](https://docs.djangoproject.com/en/5.0/ref/contrib/contenttypes/#the-contenttype-model) has been renamed to ObjectType for clarity. In general, plugins should use the ObjectType proxy when referencing content types, as it includes several custom manager methods. The one exception to this is when defining [generic foreign keys](https://docs.djangoproject.com/en/5.0/ref/contrib/contenttypes/#generic-relations): The ForeignKey field used for a GFK should point to Django's native ContentType.
Additionally, plugin maintainers are strongly encouraged to adopt the "object type" terminology for field and filter names wherever feasible to be consistent with NetBox core (however this is not required for compatibility).
```python title="Old"
content_types = models.ManyToManyField(
to='contenttypes.ContentType',
related_name='event_rules'
)
```
```python title="New"
object_types = models.ManyToManyField(
to='core.ObjectType',
related_name='event_rules'
)
```
## Views
### View actions must be dictionaries
The format for declaring view actions & permissions was updated in NetBox v3.7 (see [#13550](https://github.com/netbox-community/netbox/issues/13550)), and NetBox v4.0 drops support for the old format. Views which inherit `ActionsMixin` must declare a single `actions` map.
```python title="Old"
actions = ('add', 'import', 'export', 'bulk_edit', 'bulk_delete')
action_perms = defaultdict(set, **{
'add': {'add'},
'import': {'add'},
'bulk_edit': {'change'},
'bulk_delete': {'delete'},
})
```
```python title="New"
actions = {
'add': {'add'},
'import': {'add'},
'export': set(),
'bulk_edit': {'change'},
'bulk_delete': {'delete'},
}
```
## Forms
### Remove `BootstrapMixin`
The `BootstrapMixin` class is no longer available or needed and can be removed from all forms.
```python title="Old"
from django import forms
from utilities.forms import BootstrapMixin
class MyForm(BootstrapMixin, forms.Form):
```
```python title="New"
from django import forms
class MyForm(forms.Form):
```
### Update Fieldset definitions
NetBox v4.0 introduces [several new classes](./forms.md#form-rendering) for advanced form rendering, including FieldSet. Fieldset definitions on forms should use this new class instead of a tuple or list.
Notably, the name of a fieldset is now optional, and passed as a keyword argument rather than as the first item in the set.
```python title="Old"
from django.utils.translation import gettext_lazy as _
from netbox.forms import NetBoxModelForm
class CircuitForm(NetBoxModelForm):
...
fieldsets = (
(_('Circuit'), ('cid', 'type', 'status', 'description', 'tags')),
(_('Service Parameters'), ('install_date', 'termination_date', 'commit_rate')),
(_('Tenancy'), ('tenant_group', 'tenant')),
)
```
```python title="New"
from django.utils.translation import gettext_lazy as _
from netbox.forms import NetBoxModelForm
from utilities.forms.rendering import FieldSet
class CircuitForm(NetBoxModelForm):
...
fieldsets = (
FieldSet('cid', 'type', 'status', 'description', 'tags', name=_('Circuit')),
FieldSet('install_date', 'termination_date', 'commit_rate', name=_('Service Parameters')),
FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
)
```
## Navigation
### Remove button colors
NetBox no longer applies color to buttons within navigation menu items. Although this functionality is still supported, you might want to remove color from any buttons to ensure consistency with the updated design.
```python title="Old"
PluginMenuButton(
link='myplugin:foo_add',
title='Add a new Foo',
icon_class='mdi mdi-plus-thick',
color=ButtonColorChoices.GREEN
)
```
```python title="New"
PluginMenuButton(
link='myplugin:foo_add',
title='Add a new Foo',
icon_class='mdi mdi-plus-thick'
)
```
## UI Layout
### Renamed template blocks
The following template blocks have been renamed or removed:
| Template | Old name | New name |
|---------------------|-------------------|---------------------------|
| generic/object.html | `header` | `page-header` |
| generic/object.html | `controls` | `control-buttons` |
| base/layout.html | `content-wrapper` | _Removed_ (use `content`) |
### Utilize flex controls
Ditch any legacy "float" controls (e.g. `float-end`) in favor of Bootstrap's new [flex behaviors](https://getbootstrap.com/docs/5.3/utilities/flex/) for controlling the layout and sizing of elements horizontally. For example, the following will align two items against the left and right sides of the parent element:
```html
<div class="d-flex justify-content-between">
<h3>Title text</h3>
<i class="mdi mdi-close"></i>
</div>
```
### Check column offsets
When using [offset columns](https://getbootstrap.com/docs/5.3/layout/columns/#offsetting-columns) (e.g. `class="col-offset-3"`), be sure to also set the column width (e.g. `class="col-9 col-offset-3"`) to avoid horizontal scrolling.
### Tables inside cards
Tables inside cards should be embedded directly, not nested inside a `card-body` element.
```html title="Old"
<div class="card">
<div class="card-body">
<table class="table table-hover attr-table">
...
</table>
</div>
</div>
```
```html title="New"
<div class="card">
<table class="table table-hover attr-table">
...
</table>
</div>
```
### Remove `btn-sm` class from buttons
The `btn-sm` (small) class is no longer typically needed on general-purpose buttons.
```html title="Old"
<a href="#" class="btn btn-sm btn-primary">Text</a>
```
```html title="New"
<a href="#" class="btn btn-primary">Text</a>
```
### Update `bg-$color` classes
Foreground (text) color is no longer automatically adjusted by `bg-$color` classes. To ensure sufficient contrast with the background color, use the [`text-bg-$color`](https://getbootstrap.com/docs/5.3/helpers/color-background/) form of the class instead, or set the text color separately with `text-$color`.
```html title="Old"
<span class="badge bg-primary">Text</span>
```
```html title="New"
<span class="badge text-bg-primary">Text</span>
```
### Obsolete custom CSS classes
The following custom CSS classes have been removed:
* `object-subtitle` (use `text-secondary` instead)
## REST API
### Extend serializer for brief mode
NetBox now uses a single API serializer for both normal and "brief" modes (i.e. `GET /api/dcim/sites/?brief=true`); nested serializer classes are no longer required. Two changes to API serializers are necessary to support brief mode:
1. Define `brief_fields` under its `Meta` class. These are the fields which will be included when brief mode is used.
2. For any nested objects, switch to using the primary serializer and pass `nested=True`.
Any nested serializers which are no longer needed can be removed.
```python title="Old"
class SiteSerializer(NetBoxModelSerializer):
region = NestedRegionSerializer(required=False, allow_null=True)
class Meta:
model = Site
fields = ('id', 'url', 'display', 'name', 'slug', 'status', 'region', 'time_zone', ...)
```
```python title="New"
class SiteSerializer(NetBoxModelSerializer):
region = RegionSerializer(nested=True, required=False, allow_null=True)
class Meta:
model = Site
fields = ('id', 'url', 'display', 'name', 'slug', 'status', 'region', 'time_zone', ...)
brief_fields = ('id', 'url', 'display', 'name', 'description', 'slug')
```
### Include description fields in brief mode
NetBox now includes the `description` field in "brief" mode for all models which have one. This is not required for plugins, but you may opt to do the same for consistency.
## GraphQL
NetBox has replaced [Graphene-Django](https://github.com/graphql-python/graphene-django) with [Strawberry](https://strawberry.rocks/) which requires any GraphQL code to be updated.
### Change schema.py
Strawberry uses [Python typing](https://docs.python.org/3/library/typing.html) and generally only requires a small refactoring of the schema definition to update:
```python title="Old"
import graphene
from netbox.graphql.fields import ObjectField, ObjectListField
from utilities.graphql_optimizer import gql_query_optimizer
class CircuitsQuery(graphene.ObjectType):
circuit = ObjectField(CircuitType)
circuit_list = ObjectListField(CircuitType)
def resolve_circuit_list(root, info, **kwargs):
return gql_query_optimizer(models.Circuit.objects.all(), info)
```
```python title="New"
import strawberry
import strawberry_django
@strawberry.type
class CircuitsQuery:
@strawberry.field
def circuit(self, id: int) -> CircuitType:
return models.Circuit.objects.get(pk=id)
circuit_list: list[CircuitType] = strawberry_django.field()
```
### Change types.py
Type conversion is also fairly straight-forward, but Strawberry requires FK and M2M references to be explicitly defined to pick up the right typing.
1. The `class Meta` options need to be moved up to the Strawberry decorator
2. Add `@strawberry_django.field` definitions for any FK and M2M references in the model
```python title="Old"
import graphene
class CircuitType(NetBoxObjectType, ContactsMixin):
class Meta:
model = models.Circuit
fields = '__all__'
filterset_class = filtersets.CircuitFilterSet
```
```python title="New"
from typing import Annotated
import strawberry
import strawberry_django
@strawberry_django.type(
models.CircuitType,
fields='__all__',
filters=CircuitTypeFilter
)
class CircuitTypeType(OrganizationalObjectType):
color: str
@strawberry_django.field
def circuits(self) -> list[Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]]:
return self.circuits.all()
```
### Change filters.py
Strawberry currently doesn't directly support django-filter, so an explicit filters.py file will need to be created. NetBox includes a new `autotype_decorator` used to automatically wrap FilterSets to reduce the required code to a minimum.
```python title="New"
import strawberry
import strawberry_django
from circuits import filtersets, models
from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin
__all__ = (
'CircuitFilter',
)
@strawberry_django.filter(models.Circuit, lookups=True)
@autotype_decorator(filtersets.CircuitFilterSet)
class CircuitFilter(BaseFilterMixin):
pass
```

View File

@ -49,8 +49,8 @@ menu_items = (item1, item2, item3)
Each menu item represents a link and (optionally) a set of buttons comprising one entry in NetBox's navigation menu. Menu items are defined as PluginMenuItem instances. An example is shown below.
```python title="navigation.py"
from netbox.choices import ButtonColorChoices
from netbox.plugins import PluginMenuButton, PluginMenuItem
from utilities.choices import ButtonColorChoices
item1 = PluginMenuItem(
link='plugins:myplugin:myview',
@ -72,8 +72,6 @@ A `PluginMenuItem` has the following attributes:
| `staff_only` | - | Display only for users who have `is_staff` set to true (any specified permissions will also be required) |
| `buttons` | - | An iterable of PluginMenuButton instances to include |
!!! info "The `staff_only` attribute was introduced in NetBox v3.6.1."
## Menu Buttons
Each menu item can include a set of buttons. These can be handy for providing shortcuts related to the menu item. For instance, most items in NetBox's navigation menu include buttons to create and import new objects.

View File

@ -90,8 +90,6 @@ The table column classes listed below are supported for use in plugins. These cl
## Extending Core Tables
!!! info "This feature was introduced in NetBox v3.7."
Plugins can register their own custom columns on core tables using the `register_table_column()` utility function. This allows a plugin to attach additional information, such as relationships to its own models, to built-in object lists.
```python

View File

@ -10,6 +10,14 @@ Minor releases are published in April, August, and December of each calendar yea
This page contains a history of all major and minor releases since NetBox v2.0. For more detail on a specific patch release, please see the release notes page for that specific minor release.
#### [Version 4.0](./version-4.0.md) (April 2024)
* Complete UI Refresh ([#12128](https://github.com/netbox-community/netbox/issues/12128))
* Dynamic REST API Fields ([#15087](https://github.com/netbox-community/netbox/issues/15087))
* Strawberry GraphQL Engine ([#9856](https://github.com/netbox-community/netbox/issues/9856))
* Advanced Form Rendering Functionality ([#14739](https://github.com/netbox-community/netbox/issues/14739))
* Legacy Admin UI Disabled ([#12325](https://github.com/netbox-community/netbox/issues/12325))
#### [Version 3.7](./version-3.7.md) (December 2023)
* VPN Tunnels ([#9816](https://github.com/netbox-community/netbox/issues/9816))

View File

@ -1,5 +1,22 @@
# NetBox v3.7
## v3.7.8 (2024-05-06)
### Enhancements
* [#12127](https://github.com/netbox-community/netbox/issues/12127) - Enable adding new cables directly from navigation menu
### Bug Fixes
* [#15877](https://github.com/netbox-community/netbox/issues/15877) - Account for virtual chassis membership when assigning related interfaces via bulk edit
* [#15917](https://github.com/netbox-community/netbox/issues/15917) - Fix pagination through search results within dropdown fields
* [#15925](https://github.com/netbox-community/netbox/issues/15925) - Fix SVG rendering of cable traces to circuit terminations
* [#15948](https://github.com/netbox-community/netbox/issues/15948) - Fix cable trace SVG generation for cables with multiple terminations at both ends
* [#15960](https://github.com/netbox-community/netbox/issues/15960) - Replace CSV export formatting for several many-to-many fields
* [#15961](https://github.com/netbox-community/netbox/issues/15961) - Fix secret toggle button for IKE policies
---
## v3.7.7 (2024-05-01)
### Enhancements

View File

@ -0,0 +1,182 @@
# NetBox v4.0
## v4.0.1 (FUTURE)
---
## v4.0.0 (2024-05-06)
!!! tip "Plugin Maintainers"
Please see the dedicated [plugin migration guide](../plugins/development/migration-v4.md) for a checklist of changes that may be needed to ensure compatibility with NetBox v4.0.
### Breaking Changes
* Support for Python 3.8 and 3.9 has been removed.
* The format for GraphQL query filters has changed. Please see the GraphQL documentation for details and examples.
* The deprecated `device_role` & `device_role_id` filters for devices have been removed. (Use `role` and `role_id` instead.)
* The obsolete `device_role` field has been removed from the REST API serializer for devices. (Use `role` instead.)
* The legacy reports functionality has been dropped. Reports will be automatically converted to custom scripts on upgrade.
* The `parent` and `parent_id` filters for locations now return only immediate children of the specified location. (Use `ancestor` and `ancestor_id` to return _all_ descendants.)
* The `object_type` field on the CustomField model has been renamed to `related_object_type`.
* The `utilities.utils` module has been removed and its resources reorganized into separate modules organized by function.
* The obsolete `NullableCharField` class has been removed. (Use Django's stock `CharField` class with `null=True` instead.)
* The `annotated_date` template filter and `annotated_now` template tag have been removed.
### New Features
#### Complete UI Refresh ([#12128](https://github.com/netbox-community/netbox/issues/12128))
The NetBox user interface has been completely refreshed and updated. This massive effort entailed:
* Refactoring the base HTML templates
* Moving from Boostrap 5.0 to Bootstrap 5.3
* Adopting the [Tabler](https://tabler.io/) UI theme
* Replacing slim-select with [Tom-Select](https://tom-select.js.org/)
* Displaying additional object attributes in dropdown form fields
* Enabling opt-in HTMX-powered navigation (see [#14736](https://github.com/netbox-community/netbox/issues/14736))
* Widespread cleanup & standardization of UI components
#### Dynamic REST API Fields ([#15087](https://github.com/netbox-community/netbox/issues/15087))
The REST API now supports specifying which fields to include in the response data. For example, the response to a request for
```
GET /api/dcim/sites/?fields=name,status,region,tenant
```
will include only the four specified fields in the representation of each site. Additionally, the underlying database queries effected by such requests have been optimized to omit fields which are not included in the response, resulting in a substantial performance improvement.
#### Strawberry GraphQL Engine ([#9856](https://github.com/netbox-community/netbox/issues/9856))
The GraphQL engine has been changed from using Graphene-Django to Strawberry-Django. Changes include:
* Queryset Optimizer - reduces the number of database queries when querying related tables
* Updated GraphiQL Browser
* The format for GraphQL query filters and lookups has changed. Please see the GraphQL documentation for details and examples.
#### Advanced Form Rendering Functionality ([#14739](https://github.com/netbox-community/netbox/issues/14739))
New resources have been introduced to enable advanced form rendering without a need for custom HTML templates. These include:
* FieldSet - Represents a grouping of form fields (replaces the use of lists/tuples)
* InlineFields - Multiple fields rendered on a single row
* TabbedGroups - Fieldsets rendered under navigable tabs within a form
* ObjectAttribute - Renders a read-only representation of a particular object attribute (for reference)
#### Legacy Admin UI Disabled ([#12325](https://github.com/netbox-community/netbox/issues/12325))
The legacy admin user interface is now disabled by default, and the few remaining views it provided have been relocated to the primary UI. NetBox deployments which still depend on the legacy admin functionality for plugins can enable it by setting the `DJANGO_ADMIN_ENABLED` configuration parameter to true.
### Enhancements
* [#12776](https://github.com/netbox-community/netbox/issues/12776) - Introduce the `htmx_table` template tag to simplify the rendering of embedded tables
* [#12851](https://github.com/netbox-community/netbox/issues/12851) - Replace the deprecated Bleach HTML sanitization library with nh3
* [#13283](https://github.com/netbox-community/netbox/issues/13283) - Display additional context on API-backed dropdown form fields (e.g. object descriptions)
* [#13918](https://github.com/netbox-community/netbox/issues/13918) - Add `facility` field to Location model
* [#14237](https://github.com/netbox-community/netbox/issues/14237) - Automatically clear dependent selection form fields when modifying a parent selection
* [#14279](https://github.com/netbox-community/netbox/issues/14279) - Make the current request available as context when running custom validators
* [#14454](https://github.com/netbox-community/netbox/issues/14454) - Include member devices in the REST API representation of virtual chassis
* [#14637](https://github.com/netbox-community/netbox/issues/14637) - Upgrade to Django 5.0
* [#14672](https://github.com/netbox-community/netbox/issues/14672) - Add support for Python 3.12
* [#14728](https://github.com/netbox-community/netbox/issues/14728) - The plugins list view has been moved from the legacy admin UI to the main NetBox UI
* [#14729](https://github.com/netbox-community/netbox/issues/14729) - All background task views have been moved from the legacy admin UI to the main NetBox UI
* [#14736](https://github.com/netbox-community/netbox/issues/14736) - Introduce a user preference to enable HTMX-powered navigation
* [#14438](https://github.com/netbox-community/netbox/issues/14438) - Track individual custom scripts as database objects
* [#15131](https://github.com/netbox-community/netbox/issues/15131) - Automatically annotate related object counts on REST API querysets
* [#15237](https://github.com/netbox-community/netbox/issues/15237) - Ensure consistent filtering ability for all model fields by testing for missing/incorrect filters
* [#15238](https://github.com/netbox-community/netbox/issues/15238) - Include the `description` field in "brief" REST API serializations
* [#15278](https://github.com/netbox-community/netbox/issues/15278) - BaseModelSerializer now takes a `nested` keyword argument allowing it to represent a related object
* [#15383](https://github.com/netbox-community/netbox/issues/15383) - Standardize filtering logic for the parents of recursively-nested models (parent & ancestor filters)
* [#15413](https://github.com/netbox-community/netbox/issues/15413) - The global search engine now supports caching of non-field object attributes
* [#15490](https://github.com/netbox-community/netbox/issues/15490) - Custom validators can now reference related object attributes via dotted paths
* [#15547](https://github.com/netbox-community/netbox/issues/15547) - Add comments field to CustomField model
* [#15712](https://github.com/netbox-community/netbox/issues/15712) - Enable image attachments for virtual machines
* [#15735](https://github.com/netbox-community/netbox/issues/15735) - Display all dates & times in ISO 8601 format consistently
* [#15754](https://github.com/netbox-community/netbox/issues/15754) - Remove `is_staff` restriction on admin menu items
* [#15764](https://github.com/netbox-community/netbox/issues/15764) - Increase maximum value of Device `vc_position` field
* [#15915](https://github.com/netbox-community/netbox/issues/15915) - Provide a comprehensive system status view with export functionality
### Bug Fixes (from Beta2)
* [#15630](https://github.com/netbox-community/netbox/issues/15630) - Ensure consistent toggling between light & dark UI modes
* [#15802](https://github.com/netbox-community/netbox/issues/15802) - Improve hyperlink color contrast in dark mode
* [#15809](https://github.com/netbox-community/netbox/issues/15809) - Fix GraphQL union support for nullable fields
* [#15815](https://github.com/netbox-community/netbox/issues/15815) - Convert dashboard widgets referencing old user/group models
* [#15826](https://github.com/netbox-community/netbox/issues/15826) - Update `EXEMPT_EXCLUDE_MODELS` to reference new user & group models
* [#15831](https://github.com/netbox-community/netbox/issues/15831) - Fix LDAP group mirroring
* [#15838](https://github.com/netbox-community/netbox/issues/15838) - Fix AttributeError exception when rendering custom date fields
* [#15852](https://github.com/netbox-community/netbox/issues/15852) - Update total results count when filtering object lists
* [#15853](https://github.com/netbox-community/netbox/issues/15853) - Correct background color for cable trace SVG images in dark mode
* [#15855](https://github.com/netbox-community/netbox/issues/15855) - Fix AttributeError exception when creating an event rule tied to a custom script
* [#15944](https://github.com/netbox-community/netbox/issues/15944) - Fix styling of paginator when displayed above an object list
### Other Changes
* [#10587](https://github.com/netbox-community/netbox/issues/10587) - Enable pagination and filtering for custom script logs
* [#12325](https://github.com/netbox-community/netbox/issues/12325) - The Django admin UI is now disabled by default (set `DJANGO_ADMIN_ENABLED` to True to enable it)
* [#12510](https://github.com/netbox-community/netbox/issues/12510) - Dropped support for legacy reports
* [#12795](https://github.com/netbox-community/netbox/issues/12795) - NetBox now uses custom User and Group models rather than the stock models provided by Django
* [#13647](https://github.com/netbox-community/netbox/issues/13647) - Squash all database migrations prior to v3.7
* [#14092](https://github.com/netbox-community/netbox/issues/14092) - Remove backward compatibility for importing plugin resources from `extras.plugins` (now `netbox.plugins`)
* [#14638](https://github.com/netbox-community/netbox/issues/14638) - Drop support for Python 3.8 and 3.9
* [#14657](https://github.com/netbox-community/netbox/issues/14657) - Remove backward compatibility for old permissions mapping under `ActionsMixin`
* [#14658](https://github.com/netbox-community/netbox/issues/14658) - Remove backward compatibility for importing `process_webhook()` from `extras.webhooks_worker` (now `extras.webhooks.send_webhook()`)
* [#14740](https://github.com/netbox-community/netbox/issues/14740) - Remove the obsolete `BootstrapMixin` form mixin class
* [#15042](https://github.com/netbox-community/netbox/issues/15042) - The logic for registering models & model features now executes under the `ready()` method of individual app configs, rather than relying on the `class_prepared` signal
* [#15099](https://github.com/netbox-community/netbox/issues/15099) - Remove obsolete `device_role` and `device_role_id` filters for devices
* [#15100](https://github.com/netbox-community/netbox/issues/15100) - Remove obsolete `NullableCharField` class
* [#15154](https://github.com/netbox-community/netbox/issues/15154) - The installation documentation been extended to include instructions and an example configuration file for uWSGI as an alternative to gunicorn
* [#15193](https://github.com/netbox-community/netbox/issues/15193) - Switch to compiled distribution of the `psycopg` library
* [#15277](https://github.com/netbox-community/netbox/issues/15277) - Replace references to ContentType without ObjectType proxy model & standardize field names
* [#15292](https://github.com/netbox-community/netbox/issues/15292) - Remove obsolete `device_role` attribute from Device model (this field was renamed to `role` in v3.6)
* [#15357](https://github.com/netbox-community/netbox/issues/15357) - The `object_type` field on the CustomField model has been renamed to `related_object_type` to avoid confusion with its `object_types` field
* [#15401](https://github.com/netbox-community/netbox/issues/15401) - PostgreSQL indexes and sequence tables for the relocated L2VPN models (see [#14311](https://github.com/netbox-community/netbox/issues/14311)) have been renamed
* [#15462](https://github.com/netbox-community/netbox/issues/15462) - Relocate resources from the `utilities.utils` module
* [#15464](https://github.com/netbox-community/netbox/issues/15464) - The many-to-many relationships for ObjectPermission are now defined on the custom User and Group models
* [#15736](https://github.com/netbox-community/netbox/issues/15736) - Remove obsolete `annotated_date` template filter & `annotated_now` template tag
* [#15738](https://github.com/netbox-community/netbox/issues/15738) - Remove obsolete configuration parameters for date & time formatting
* [#15752](https://github.com/netbox-community/netbox/issues/15752) - Remove the obsolete `ENABLE_LOCALIZATION` configuration parameter
* [#15942](https://github.com/netbox-community/netbox/issues/15942) - Refactor `settings_and_registry()` context processor
### REST API Changes
* The `/api/extras/content-types/` endpoint has moved to `/api/extras/object-types/`
* The `/api/extras/reports/` endpoint has been removed
* The `description` field is now included by default when using "brief mode" for all relevant models
* dcim.Device
* The obsolete read-only attribute `device_role` has been removed (replaced by `role` in v3.6)
* dcim.Location
* Added the optional `location` field
* dcim.VirtualChassis
* Added `members` field to list the member devices
* extras.CustomField
* `content_types` has been renamed to `object_types`
* `object_type` has been renamed to `related_object_type`
* The `content_types` filter is now `object_type`
* The `content_type_id` filter is now `object_type_id`
* extras.CustomLink
* `content_types` has been renamed to `object_types`
* The `content_types` filter is now `object_type`
* The `content_type_id` filter is now `object_type_id`
* extras.EventRule
* `content_types` has been renamed to `object_types`
* The `content_types` filter is now `object_type`
* The `content_type_id` filter is now `object_type_id`
* extras.ExportTemplate
* `content_types` has been renamed to `object_types`
* The `content_types` filter is now `object_type`
* The `content_type_id` filter is now `object_type_id`
* extras.ImageAttachment
* `content_type` has been renamed to `object_type`
* The `content_type` filter is now `object_type`
* extras.SavedFilter
* `content_types` has been renamed to `object_types`
* The `content_types` filter is now `object_type`
* The `content_type_id` filter is now `object_type_id`
* tenancy.ContactAssignment
* `content_type` has been renamed to `object_type`
* The `content_type_id` filter is now `object_type_id`
* users.Group
* Added the `permissions` field
* users.User
* Added the `permissions` field

View File

@ -53,6 +53,7 @@ extra_css:
markdown_extensions:
- admonition
- attr_list
- footnotes
- pymdownx.emoji:
emoji_index: !!python/name:material.extensions.emoji.twemoji
emoji_generator: !!python/name:material.extensions.emoji.to_svg
@ -94,7 +95,8 @@ nav:
- 1. PostgreSQL: 'installation/1-postgresql.md'
- 2. Redis: 'installation/2-redis.md'
- 3. NetBox: 'installation/3-netbox.md'
- 4. Gunicorn: 'installation/4-gunicorn.md'
- 4a. Gunicorn: 'installation/4a-gunicorn.md'
- 4b. uWSGI: 'installation/4b-uwsgi.md'
- 5. HTTP Server: 'installation/5-http-server.md'
- 6. LDAP (Optional): 'installation/6-ldap.md'
- Upgrading NetBox: 'installation/upgrading.md'
@ -111,7 +113,6 @@ nav:
- Default Values: 'configuration/default-values.md'
- Error Reporting: 'configuration/error-reporting.md'
- Plugins: 'configuration/plugins.md'
- Date & Time: 'configuration/date-time.md'
- Miscellaneous: 'configuration/miscellaneous.md'
- Development: 'configuration/development.md'
- Customization:
@ -148,6 +149,7 @@ nav:
- Dashboard Widgets: 'plugins/development/dashboard-widgets.md'
- Staged Changes: 'plugins/development/staged-changes.md'
- Exceptions: 'plugins/development/exceptions.md'
- Migrating to v4.0: 'plugins/development/migration-v4.md'
- Administration:
- Authentication:
- Overview: 'administration/authentication/overview.md'
@ -294,6 +296,7 @@ nav:
- git Cheat Sheet: 'development/git-cheat-sheet.md'
- Release Notes:
- Summary: 'release-notes/index.md'
- Version 4.0: 'release-notes/version-4.0.md'
- Version 3.7: 'release-notes/version-3.7.md'
- Version 3.6: 'release-notes/version-3.6.md'
- Version 3.5: 'release-notes/version-3.5.md'

View File

@ -30,10 +30,12 @@ class UserTokenTable(NetBoxTable):
write_enabled = columns.BooleanColumn(
verbose_name=_('Write Enabled')
)
created = columns.DateColumn(
created = columns.DateTimeColumn(
timespec='minutes',
verbose_name=_('Created'),
)
expires = columns.DateColumn(
expires = columns.DateTimeColumn(
timespec='minutes',
verbose_name=_('Expires'),
)
last_used = columns.DateTimeColumn(

View File

@ -2,8 +2,8 @@ import logging
from django.conf import settings
from django.contrib import messages
from django.contrib.auth import login as auth_login, logout as auth_logout
from django.contrib.auth import update_session_auth_hash
from django.contrib.auth import login as auth_login, logout as auth_logout, update_session_auth_hash
from django.contrib.auth.forms import AuthenticationForm, PasswordChangeForm
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.models import update_last_login
from django.contrib.auth.signals import user_logged_in
@ -72,7 +72,7 @@ class LoginView(View):
return auth_backends
def get(self, request):
form = forms.LoginForm(request)
form = AuthenticationForm(request)
if request.user.is_authenticated:
logger = logging.getLogger('netbox.auth.login')
@ -85,7 +85,7 @@ class LoginView(View):
def post(self, request):
logger = logging.getLogger('netbox.auth.login')
form = forms.LoginForm(request, data=request.POST)
form = AuthenticationForm(request, data=request.POST)
if form.is_valid():
logger.debug("Login form validation was successful")
@ -220,7 +220,7 @@ class ChangePasswordView(LoginRequiredMixin, View):
messages.warning(request, "LDAP-authenticated user credentials cannot be changed within NetBox.")
return redirect('account:profile')
form = forms.PasswordChangeForm(user=request.user)
form = PasswordChangeForm(user=request.user)
return render(request, self.template_name, {
'form': form,
@ -228,7 +228,7 @@ class ChangePasswordView(LoginRequiredMixin, View):
})
def post(self, request):
form = forms.PasswordChangeForm(user=request.user, data=request.POST)
form = PasswordChangeForm(user=request.user, data=request.POST)
if form.is_valid():
form.save()
update_session_auth_hash(request, form.user)

View File

@ -1,8 +1,8 @@
from drf_spectacular.utils import extend_schema_field, extend_schema_serializer
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema_serializer
from rest_framework import serializers
from circuits.models import *
from netbox.api.fields import RelatedObjectCountField
from netbox.api.serializers import WritableNestedSerializer
__all__ = [
@ -36,7 +36,7 @@ class NestedProviderNetworkSerializer(WritableNestedSerializer):
)
class NestedProviderSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:provider-detail')
circuit_count = serializers.IntegerField(read_only=True)
circuit_count = RelatedObjectCountField('circuits')
class Meta:
model = Provider
@ -64,7 +64,7 @@ class NestedProviderAccountSerializer(WritableNestedSerializer):
)
class NestedCircuitTypeSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittype-detail')
circuit_count = serializers.IntegerField(read_only=True)
circuit_count = RelatedObjectCountField('circuits')
class Meta:
model = CircuitType

View File

@ -1,137 +1,3 @@
from rest_framework import serializers
from circuits.choices import CircuitStatusChoices
from circuits.models import *
from dcim.api.nested_serializers import NestedSiteSerializer
from dcim.api.serializers import CabledObjectSerializer
from ipam.models import ASN
from ipam.api.nested_serializers import NestedASNSerializer
from netbox.api.fields import ChoiceField, SerializedPKRelatedField
from netbox.api.serializers import NetBoxModelSerializer, WritableNestedSerializer
from tenancy.api.nested_serializers import NestedTenantSerializer
from .serializers_.providers import *
from .serializers_.circuits import *
from .nested_serializers import *
#
# Providers
#
class ProviderSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:provider-detail')
accounts = SerializedPKRelatedField(
queryset=ProviderAccount.objects.all(),
serializer=NestedProviderAccountSerializer,
required=False,
many=True
)
asns = SerializedPKRelatedField(
queryset=ASN.objects.all(),
serializer=NestedASNSerializer,
required=False,
many=True
)
# Related object counts
circuit_count = serializers.IntegerField(read_only=True)
class Meta:
model = Provider
fields = [
'id', 'url', 'display', 'name', 'slug', 'accounts', 'description', 'comments', 'asns', 'tags',
'custom_fields', 'created', 'last_updated', 'circuit_count',
]
#
# Provider Accounts
#
class ProviderAccountSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:provideraccount-detail')
provider = NestedProviderSerializer()
class Meta:
model = ProviderAccount
fields = [
'id', 'url', 'display', 'provider', 'name', 'account', 'description', 'comments', 'tags', 'custom_fields',
'created', 'last_updated',
]
#
# Provider networks
#
class ProviderNetworkSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:providernetwork-detail')
provider = NestedProviderSerializer()
class Meta:
model = ProviderNetwork
fields = [
'id', 'url', 'display', 'provider', 'name', 'service_id', 'description', 'comments', 'tags',
'custom_fields', 'created', 'last_updated',
]
#
# Circuits
#
class CircuitTypeSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittype-detail')
circuit_count = serializers.IntegerField(read_only=True)
class Meta:
model = CircuitType
fields = [
'id', 'url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields', 'created', 'last_updated',
'circuit_count',
]
class CircuitCircuitTerminationSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittermination-detail')
site = NestedSiteSerializer(allow_null=True)
provider_network = NestedProviderNetworkSerializer(allow_null=True)
class Meta:
model = CircuitTermination
fields = [
'id', 'url', 'display', 'site', 'provider_network', 'port_speed', 'upstream_speed', 'xconnect_id',
'description',
]
class CircuitSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuit-detail')
provider = NestedProviderSerializer()
provider_account = NestedProviderAccountSerializer(required=False, allow_null=True)
status = ChoiceField(choices=CircuitStatusChoices, required=False)
type = NestedCircuitTypeSerializer()
tenant = NestedTenantSerializer(required=False, allow_null=True)
termination_a = CircuitCircuitTerminationSerializer(read_only=True, allow_null=True)
termination_z = CircuitCircuitTerminationSerializer(read_only=True, allow_null=True)
class Meta:
model = Circuit
fields = [
'id', 'url', 'display', 'cid', 'provider', 'provider_account', 'type', 'status', 'tenant', 'install_date',
'termination_date', 'commit_rate', 'description', 'termination_a', 'termination_z', 'comments', 'tags',
'custom_fields', 'created', 'last_updated',
]
class CircuitTerminationSerializer(NetBoxModelSerializer, CabledObjectSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittermination-detail')
circuit = NestedCircuitSerializer()
site = NestedSiteSerializer(required=False, allow_null=True)
provider_network = NestedProviderNetworkSerializer(required=False, allow_null=True)
class Meta:
model = CircuitTermination
fields = [
'id', 'url', 'display', 'circuit', 'term_side', 'site', 'provider_network', 'port_speed', 'upstream_speed',
'xconnect_id', 'pp_info', 'description', 'mark_connected', 'cable', 'cable_end', 'link_peers',
'link_peers_type', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
]

View File

@ -0,0 +1,81 @@
from rest_framework import serializers
from circuits.choices import CircuitStatusChoices
from circuits.models import Circuit, CircuitTermination, CircuitType
from dcim.api.serializers_.cables import CabledObjectSerializer
from dcim.api.serializers_.sites import SiteSerializer
from netbox.api.fields import ChoiceField, RelatedObjectCountField
from netbox.api.serializers import NetBoxModelSerializer, WritableNestedSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
from .providers import ProviderAccountSerializer, ProviderNetworkSerializer, ProviderSerializer
__all__ = (
'CircuitSerializer',
'CircuitTerminationSerializer',
'CircuitTypeSerializer',
)
class CircuitTypeSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittype-detail')
# Related object counts
circuit_count = RelatedObjectCountField('circuits')
class Meta:
model = CircuitType
fields = [
'id', 'url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields', 'created',
'last_updated', 'circuit_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'circuit_count')
class CircuitCircuitTerminationSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittermination-detail')
site = SiteSerializer(nested=True, allow_null=True)
provider_network = ProviderNetworkSerializer(nested=True, allow_null=True)
class Meta:
model = CircuitTermination
fields = [
'id', 'url', 'display', 'site', 'provider_network', 'port_speed', 'upstream_speed', 'xconnect_id',
'description',
]
class CircuitSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuit-detail')
provider = ProviderSerializer(nested=True)
provider_account = ProviderAccountSerializer(nested=True, required=False, allow_null=True, default=None)
status = ChoiceField(choices=CircuitStatusChoices, required=False)
type = CircuitTypeSerializer(nested=True)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
termination_a = CircuitCircuitTerminationSerializer(read_only=True, allow_null=True)
termination_z = CircuitCircuitTerminationSerializer(read_only=True, allow_null=True)
class Meta:
model = Circuit
fields = [
'id', 'url', 'display', 'cid', 'provider', 'provider_account', 'type', 'status', 'tenant', 'install_date',
'termination_date', 'commit_rate', 'description', 'termination_a', 'termination_z', 'comments', 'tags',
'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'cid', 'description')
class CircuitTerminationSerializer(NetBoxModelSerializer, CabledObjectSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittermination-detail')
circuit = CircuitSerializer(nested=True)
site = SiteSerializer(nested=True, required=False, allow_null=True)
provider_network = ProviderNetworkSerializer(nested=True, required=False, allow_null=True)
class Meta:
model = CircuitTermination
fields = [
'id', 'url', 'display', 'circuit', 'term_side', 'site', 'provider_network', 'port_speed', 'upstream_speed',
'xconnect_id', 'pp_info', 'description', 'mark_connected', 'cable', 'cable_end', 'link_peers',
'link_peers_type', 'tags', 'custom_fields', 'created', 'last_updated', '_occupied',
]
brief_fields = ('id', 'url', 'display', 'circuit', 'term_side', 'description', 'cable', '_occupied')

View File

@ -0,0 +1,69 @@
from rest_framework import serializers
from circuits.models import Provider, ProviderAccount, ProviderNetwork
from ipam.api.serializers_.asns import ASNSerializer
from ipam.models import ASN
from netbox.api.fields import RelatedObjectCountField, SerializedPKRelatedField
from netbox.api.serializers import NetBoxModelSerializer
from ..nested_serializers import *
__all__ = (
'ProviderAccountSerializer',
'ProviderNetworkSerializer',
'ProviderSerializer',
)
class ProviderSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:provider-detail')
accounts = SerializedPKRelatedField(
queryset=ProviderAccount.objects.all(),
serializer=NestedProviderAccountSerializer,
required=False,
many=True
)
asns = SerializedPKRelatedField(
queryset=ASN.objects.all(),
serializer=ASNSerializer,
nested=True,
required=False,
many=True
)
# Related object counts
circuit_count = RelatedObjectCountField('circuits')
class Meta:
model = Provider
fields = [
'id', 'url', 'display', 'name', 'slug', 'accounts', 'description', 'comments', 'asns', 'tags',
'custom_fields', 'created', 'last_updated', 'circuit_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'circuit_count')
class ProviderAccountSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:provideraccount-detail')
provider = ProviderSerializer(nested=True)
name = serializers.CharField(allow_blank=True, max_length=100, required=False, default='')
class Meta:
model = ProviderAccount
fields = [
'id', 'url', 'display', 'provider', 'name', 'account', 'description', 'comments', 'tags', 'custom_fields',
'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'account', 'description')
class ProviderNetworkSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:providernetwork-detail')
provider = ProviderSerializer(nested=True)
class Meta:
model = ProviderNetwork
fields = [
'id', 'url', 'display', 'provider', 'name', 'service_id', 'description', 'comments', 'tags',
'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')

View File

@ -4,7 +4,6 @@ from circuits import filtersets
from circuits.models import *
from dcim.api.views import PassThroughPortMixin
from netbox.api.viewsets import NetBoxModelViewSet
from utilities.utils import count_related
from . import serializers
@ -21,9 +20,7 @@ class CircuitsRootView(APIRootView):
#
class ProviderViewSet(NetBoxModelViewSet):
queryset = Provider.objects.prefetch_related('asns', 'tags').annotate(
circuit_count=count_related(Circuit, 'provider')
)
queryset = Provider.objects.all()
serializer_class = serializers.ProviderSerializer
filterset_class = filtersets.ProviderFilterSet
@ -33,9 +30,7 @@ class ProviderViewSet(NetBoxModelViewSet):
#
class CircuitTypeViewSet(NetBoxModelViewSet):
queryset = CircuitType.objects.prefetch_related('tags').annotate(
circuit_count=count_related(Circuit, 'type')
)
queryset = CircuitType.objects.all()
serializer_class = serializers.CircuitTypeSerializer
filterset_class = filtersets.CircuitTypeFilterSet
@ -45,9 +40,7 @@ class CircuitTypeViewSet(NetBoxModelViewSet):
#
class CircuitViewSet(NetBoxModelViewSet):
queryset = Circuit.objects.prefetch_related(
'type', 'tenant', 'provider', 'provider_account', 'termination_a', 'termination_z'
).prefetch_related('tags')
queryset = Circuit.objects.all()
serializer_class = serializers.CircuitSerializer
filterset_class = filtersets.CircuitFilterSet
@ -57,12 +50,9 @@ class CircuitViewSet(NetBoxModelViewSet):
#
class CircuitTerminationViewSet(PassThroughPortMixin, NetBoxModelViewSet):
queryset = CircuitTermination.objects.prefetch_related(
'circuit', 'site', 'provider_network', 'cable__terminations'
)
queryset = CircuitTermination.objects.all()
serializer_class = serializers.CircuitTerminationSerializer
filterset_class = filtersets.CircuitTerminationFilterSet
brief_prefetch_fields = ['circuit']
#
@ -70,7 +60,7 @@ class CircuitTerminationViewSet(PassThroughPortMixin, NetBoxModelViewSet):
#
class ProviderAccountViewSet(NetBoxModelViewSet):
queryset = ProviderAccount.objects.prefetch_related('provider', 'tags')
queryset = ProviderAccount.objects.all()
serializer_class = serializers.ProviderAccountSerializer
filterset_class = filtersets.ProviderAccountFilterSet
@ -80,6 +70,6 @@ class ProviderAccountViewSet(NetBoxModelViewSet):
#
class ProviderNetworkViewSet(NetBoxModelViewSet):
queryset = ProviderNetwork.objects.prefetch_related('tags')
queryset = ProviderNetwork.objects.all()
serializer_class = serializers.ProviderNetworkSerializer
filterset_class = filtersets.ProviderNetworkFilterSet

View File

@ -6,4 +6,8 @@ class CircuitsConfig(AppConfig):
verbose_name = "Circuits"
def ready(self):
from netbox.models.features import register_models
from . import signals, search
# Register models
register_models(*self.get_models())

View File

@ -73,7 +73,7 @@ class ProviderFilterSet(NetBoxModelFilterSet, ContactModelFilterSet):
class Meta:
model = Provider
fields = ['id', 'name', 'slug', 'description']
fields = ('id', 'name', 'slug', 'description')
def search(self, queryset, name, value):
if not value.strip():
@ -101,7 +101,7 @@ class ProviderAccountFilterSet(NetBoxModelFilterSet):
class Meta:
model = ProviderAccount
fields = ['id', 'name', 'account', 'description']
fields = ('id', 'name', 'account', 'description')
def search(self, queryset, name, value):
if not value.strip():
@ -128,7 +128,7 @@ class ProviderNetworkFilterSet(NetBoxModelFilterSet):
class Meta:
model = ProviderNetwork
fields = ['id', 'name', 'service_id', 'description']
fields = ('id', 'name', 'service_id', 'description')
def search(self, queryset, name, value):
if not value.strip():
@ -145,7 +145,7 @@ class CircuitTypeFilterSet(OrganizationalModelFilterSet):
class Meta:
model = CircuitType
fields = ['id', 'name', 'slug', 'color', 'description']
fields = ('id', 'name', 'slug', 'color', 'description')
class CircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
@ -164,6 +164,12 @@ class CircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilte
queryset=ProviderAccount.objects.all(),
label=_('Provider account (ID)'),
)
provider_account = django_filters.ModelMultipleChoiceFilter(
field_name='provider_account__account',
queryset=Provider.objects.all(),
to_field_name='account',
label=_('Provider account (account)'),
)
provider_network_id = django_filters.ModelMultipleChoiceFilter(
field_name='terminations__provider_network',
queryset=ProviderNetwork.objects.all(),
@ -220,10 +226,18 @@ class CircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilte
to_field_name='slug',
label=_('Site (slug)'),
)
termination_a_id = django_filters.ModelMultipleChoiceFilter(
queryset=CircuitTermination.objects.all(),
label=_('Termination A (ID)'),
)
termination_z_id = django_filters.ModelMultipleChoiceFilter(
queryset=CircuitTermination.objects.all(),
label=_('Termination A (ID)'),
)
class Meta:
model = Circuit
fields = ['id', 'cid', 'description', 'install_date', 'termination_date', 'commit_rate']
fields = ('id', 'cid', 'description', 'install_date', 'termination_date', 'commit_rate')
def search(self, queryset, name, value):
if not value.strip():
@ -264,7 +278,10 @@ class CircuitTerminationFilterSet(NetBoxModelFilterSet, CabledObjectFilterSet):
class Meta:
model = CircuitTermination
fields = ['id', 'term_side', 'port_speed', 'upstream_speed', 'xconnect_id', 'description', 'cable_end']
fields = (
'id', 'term_side', 'port_speed', 'upstream_speed', 'xconnect_id', 'description', 'mark_connected',
'pp_info', 'cable_end',
)
def search(self, queryset, name, value):
if not value.strip():

View File

@ -8,6 +8,7 @@ from netbox.forms import NetBoxModelBulkEditForm
from tenancy.models import Tenant
from utilities.forms import add_blank_choice
from utilities.forms.fields import ColorField, CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import DatePicker, NumberWithOptions
__all__ = (
@ -34,7 +35,7 @@ class ProviderBulkEditForm(NetBoxModelBulkEditForm):
model = Provider
fieldsets = (
(None, ('asns', 'description')),
FieldSet('asns', 'description'),
)
nullable_fields = (
'asns', 'description', 'comments',
@ -56,7 +57,7 @@ class ProviderAccountBulkEditForm(NetBoxModelBulkEditForm):
model = ProviderAccount
fieldsets = (
(None, ('provider', 'description')),
FieldSet('provider', 'description'),
)
nullable_fields = (
'description', 'comments',
@ -83,7 +84,7 @@ class ProviderNetworkBulkEditForm(NetBoxModelBulkEditForm):
model = ProviderNetwork
fieldsets = (
(None, ('provider', 'service_id', 'description')),
FieldSet('provider', 'service_id', 'description'),
)
nullable_fields = (
'service_id', 'description', 'comments',
@ -103,7 +104,7 @@ class CircuitTypeBulkEditForm(NetBoxModelBulkEditForm):
model = CircuitType
fieldsets = (
(None, ('color', 'description')),
FieldSet('color', 'description'),
)
nullable_fields = ('color', 'description')
@ -164,9 +165,9 @@ class CircuitBulkEditForm(NetBoxModelBulkEditForm):
model = Circuit
fieldsets = (
(_('Circuit'), ('provider', 'type', 'status', 'description')),
(_('Service Parameters'), ('provider_account', 'install_date', 'termination_date', 'commit_rate')),
(_('Tenancy'), ('tenant',)),
FieldSet('provider', 'type', 'status', 'description', name=_('Circuit')),
FieldSet('provider_account', 'install_date', 'termination_date', 'commit_rate', name=_('Service Parameters')),
FieldSet('tenant', name=_('Tenancy')),
)
nullable_fields = (
'tenant', 'commit_rate', 'description', 'comments',

View File

@ -7,7 +7,6 @@ from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _
from netbox.forms import NetBoxModelImportForm
from tenancy.models import Tenant
from utilities.forms import BootstrapMixin
from utilities.forms.fields import CSVChoiceField, CSVModelChoiceField, SlugField
__all__ = (
@ -112,7 +111,7 @@ class CircuitImportForm(NetBoxModelImportForm):
]
class CircuitTerminationImportForm(BootstrapMixin, forms.ModelForm):
class CircuitTerminationImportForm(forms.ModelForm):
site = CSVModelChoiceField(
label=_('Site'),
queryset=Site.objects.all(),

View File

@ -8,6 +8,7 @@ from ipam.models import ASN
from netbox.forms import NetBoxModelFilterSetForm
from tenancy.forms import TenancyFilterForm, ContactModelFilterForm
from utilities.forms.fields import ColorField, DynamicModelMultipleChoiceField, TagFilterField
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import DatePicker, NumberWithOptions
__all__ = (
@ -22,10 +23,10 @@ __all__ = (
class ProviderFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
model = Provider
fieldsets = (
(None, ('q', 'filter_id', 'tag')),
(_('Location'), ('region_id', 'site_group_id', 'site_id')),
(_('ASN'), ('asn_id',)),
(_('Contacts'), ('contact', 'contact_role', 'contact_group')),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('region_id', 'site_group_id', 'site_id', name=_('Location')),
FieldSet('asn_id', name=_('ASN')),
FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')),
)
region_id = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(),
@ -57,8 +58,8 @@ class ProviderFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm):
class ProviderAccountFilterForm(NetBoxModelFilterSetForm):
model = ProviderAccount
fieldsets = (
(None, ('q', 'filter_id', 'tag')),
(_('Attributes'), ('provider_id', 'account')),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('provider_id', 'account', name=_('Attributes')),
)
provider_id = DynamicModelMultipleChoiceField(
queryset=Provider.objects.all(),
@ -75,8 +76,8 @@ class ProviderAccountFilterForm(NetBoxModelFilterSetForm):
class ProviderNetworkFilterForm(NetBoxModelFilterSetForm):
model = ProviderNetwork
fieldsets = (
(None, ('q', 'filter_id', 'tag')),
(_('Attributes'), ('provider_id', 'service_id')),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('provider_id', 'service_id', name=_('Attributes')),
)
provider_id = DynamicModelMultipleChoiceField(
queryset=Provider.objects.all(),
@ -94,8 +95,8 @@ class ProviderNetworkFilterForm(NetBoxModelFilterSetForm):
class CircuitTypeFilterForm(NetBoxModelFilterSetForm):
model = CircuitType
fieldsets = (
(None, ('q', 'filter_id', 'tag')),
(_('Attributes'), ('color',)),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('color', name=_('Attributes')),
)
tag = TagFilterField(model)
@ -108,12 +109,12 @@ class CircuitTypeFilterForm(NetBoxModelFilterSetForm):
class CircuitFilterForm(TenancyFilterForm, ContactModelFilterForm, NetBoxModelFilterSetForm):
model = Circuit
fieldsets = (
(None, ('q', 'filter_id', 'tag')),
(_('Provider'), ('provider_id', 'provider_account_id', 'provider_network_id')),
(_('Attributes'), ('type_id', 'status', 'install_date', 'termination_date', 'commit_rate')),
(_('Location'), ('region_id', 'site_group_id', 'site_id')),
(_('Tenant'), ('tenant_group_id', 'tenant_id')),
(_('Contacts'), ('contact', 'contact_role', 'contact_group')),
FieldSet('q', 'filter_id', 'tag'),
FieldSet('provider_id', 'provider_account_id', 'provider_network_id', name=_('Provider')),
FieldSet('type_id', 'status', 'install_date', 'termination_date', 'commit_rate', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', name=_('Location')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')),
)
selector_fields = ('filter_id', 'q', 'region_id', 'site_group_id', 'site_id', 'provider_id', 'provider_network_id')
type_id = DynamicModelMultipleChoiceField(

View File

@ -7,6 +7,7 @@ from ipam.models import ASN
from netbox.forms import NetBoxModelForm
from tenancy.forms import TenancyForm
from utilities.forms.fields import CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField
from utilities.forms.rendering import FieldSet, TabbedGroups
from utilities.forms.widgets import DatePicker, NumberWithOptions
__all__ = (
@ -29,7 +30,7 @@ class ProviderForm(NetBoxModelForm):
comments = CommentField()
fieldsets = (
(_('Provider'), ('name', 'slug', 'asns', 'description', 'tags')),
FieldSet('name', 'slug', 'asns', 'description', 'tags'),
)
class Meta:
@ -61,7 +62,7 @@ class ProviderNetworkForm(NetBoxModelForm):
comments = CommentField()
fieldsets = (
(_('Provider Network'), ('provider', 'name', 'service_id', 'description', 'tags')),
FieldSet('provider', 'name', 'service_id', 'description', 'tags'),
)
class Meta:
@ -75,9 +76,7 @@ class CircuitTypeForm(NetBoxModelForm):
slug = SlugField()
fieldsets = (
(_('Circuit Type'), (
'name', 'slug', 'color', 'description', 'tags',
)),
FieldSet('name', 'slug', 'color', 'description', 'tags'),
)
class Meta:
@ -107,9 +106,9 @@ class CircuitForm(TenancyForm, NetBoxModelForm):
comments = CommentField()
fieldsets = (
(_('Circuit'), ('provider', 'provider_account', 'cid', 'type', 'status', 'description', 'tags')),
(_('Service Parameters'), ('install_date', 'termination_date', 'commit_rate')),
(_('Tenancy'), ('tenant_group', 'tenant')),
FieldSet('provider', 'provider_account', 'cid', 'type', 'status', 'description', 'tags', name=_('Circuit')),
FieldSet('install_date', 'termination_date', 'commit_rate', name=_('Service Parameters')),
FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
)
class Meta:
@ -146,6 +145,18 @@ class CircuitTerminationForm(NetBoxModelForm):
selector=True
)
fieldsets = (
FieldSet(
'circuit', 'term_side', 'description', 'tags',
TabbedGroups(
FieldSet('site', name=_('Site')),
FieldSet('provider_network', name=_('Provider Network')),
),
'mark_connected', name=_('Circuit Termination')
),
FieldSet('port_speed', 'upstream_speed', 'xconnect_id', 'pp_info', name=_('Termination Details')),
)
class Meta:
model = CircuitTermination
fields = [

View File

@ -0,0 +1,50 @@
import strawberry
import strawberry_django
from circuits import filtersets, models
from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin
__all__ = (
'CircuitTerminationFilter',
'CircuitFilter',
'CircuitTypeFilter',
'ProviderFilter',
'ProviderAccountFilter',
'ProviderNetworkFilter',
)
@strawberry_django.filter(models.CircuitTermination, lookups=True)
@autotype_decorator(filtersets.CircuitTerminationFilterSet)
class CircuitTerminationFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.Circuit, lookups=True)
@autotype_decorator(filtersets.CircuitFilterSet)
class CircuitFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.CircuitType, lookups=True)
@autotype_decorator(filtersets.CircuitTypeFilterSet)
class CircuitTypeFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.Provider, lookups=True)
@autotype_decorator(filtersets.ProviderFilterSet)
class ProviderFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.ProviderAccount, lookups=True)
@autotype_decorator(filtersets.ProviderAccountFilterSet)
class ProviderAccountFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.ProviderNetwork, lookups=True)
@autotype_decorator(filtersets.ProviderNetworkFilterSet)
class ProviderNetworkFilter(BaseFilterMixin):
pass

View File

@ -1,41 +1,40 @@
import graphene
from typing import List
import strawberry
import strawberry_django
from circuits import models
from netbox.graphql.fields import ObjectField, ObjectListField
from .types import *
from utilities.graphql_optimizer import gql_query_optimizer
class CircuitsQuery(graphene.ObjectType):
circuit = ObjectField(CircuitType)
circuit_list = ObjectListField(CircuitType)
@strawberry.type
class CircuitsQuery:
@strawberry.field
def circuit(self, id: int) -> CircuitType:
return models.Circuit.objects.get(pk=id)
circuit_list: List[CircuitType] = strawberry_django.field()
def resolve_circuit_list(root, info, **kwargs):
return gql_query_optimizer(models.Circuit.objects.all(), info)
@strawberry.field
def circuit_termination(self, id: int) -> CircuitTerminationType:
return models.CircuitTermination.objects.get(pk=id)
circuit_termination_list: List[CircuitTerminationType] = strawberry_django.field()
circuit_termination = ObjectField(CircuitTerminationType)
circuit_termination_list = ObjectListField(CircuitTerminationType)
@strawberry.field
def circuit_type(self, id: int) -> CircuitTypeType:
return models.CircuitType.objects.get(pk=id)
circuit_type_list: List[CircuitTypeType] = strawberry_django.field()
def resolve_circuit_termination_list(root, info, **kwargs):
return gql_query_optimizer(models.CircuitTermination.objects.all(), info)
@strawberry.field
def provider(self, id: int) -> ProviderType:
return models.Provider.objects.get(pk=id)
provider_list: List[ProviderType] = strawberry_django.field()
circuit_type = ObjectField(CircuitTypeType)
circuit_type_list = ObjectListField(CircuitTypeType)
@strawberry.field
def provider_account(self, id: int) -> ProviderAccountType:
return models.ProviderAccount.objects.get(pk=id)
provider_account_list: List[ProviderAccountType] = strawberry_django.field()
def resolve_circuit_type_list(root, info, **kwargs):
return gql_query_optimizer(models.CircuitType.objects.all(), info)
provider = ObjectField(ProviderType)
provider_list = ObjectListField(ProviderType)
def resolve_provider_list(root, info, **kwargs):
return gql_query_optimizer(models.Provider.objects.all(), info)
provider_account = ObjectField(ProviderAccountType)
provider_account_list = ObjectListField(ProviderAccountType)
provider_network = ObjectField(ProviderNetworkType)
provider_network_list = ObjectListField(ProviderNetworkType)
def resolve_provider_network_list(root, info, **kwargs):
return gql_query_optimizer(models.ProviderNetwork.objects.all(), info)
@strawberry.field
def provider_network(self, id: int) -> ProviderNetworkType:
return models.ProviderNetwork.objects.get(pk=id)
provider_network_list: List[ProviderNetworkType] = strawberry_django.field()

View File

@ -1,9 +1,14 @@
import graphene
from typing import Annotated, List
from circuits import filtersets, models
import strawberry
import strawberry_django
from circuits import models
from dcim.graphql.mixins import CabledObjectMixin
from extras.graphql.mixins import CustomFieldsMixin, TagsMixin, ContactsMixin
from netbox.graphql.types import ObjectType, OrganizationalObjectType, NetBoxObjectType
from extras.graphql.mixins import ContactsMixin, CustomFieldsMixin, TagsMixin
from netbox.graphql.types import NetBoxObjectType, ObjectType, OrganizationalObjectType
from tenancy.graphql.types import TenantType
from .filters import *
__all__ = (
'CircuitTerminationType',
@ -15,48 +20,74 @@ __all__ = (
)
class CircuitTerminationType(CustomFieldsMixin, TagsMixin, CabledObjectMixin, ObjectType):
class Meta:
model = models.CircuitTermination
fields = '__all__'
filterset_class = filtersets.CircuitTerminationFilterSet
class CircuitType(NetBoxObjectType, ContactsMixin):
class Meta:
model = models.Circuit
fields = '__all__'
filterset_class = filtersets.CircuitFilterSet
class CircuitTypeType(OrganizationalObjectType):
class Meta:
model = models.CircuitType
fields = '__all__'
filterset_class = filtersets.CircuitTypeFilterSet
@strawberry_django.type(
models.Provider,
fields='__all__',
filters=ProviderFilter
)
class ProviderType(NetBoxObjectType, ContactsMixin):
class Meta:
model = models.Provider
fields = '__all__'
filterset_class = filtersets.ProviderFilterSet
networks: List[Annotated["ProviderNetworkType", strawberry.lazy('circuits.graphql.types')]]
circuits: List[Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]]
asns: List[Annotated["ASNType", strawberry.lazy('ipam.graphql.types')]]
accounts: List[Annotated["ProviderAccountType", strawberry.lazy('circuits.graphql.types')]]
@strawberry_django.type(
models.ProviderAccount,
fields='__all__',
filters=ProviderAccountFilter
)
class ProviderAccountType(NetBoxObjectType):
provider: Annotated["ProviderType", strawberry.lazy('circuits.graphql.types')]
class Meta:
model = models.ProviderAccount
fields = '__all__'
filterset_class = filtersets.ProviderAccountFilterSet
circuits: List[Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]]
@strawberry_django.type(
models.ProviderNetwork,
fields='__all__',
filters=ProviderNetworkFilter
)
class ProviderNetworkType(NetBoxObjectType):
provider: Annotated["ProviderType", strawberry.lazy('circuits.graphql.types')]
class Meta:
model = models.ProviderNetwork
fields = '__all__'
filterset_class = filtersets.ProviderNetworkFilterSet
circuit_terminations: List[Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')]]
@strawberry_django.type(
models.CircuitTermination,
fields='__all__',
filters=CircuitTerminationFilter
)
class CircuitTerminationType(CustomFieldsMixin, TagsMixin, CabledObjectMixin, ObjectType):
circuit: Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]
provider_network: Annotated["ProviderNetworkType", strawberry.lazy('circuits.graphql.types')] | None
site: Annotated["SiteType", strawberry.lazy('dcim.graphql.types')] | None
@strawberry_django.type(
models.CircuitType,
fields='__all__',
filters=CircuitTypeFilter
)
class CircuitTypeType(OrganizationalObjectType):
color: str
circuits: List[Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]]
@strawberry_django.type(
models.Circuit,
fields='__all__',
filters=CircuitFilter
)
class CircuitType(NetBoxObjectType, ContactsMixin):
provider: ProviderType
provider_account: ProviderAccountType | None
termination_a: CircuitTerminationType | None
termination_z: CircuitTerminationType | None
type: CircuitTypeType
tenant: TenantType | None
terminations: List[CircuitTerminationType]

View File

@ -1,20 +0,0 @@
# Generated by Django 3.2.8 on 2021-10-21 14:50
from django.db import migrations
import taggit.managers
class Migration(migrations.Migration):
dependencies = [
('extras', '0062_clear_secrets_changelog'),
('circuits', '0002_squashed_0029'),
]
operations = [
migrations.AddField(
model_name='circuittype',
name='tags',
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
),
]

View File

@ -0,0 +1,127 @@
import taggit.managers
from django.db import migrations, models
import utilities.json
class Migration(migrations.Migration):
replaces = [
('circuits', '0003_extend_tag_support'),
('circuits', '0004_rename_cable_peer'),
('circuits', '0032_provider_service_id'),
('circuits', '0033_standardize_id_fields'),
('circuits', '0034_created_datetimefield'),
('circuits', '0035_provider_asns'),
('circuits', '0036_circuit_termination_date_tags_custom_fields'),
('circuits', '0037_new_cabling_models')
]
dependencies = [
('ipam', '0047_squashed_0053'),
('extras', '0002_squashed_0059'),
('circuits', '0002_squashed_0029'),
]
operations = [
migrations.AddField(
model_name='circuittype',
name='tags',
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
),
migrations.RenameField(
model_name='circuittermination',
old_name='_cable_peer_id',
new_name='_link_peer_id',
),
migrations.RenameField(
model_name='circuittermination',
old_name='_cable_peer_type',
new_name='_link_peer_type',
),
migrations.AddField(
model_name='providernetwork',
name='service_id',
field=models.CharField(blank=True, max_length=100),
),
migrations.AlterField(
model_name='circuit',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
),
migrations.AlterField(
model_name='circuittermination',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
),
migrations.AlterField(
model_name='circuittype',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
),
migrations.AlterField(
model_name='provider',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
),
migrations.AlterField(
model_name='providernetwork',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
),
migrations.AlterField(
model_name='circuittermination',
name='_link_peer_id',
field=models.PositiveBigIntegerField(blank=True, null=True),
),
migrations.AlterField(
model_name='circuit',
name='created',
field=models.DateTimeField(auto_now_add=True, null=True),
),
migrations.AlterField(
model_name='circuittermination',
name='created',
field=models.DateTimeField(auto_now_add=True, null=True),
),
migrations.AlterField(
model_name='circuittype',
name='created',
field=models.DateTimeField(auto_now_add=True, null=True),
),
migrations.AlterField(
model_name='provider',
name='created',
field=models.DateTimeField(auto_now_add=True, null=True),
),
migrations.AlterField(
model_name='providernetwork',
name='created',
field=models.DateTimeField(auto_now_add=True, null=True),
),
migrations.AddField(
model_name='provider',
name='asns',
field=models.ManyToManyField(blank=True, related_name='providers', to='ipam.asn'),
),
migrations.AddField(
model_name='circuit',
name='termination_date',
field=models.DateField(blank=True, null=True),
),
migrations.AddField(
model_name='circuittermination',
name='custom_field_data',
field=models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder),
),
migrations.AddField(
model_name='circuittermination',
name='tags',
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
),
migrations.AddField(
model_name='circuittermination',
name='cable_end',
field=models.CharField(blank=True, max_length=1),
),
]

View File

@ -1,21 +0,0 @@
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('circuits', '0003_extend_tag_support'),
]
operations = [
migrations.RenameField(
model_name='circuittermination',
old_name='_cable_peer_id',
new_name='_link_peer_id',
),
migrations.RenameField(
model_name='circuittermination',
old_name='_cable_peer_type',
new_name='_link_peer_type',
),
]

View File

@ -1,17 +0,0 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('circuits', '0004_rename_cable_peer'),
('dcim', '0145_site_remove_deprecated_fields'),
]
operations = [
migrations.AddField(
model_name='providernetwork',
name='service_id',
field=models.CharField(blank=True, max_length=100),
),
]

View File

@ -1,44 +0,0 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('circuits', '0032_provider_service_id'),
]
operations = [
# Model IDs
migrations.AlterField(
model_name='circuit',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
),
migrations.AlterField(
model_name='circuittermination',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
),
migrations.AlterField(
model_name='circuittype',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
),
migrations.AlterField(
model_name='provider',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
),
migrations.AlterField(
model_name='providernetwork',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False),
),
# GFK IDs
migrations.AlterField(
model_name='circuittermination',
name='_link_peer_id',
field=models.PositiveBigIntegerField(blank=True, null=True),
),
]

View File

@ -1,38 +0,0 @@
# Generated by Django 4.0.2 on 2022-02-08 18:54
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('circuits', '0033_standardize_id_fields'),
]
operations = [
migrations.AlterField(
model_name='circuit',
name='created',
field=models.DateTimeField(auto_now_add=True, null=True),
),
migrations.AlterField(
model_name='circuittermination',
name='created',
field=models.DateTimeField(auto_now_add=True, null=True),
),
migrations.AlterField(
model_name='circuittype',
name='created',
field=models.DateTimeField(auto_now_add=True, null=True),
),
migrations.AlterField(
model_name='provider',
name='created',
field=models.DateTimeField(auto_now_add=True, null=True),
),
migrations.AlterField(
model_name='providernetwork',
name='created',
field=models.DateTimeField(auto_now_add=True, null=True),
),
]

View File

@ -1,19 +0,0 @@
# Generated by Django 4.0.3 on 2022-03-30 20:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('ipam', '0057_created_datetimefield'),
('circuits', '0034_created_datetimefield'),
]
operations = [
migrations.AddField(
model_name='provider',
name='asns',
field=models.ManyToManyField(blank=True, related_name='providers', to='ipam.asn'),
),
]

View File

@ -1,28 +0,0 @@
from utilities.json import CustomFieldJSONEncoder
from django.db import migrations, models
import taggit.managers
class Migration(migrations.Migration):
dependencies = [
('circuits', '0035_provider_asns'),
]
operations = [
migrations.AddField(
model_name='circuit',
name='termination_date',
field=models.DateField(blank=True, null=True),
),
migrations.AddField(
model_name='circuittermination',
name='custom_field_data',
field=models.JSONField(blank=True, default=dict, encoder=CustomFieldJSONEncoder),
),
migrations.AddField(
model_name='circuittermination',
name='tags',
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
),
]

View File

@ -1,16 +0,0 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('circuits', '0036_circuit_termination_date_tags_custom_fields'),
]
operations = [
migrations.AddField(
model_name='circuittermination',
name='cable_end',
field=models.CharField(blank=True, max_length=1),
),
]

View File

@ -1,20 +0,0 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('circuits', '0037_new_cabling_models'),
('dcim', '0160_populate_cable_ends'),
]
operations = [
migrations.RemoveField(
model_name='circuittermination',
name='_link_peer_id',
),
migrations.RemoveField(
model_name='circuittermination',
name='_link_peer_type',
),
]

View File

@ -1,46 +1,83 @@
from django.db import migrations, models
import django.db.models.deletion
import taggit.managers
from django.db import migrations, models
import utilities.json
def create_provideraccounts_from_providers(apps, schema_editor):
"""
Migrate Account in Provider model to separate account model
"""
Provider = apps.get_model('circuits', 'Provider')
ProviderAccount = apps.get_model('circuits', 'ProviderAccount')
provider_accounts = []
for provider in Provider.objects.all():
if provider.account:
provider_accounts.append(ProviderAccount(
provider=provider,
account=provider.account
))
ProviderAccount.objects.bulk_create(provider_accounts, batch_size=100)
def restore_providers_from_provideraccounts(apps, schema_editor):
"""
Restore Provider account values from auto-generated ProviderAccounts
"""
ProviderAccount = apps.get_model('circuits', 'ProviderAccount')
provider_accounts = ProviderAccount.objects.order_by('pk')
for provideraccount in provider_accounts:
if provider_accounts.filter(provider=provideraccount.provider)[0] == provideraccount:
provideraccount.provider.account = provideraccount.account
provideraccount.provider.save()
class Migration(migrations.Migration):
dependencies = [
('extras', '0084_staging'),
replaces = [
('circuits', '0038_cabling_cleanup'),
('circuits', '0039_unique_constraints'),
('circuits', '0040_provider_remove_deprecated_fields'),
('circuits', '0041_standardize_description_comments'),
('circuits', '0042_provideraccount')
]
dependencies = [
('circuits', '0037_new_cabling_models'),
('dcim', '0160_populate_cable_ends'),
]
operations = [
migrations.RemoveField(
model_name='circuittermination',
name='_link_peer_id',
),
migrations.RemoveField(
model_name='circuittermination',
name='_link_peer_type',
),
migrations.RemoveConstraint(
model_name='providernetwork',
name='circuits_providernetwork_provider_name',
),
migrations.AlterUniqueTogether(
name='circuit',
unique_together=set(),
),
migrations.AlterUniqueTogether(
name='circuittermination',
unique_together=set(),
),
migrations.AlterUniqueTogether(
name='providernetwork',
unique_together=set(),
),
migrations.AddConstraint(
model_name='circuit',
constraint=models.UniqueConstraint(fields=('provider', 'cid'), name='circuits_circuit_unique_provider_cid'),
),
migrations.AddConstraint(
model_name='circuittermination',
constraint=models.UniqueConstraint(fields=('circuit', 'term_side'), name='circuits_circuittermination_unique_circuit_term_side'),
),
migrations.AddConstraint(
model_name='providernetwork',
constraint=models.UniqueConstraint(fields=('provider', 'name'), name='circuits_providernetwork_unique_provider_name'),
),
migrations.RemoveField(
model_name='provider',
name='admin_contact',
),
migrations.RemoveField(
model_name='provider',
name='asn',
),
migrations.RemoveField(
model_name='provider',
name='noc_contact',
),
migrations.RemoveField(
model_name='provider',
name='portal_url',
),
migrations.AddField(
model_name='provider',
name='description',
field=models.CharField(blank=True, max_length=200),
),
migrations.CreateModel(
name='ProviderAccount',
fields=[
@ -67,9 +104,6 @@ class Migration(migrations.Migration):
model_name='provideraccount',
constraint=models.UniqueConstraint(fields=('provider', 'account'), name='circuits_provideraccount_unique_provider_account'),
),
migrations.RunPython(
create_provideraccounts_from_providers, restore_providers_from_provideraccounts
),
migrations.RemoveField(
model_name='provider',
name='account',
@ -77,7 +111,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='circuit',
name='provider_account',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='circuits', to='circuits.provideraccount', null=True, blank=True),
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='circuits', to='circuits.provideraccount'),
preserve_default=False,
),
migrations.AlterModelOptions(

View File

@ -1,39 +0,0 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('circuits', '0038_cabling_cleanup'),
]
operations = [
migrations.RemoveConstraint(
model_name='providernetwork',
name='circuits_providernetwork_provider_name',
),
migrations.AlterUniqueTogether(
name='circuit',
unique_together=set(),
),
migrations.AlterUniqueTogether(
name='circuittermination',
unique_together=set(),
),
migrations.AlterUniqueTogether(
name='providernetwork',
unique_together=set(),
),
migrations.AddConstraint(
model_name='circuit',
constraint=models.UniqueConstraint(fields=('provider', 'cid'), name='circuits_circuit_unique_provider_cid'),
),
migrations.AddConstraint(
model_name='circuittermination',
constraint=models.UniqueConstraint(fields=('circuit', 'term_side'), name='circuits_circuittermination_unique_circuit_term_side'),
),
migrations.AddConstraint(
model_name='providernetwork',
constraint=models.UniqueConstraint(fields=('provider', 'name'), name='circuits_providernetwork_unique_provider_name'),
),
]

View File

@ -1,59 +0,0 @@
import os
from django.db import migrations
from django.db.utils import DataError
def check_legacy_data(apps, schema_editor):
"""
Abort the migration if any legacy provider fields still contain data.
"""
Provider = apps.get_model('circuits', 'Provider')
provider_count = Provider.objects.exclude(asn__isnull=True).count()
if provider_count and 'NETBOX_DELETE_LEGACY_DATA' not in os.environ:
raise DataError(
f"Unable to proceed with deleting asn field from Provider model: Found {provider_count} "
f"providers with legacy ASN data. Please ensure all legacy provider ASN data has been "
f"migrated to ASN objects before proceeding. Or, set the NETBOX_DELETE_LEGACY_DATA "
f"environment variable to bypass this safeguard and delete all legacy provider ASN data."
)
provider_count = Provider.objects.exclude(admin_contact='', noc_contact='', portal_url='').count()
if provider_count and 'NETBOX_DELETE_LEGACY_DATA' not in os.environ:
raise DataError(
f"Unable to proceed with deleting contact fields from Provider model: Found {provider_count} "
f"providers with legacy contact data. Please ensure all legacy provider contact data has been "
f"migrated to contact objects before proceeding. Or, set the NETBOX_DELETE_LEGACY_DATA "
f"environment variable to bypass this safeguard and delete all legacy provider contact data."
)
class Migration(migrations.Migration):
dependencies = [
('circuits', '0039_unique_constraints'),
]
operations = [
migrations.RunPython(
code=check_legacy_data,
reverse_code=migrations.RunPython.noop
),
migrations.RemoveField(
model_name='provider',
name='admin_contact',
),
migrations.RemoveField(
model_name='provider',
name='asn',
),
migrations.RemoveField(
model_name='provider',
name='noc_contact',
),
migrations.RemoveField(
model_name='provider',
name='portal_url',
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 4.1.2 on 2022-11-03 18:24
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('circuits', '0040_provider_remove_deprecated_fields'),
]
operations = [
migrations.AddField(
model_name='provider',
name='description',
field=models.CharField(blank=True, max_length=200),
),
]

View File

@ -18,7 +18,7 @@ class AppTest(APITestCase):
class ProviderTest(APIViewTestCases.APIViewTestCase):
model = Provider
brief_fields = ['circuit_count', 'display', 'id', 'name', 'slug', 'url']
brief_fields = ['circuit_count', 'description', 'display', 'id', 'name', 'slug', 'url']
bulk_update_data = {
'comments': 'New comments',
}
@ -60,7 +60,7 @@ class ProviderTest(APIViewTestCases.APIViewTestCase):
class CircuitTypeTest(APIViewTestCases.APIViewTestCase):
model = CircuitType
brief_fields = ['circuit_count', 'display', 'id', 'name', 'slug', 'url']
brief_fields = ['circuit_count', 'description', 'display', 'id', 'name', 'slug', 'url']
create_data = (
{
'name': 'Circuit Type 4',
@ -92,7 +92,7 @@ class CircuitTypeTest(APIViewTestCases.APIViewTestCase):
class CircuitTest(APIViewTestCases.APIViewTestCase):
model = Circuit
brief_fields = ['cid', 'display', 'id', 'url']
brief_fields = ['cid', 'description', 'display', 'id', 'url']
bulk_update_data = {
'status': 'planned',
}
@ -141,7 +141,7 @@ class CircuitTest(APIViewTestCases.APIViewTestCase):
{
'cid': 'Circuit 6',
'provider': providers[1].pk,
'provider_account': provider_accounts[1].pk,
# Omit provider account to test uniqueness constraint
'type': circuit_types[1].pk,
},
]
@ -149,7 +149,7 @@ class CircuitTest(APIViewTestCases.APIViewTestCase):
class CircuitTerminationTest(APIViewTestCases.APIViewTestCase):
model = CircuitTermination
brief_fields = ['_occupied', 'cable', 'circuit', 'display', 'id', 'term_side', 'url']
brief_fields = ['_occupied', 'cable', 'circuit', 'description', 'display', 'id', 'term_side', 'url']
@classmethod
def setUpTestData(cls):
@ -208,7 +208,7 @@ class CircuitTerminationTest(APIViewTestCases.APIViewTestCase):
class ProviderAccountTest(APIViewTestCases.APIViewTestCase):
model = ProviderAccount
brief_fields = ['account', 'display', 'id', 'name', 'url']
brief_fields = ['account', 'description', 'display', 'id', 'name', 'url']
@classmethod
def setUpTestData(cls):
@ -237,7 +237,7 @@ class ProviderAccountTest(APIViewTestCases.APIViewTestCase):
'account': '5678',
},
{
'name': 'Provider Account 6',
# Omit name to test uniqueness constraint
'provider': providers[0].pk,
'account': '6789',
},
@ -251,7 +251,7 @@ class ProviderAccountTest(APIViewTestCases.APIViewTestCase):
class ProviderNetworkTest(APIViewTestCases.APIViewTestCase):
model = ProviderNetwork
brief_fields = ['display', 'id', 'name', 'url']
brief_fields = ['description', 'display', 'id', 'name', 'url']
@classmethod
def setUpTestData(cls):

View File

@ -332,6 +332,7 @@ class CircuitTestCase(TestCase, ChangeLoggedFilterSetTests):
class CircuitTerminationTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = CircuitTermination.objects.all()
filterset = CircuitTerminationFilterSet
ignore_fields = ('cable',)
@classmethod
def setUpTestData(cls):

View File

@ -6,7 +6,7 @@ from dcim.views import PathTraceView
from netbox.views import generic
from tenancy.views import ObjectContactsView
from utilities.forms import ConfirmationForm
from utilities.utils import count_related
from utilities.query import count_related
from utilities.views import register_model_view
from . import filtersets, forms, tables
from .models import *
@ -412,7 +412,6 @@ class CircuitContactsView(ObjectContactsView):
class CircuitTerminationEditView(generic.ObjectEditView):
queryset = CircuitTermination.objects.all()
form = forms.CircuitTerminationForm
template_name = 'circuits/circuittermination_edit.html'
@register_model_view(CircuitTermination, 'delete')

View File

@ -4,7 +4,7 @@ from core.choices import JobStatusChoices
from core.models import *
from netbox.api.fields import ChoiceField
from netbox.api.serializers import WritableNestedSerializer
from users.api.nested_serializers import NestedUserSerializer
from users.api.serializers import UserSerializer
__all__ = (
'NestedDataFileSerializer',
@ -32,7 +32,8 @@ class NestedDataFileSerializer(WritableNestedSerializer):
class NestedJobSerializer(serializers.ModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='core-api:job-detail')
status = ChoiceField(choices=JobStatusChoices)
user = NestedUserSerializer(
user = UserSerializer(
nested=True,
read_only=True
)

View File

@ -47,6 +47,13 @@ class ChoiceFieldFix(OpenApiSerializerFieldExtension):
)
class SerializedPKRelatedFieldFix(OpenApiSerializerFieldExtension):
target_class = "netbox.api.fields.SerializedPKRelatedField"
def map_serializer_field(self, auto_schema, direction):
return auto_schema._map_serializer(self.target.serializer, direction)
class NetBoxAutoSchema(AutoSchema):
"""
Overrides to drf_spectacular.openapi.AutoSchema to fix following issues:
@ -156,8 +163,6 @@ class NetBoxAutoSchema(AutoSchema):
remove_fields.append(child_name)
if isinstance(child, (ChoiceField, WritableNestedSerializer)):
properties[child_name] = None
elif isinstance(child, ManyRelatedField) and isinstance(child.child_relation, SerializedPKRelatedField):
properties[child_name] = None
if not properties:
return None

View File

@ -1,73 +1,3 @@
from rest_framework import serializers
from core.choices import *
from core.models import *
from netbox.api.fields import ChoiceField, ContentTypeField
from netbox.api.serializers import BaseModelSerializer, NetBoxModelSerializer
from netbox.utils import get_data_backend_choices
from users.api.nested_serializers import NestedUserSerializer
from .serializers_.data import *
from .serializers_.jobs import *
from .nested_serializers import *
__all__ = (
'DataFileSerializer',
'DataSourceSerializer',
'JobSerializer',
)
class DataSourceSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='core-api:datasource-detail'
)
type = ChoiceField(
choices=get_data_backend_choices()
)
status = ChoiceField(
choices=DataSourceStatusChoices,
read_only=True
)
# Related object counts
file_count = serializers.IntegerField(
read_only=True
)
class Meta:
model = DataSource
fields = [
'id', 'url', 'display', 'name', 'type', 'source_url', 'enabled', 'status', 'description', 'comments',
'parameters', 'ignore_rules', 'custom_fields', 'created', 'last_updated', 'file_count',
]
class DataFileSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='core-api:datafile-detail'
)
source = NestedDataSourceSerializer(
read_only=True
)
class Meta:
model = DataFile
fields = [
'id', 'url', 'display', 'source', 'path', 'last_updated', 'size', 'hash',
]
class JobSerializer(BaseModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='core-api:job-detail')
user = NestedUserSerializer(
read_only=True
)
status = ChoiceField(choices=JobStatusChoices, read_only=True)
object_type = ContentTypeField(
read_only=True
)
class Meta:
model = Job
fields = [
'id', 'url', 'display', 'object_type', 'object_id', 'name', 'status', 'created', 'scheduled', 'interval',
'started', 'completed', 'user', 'data', 'error', 'job_id',
]

View File

View File

@ -0,0 +1,53 @@
from rest_framework import serializers
from core.choices import *
from core.models import DataFile, DataSource
from netbox.api.fields import ChoiceField, RelatedObjectCountField
from netbox.api.serializers import NetBoxModelSerializer
from netbox.utils import get_data_backend_choices
__all__ = (
'DataFileSerializer',
'DataSourceSerializer',
)
class DataSourceSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='core-api:datasource-detail'
)
type = ChoiceField(
choices=get_data_backend_choices()
)
status = ChoiceField(
choices=DataSourceStatusChoices,
read_only=True
)
# Related object counts
file_count = RelatedObjectCountField('datafiles')
class Meta:
model = DataSource
fields = [
'id', 'url', 'display', 'name', 'type', 'source_url', 'enabled', 'status', 'description', 'comments',
'parameters', 'ignore_rules', 'custom_fields', 'created', 'last_updated', 'file_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')
class DataFileSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='core-api:datafile-detail'
)
source = DataSourceSerializer(
nested=True,
read_only=True
)
class Meta:
model = DataFile
fields = [
'id', 'url', 'display', 'source', 'path', 'last_updated', 'size', 'hash',
]
brief_fields = ('id', 'url', 'display', 'path')

View File

@ -0,0 +1,31 @@
from rest_framework import serializers
from core.choices import *
from core.models import Job
from netbox.api.fields import ChoiceField, ContentTypeField
from netbox.api.serializers import BaseModelSerializer
from users.api.serializers_.users import UserSerializer
__all__ = (
'JobSerializer',
)
class JobSerializer(BaseModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='core-api:job-detail')
user = UserSerializer(
nested=True,
read_only=True
)
status = ChoiceField(choices=JobStatusChoices, read_only=True)
object_type = ContentTypeField(
read_only=True
)
class Meta:
model = Job
fields = [
'id', 'url', 'display', 'object_type', 'object_id', 'name', 'status', 'created', 'scheduled', 'interval',
'started', 'completed', 'user', 'data', 'error', 'job_id',
]
brief_fields = ('url', 'created', 'completed', 'user', 'status')

View File

@ -9,7 +9,6 @@ from rest_framework.viewsets import ReadOnlyModelViewSet
from core import filtersets
from core.models import *
from netbox.api.viewsets import NetBoxModelViewSet, NetBoxReadOnlyModelViewSet
from utilities.utils import count_related
from . import serializers
@ -22,9 +21,7 @@ class CoreRootView(APIRootView):
class DataSourceViewSet(NetBoxModelViewSet):
queryset = DataSource.objects.annotate(
file_count=count_related(DataFile, 'source')
)
queryset = DataSource.objects.all()
serializer_class = serializers.DataSourceSerializer
filterset_class = filtersets.DataSourceFilterSet
@ -45,7 +42,7 @@ class DataSourceViewSet(NetBoxModelViewSet):
class DataFileViewSet(NetBoxReadOnlyModelViewSet):
queryset = DataFile.objects.defer('data').prefetch_related('source')
queryset = DataFile.objects.defer('data')
serializer_class = serializers.DataFileSerializer
filterset_class = filtersets.DataFileFilterSet
@ -54,6 +51,6 @@ class JobViewSet(ReadOnlyModelViewSet):
"""
Retrieve a list of job results
"""
queryset = Job.objects.prefetch_related('user')
queryset = Job.objects.all()
serializer_class = serializers.JobSerializer
filterset_class = filtersets.JobFilterSet

View File

@ -16,5 +16,9 @@ class CoreConfig(AppConfig):
name = "core"
def ready(self):
from core.api import schema # noqa
from netbox.models.features import register_models
from . import data_backends, search
from core.api import schema # noqa: E402
# Register models
register_models(*self.get_models())

26
netbox/core/constants.py Normal file
View File

@ -0,0 +1,26 @@
from dataclasses import dataclass
from django.utils.translation import gettext_lazy as _
from rq.job import JobStatus
__all__ = (
'RQ_TASK_STATUSES',
)
@dataclass
class Status:
label: str
color: str
RQ_TASK_STATUSES = {
JobStatus.QUEUED: Status(_('Queued'), 'cyan'),
JobStatus.FINISHED: Status(_('Finished'), 'green'),
JobStatus.FAILED: Status(_('Failed'), 'red'),
JobStatus.STARTED: Status(_('Started'), 'blue'),
JobStatus.DEFERRED: Status(_('Deferred'), 'gray'),
JobStatus.SCHEDULED: Status(_('Scheduled'), 'purple'),
JobStatus.STOPPED: Status(_('Stopped'), 'orange'),
JobStatus.CANCELED: Status(_('Cancelled'), 'yellow'),
}

Some files were not shown because too many files have changed in this diff Show More