Compare commits

...

237 Commits

Author SHA1 Message Date
Jeremy Stretch
7bc0d34196 Merge pull request #17350 from netbox-community/develop
Release v4.1.0
2024-09-03 15:09:35 -04:00
Jeremy Stretch
9cfb3bf24d Release v4.1.0 2024-09-03 14:45:28 -04:00
Jeremy Stretch
8707c44a85 Merge pull request #17349 from netbox-community/feature
Move to v4.1
2024-09-03 14:18:51 -04:00
Jeremy Stretch
16dfafa35b Update changelog 2024-09-03 13:56:29 -04:00
Jeremy Stretch
39c5f57c1e Fix broken link 2024-09-03 13:36:39 -04:00
Jeremy Stretch
7c0bdf3fb8 #17289: Document new default configuration 2024-09-03 12:17:56 -04:00
Jeremy Stretch
a777850702 Merge branch 'develop' into feature 2024-09-03 11:59:59 -04:00
Jeremy Stretch
0b120e6ad2 Merge pull request #17346 from netbox-community/develop
Release v4.0.11
2024-09-03 10:50:46 -04:00
Jeremy Stretch
e174a8af09 Release v4.0.11 2024-09-03 10:33:47 -04:00
Jeremy Stretch
77d1dc4807 Compile translations 2024-09-03 10:16:26 -04:00
dependabot[bot]
2f1798c7de Bump micromatch from 4.0.5 to 4.0.8 in /netbox/project-static
Bumps [micromatch](https://github.com/micromatch/micromatch) from 4.0.5 to 4.0.8.
- [Release notes](https://github.com/micromatch/micromatch/releases)
- [Changelog](https://github.com/micromatch/micromatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/micromatch/compare/4.0.5...4.0.8)

---
updated-dependencies:
- dependency-name: micromatch
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-03 10:12:50 -04:00
transifex-integration[bot]
52b7f62b10 Updates for project NetBox (#17345)
* Translate django.po in es

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

* Translate django.po in ru

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

* Translate django.po in ja

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

* Translate django.po in cs

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

* Translate django.po in da

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

* Translate django.po in nl

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

* Translate django.po in de

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

* Translate django.po in pl

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

* Translate django.po in it

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

* Translate django.po in tr

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

* Translate django.po in uk

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

* Translate django.po in pt

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

* Translate django.po in fr

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

* Translate django.po in zh

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

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-09-03 10:11:56 -04:00
Brian Candler
7a2ff96abe Hide exception in ObjectCountsWidget for models without a xxx_list view function (#17342)
* Hide exception in ObjectCountsWidget for models without a `xxx_list` view function

Fixes #17341

* Disable hyperlink for invalid view names

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-09-03 09:38:06 -04:00
Jeremy Stretch
814b699204 Fixes #17323: Associate job with script object when executed using runscript command 2024-09-03 09:20:18 -04:00
bctiemann
ce33e0bc0c Fixes: #17320 - New screenshots for 4.1 UI (#17331)
* New screenshots for 4.1

* Remove footer info

* Add bookmarks to dashboard to match previous

* Reverse ordering of bookmarks

* Fix colors of cables in cable-trace.png

* New screenshots for 4.1

* Recapture screenshots with better rendering gradient

* Better sidebar on cable-trace.png
2024-09-03 09:03:41 -04:00
Jeremy Stretch
d18a853baa Fixes #17337: Fix ordering of virtual device contexts by device name 2024-09-03 08:58:13 -04:00
Arzhel Younsi
b2bbdbf1d9 runscript: use the previously defined "user"
There is already a logic set earlier in the code to define "user" if --user is passed as parameter, and default to the user with the lowest ID no none is provided.
This patch uses this "user" to run the job instead of always applying the default.
2024-09-03 08:42:37 -04:00
Jeremy Stretch
56f110c2a9 Closes #17288: Limit the number of aliases within a GraphQL API requests to 10 (#17329)
* Closes #17288: Limit the number of aliases within a GraphQL API request to 10

* Introduce GRAPHQL_MAX_ALIASES config parameter
2024-09-02 09:30:41 -04:00
Jeremy Stretch
31d5d8c395 Fixes #17301: Fix script module edit & delete button styling (#17328) 2024-09-02 08:38:16 -04:00
Arthur Hanson
00874ac9e7 17289 enforce minimum password strength (#17299)
* 17289 add password validation

* 17289 add password validation

* 17289 fix tests

* 17289 fix tests

* Update netbox/utilities/password_validation.py

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

* Update netbox/utilities/password_validation.py

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

* Update netbox/utilities/password_validation.py

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

* 17289 update tests

* 17289 remove common password check

* 17289 fix user create

* 17289 revert _post_clean

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-08-30 17:23:01 -04:00
Daniel Sheppard
4fead1c85f Fixes: #17310 - Properly restrict GraphQL related object queries (#17312)
* Fixes: #17310 - Fix GraphQL restriction of related objects

* Fix some failing tests

* Fix test
2024-08-30 14:22:58 -04:00
Jeremy Stretch
b4dd57f3c7 #8198: Rename CustomField.validation_unique to unique (#17325)
* #8198: Rename CustomField.validation_unique to unique

* Update CustomField model documentation
2024-08-30 13:44:03 -04:00
Arthur Hanson
8fff4e2a5d 12826 make RackType.form_factor required (#17327)
* 12826 make RackType.form_factor required

* 12826 fix tests
2024-08-30 13:43:07 -04:00
Jeremy Stretch
6db29880cc Update changelog 2024-08-30 13:31:41 -04:00
Arthur Hanson
829bef041d 11969 remove airflow from racktype (#17324)
* 11969 remove airflow from racktype

* 11969 remove airflow from racktype

* 11969 update docs

* 11969 fix rack edit

* Rename migration

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-08-30 13:30:51 -04:00
Jeremy Stretch
b797fcc03d Closes #17318: Include provider in nested API representation of circuits 2024-08-30 13:13:33 -04:00
Jeremy Stretch
af93b47f94 #17051: Tag ISOLATED_DEPLOYMENT as a v4.1 feature 2024-08-30 10:19:22 -04:00
Jeremy Stretch
10a74492f6 #17143: Update docs for nested serializer use 2024-08-30 10:18:29 -04:00
Jeremy Stretch
a6b1f97525 #16782: Fix help text on form field 2024-08-30 09:36:27 -04:00
Jeremy Stretch
a09c1667ff #15106: Put distance & distance_unit on same line in model form 2024-08-30 08:40:16 -04:00
github-actions
91ad3f22a3 Update source translation strings 2024-08-30 05:02:15 +00:00
Jeremy Stretch
cdf2fdee83 #14692: Correct VirtualDisk model docs 2024-08-29 16:48:48 -04:00
Arthur Hanson
2f53dc0095 17302 fix log level filtering 2024-08-29 15:58:13 -04:00
Arthur Hanson
a9a926cc72 17306 fix vlan group utilization 2024-08-29 15:56:06 -04:00
Jeremy Stretch
00d8eb40fc #11969: Cleanup (#17308)
* Shorten choice constant names

* Add filter tests for airflow
2024-08-29 15:47:11 -04:00
Jeremy Stretch
db6246a437 #10500: Cleanup (#17305)
* Use MPTTColumn for module bays

* Fix return URLs

* Omit extraneous relationships from nested serializers

* Add note re: v4.1
2024-08-29 15:38:53 -04:00
Jeremy Stretch
9a11191ac5 #7025: Cleanup 2024-08-29 15:38:16 -04:00
Jeremy Stretch
6f7bf5baf4 Merge branch 'develop' into feature 2024-08-29 10:51:38 -04:00
Jeremy Stretch
31f167d0f9 PRVB 2024-08-29 09:51:06 -04:00
Jeremy Stretch
e93e9ac4a0 Merge pull request #17297 from netbox-community/develop
Release v4.0.10
2024-08-29 09:47:31 -04:00
Jeremy Stretch
97cd6b89fd Release v4.0.10 2024-08-29 09:25:58 -04:00
transifex-integration[bot]
3684e011e6 Updates for project NetBox (#17296)
* Translate django.po in cs

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

* Translate django.po in es

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

* Translate django.po in de

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

* Translate django.po in da

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

* Translate django.po in fr

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

* Translate django.po in it

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

* Translate django.po in uk

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

* Translate django.po in pt

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

* Translate django.po in zh

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

* Translate django.po in ja

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

* Translate django.po in nl

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

* Translate django.po in tr

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

* Translate django.po in pl

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

* Translate django.po in ru

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

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-08-29 09:21:52 -04:00
Jeremy Stretch
a9fd5bbf55 Fix broken intra-page links 2024-08-29 09:13:02 -04:00
Jeremy Stretch
e325a4b2e0 Update change log 2024-08-29 09:01:23 -04:00
bctiemann
a150e5d561 Fixes: #16905 - Allow filtering on Device Status in InventoryItemTable (#17260)
* Add device_status as filtering option (and configurable column) for InventoryItemTable

* Add device_status to common superclasses for Device Components, and refactor ChoiceFieldColumn to support a "color" callable allowing get_FOO_color behavior to be overridden

* Remove unnecessary 'device_status' in fields

* Add unit tests for device_status
2024-08-29 08:10:30 -04:00
Jeremy Stretch
8282a6ddfe Fixes #17279: Do not regenerate key when updating a token via REST API 2024-08-29 07:51:09 -04:00
github-actions
07b1362b5e Update source translation strings 2024-08-29 05:02:20 +00:00
Jeremy Stretch
e3e351d1f0 Fixes #17278: Rebuild the tree after bulk editing MPTT-enabled object types 2024-08-28 16:25:36 -04:00
Jeremy Stretch
50839fcb6b Fixes #16756: Fix table pagination for custom script results 2024-08-28 16:24:16 -04:00
Arthur Hanson
cac92352ca 17286 fix add member to virtual chassis 2024-08-28 12:29:04 -04:00
Daniel Sheppard
0464dacf7e Fixes: #16292 - Properly restrict GraphQL queries for querys with pk set (#17244)
* Fixes: #16292 - Properly restrict GraphQL queries for querys with pk set

* Update netbox/netbox/settings.py

* Apply schema adaptations across all apps

* Extend GraphQL API tests

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-08-28 12:23:25 -04:00
github-actions
cf62178471 Update source translation strings 2024-08-28 05:02:04 +00:00
Jeremy Stretch
c3276b7d2e Update changelog 2024-08-27 17:09:16 -04:00
Arthur Hanson
7bae448eaf 16733 fix bulk edit/delete buttons with quick search (#17130)
* 16733 fix bulk edit/delete buttons with quick search

* 16733 fix bulk edit/delete buttons with quick search

* 16733 fix bulk edit/delete buttons with quick search

* Wrap bulk action buttons with .bulk-action-buttons for replacement via HTMX

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-08-27 16:50:05 -04:00
mcmehrtens
5ebdb7c9d2 Closes #17007: Center SSO if display name is an empty string 2024-08-27 16:41:40 -04:00
Arthur Hanson
0157ac6c9b 17186 change custom link button color from outline-dark to outline-se… (#17248)
* 17186 change custom link button color from outline-dark to outline-secondary

* 17186 change choice to default

* 17186 change choice to default

* 17186 change choice to default

* Misc cleanup

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-08-27 15:08:39 -04:00
Arthur Hanson
d23b9370f6 17219 fix custom validator display if function (#17247)
* 17219 fix custom validator display if function

* 17219 fix custom validator display if function

* 17219 use custom json encoder

* Fix system config export

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-08-27 14:19:16 -04:00
Jeremy Stretch
c2d67fa17e Closes #17256: Fix translation support in VLAN group scope assignment form (#17270)
* Closes #17256: Fix translation support in VLAN group scope assignment form

* Disable scope field if scope type not selected; update label on type change

* Reset selected scope object when changing scope type
2024-08-27 13:58:56 -04:00
Arthur Hanson
4f225b4e56 16670 fix OpenAPI schema generation with nested serializers (#17078)
* 16670 fix OpenAPI schema generation with nested serializers

* 16670 fix comment

* Add comment; misc cleanup

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-08-27 12:57:14 -04:00
Jeremy Stretch
263664ae52 Update changelog 2024-08-27 12:08:02 -04:00
Arthur Hanson
0238aeec22 16959 Fix filter reset button (#17154)
* 16959 fix filter reset button

* 16959 fix filter reset button

* Move reset button initialization logic to initFormElements()

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-08-27 12:01:16 -04:00
Arthur Hanson
3fee28cd5e 16946 return empty queryset if filterset is not valid (#17015)
* 16946 raise error if filterset is not valid

* 16946 cleanup

* 16946 change to None qs return and add test

* Remove obsolete logic

* Clean up test case

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-08-27 10:59:14 -04:00
Jeremy Stretch
515d041560 Fixes #17230: Ensure consistent rendering for all dashboard widget colors 2024-08-27 10:14:07 -04:00
Jonathan Senecal
8bea914163 Update related_objects.html 2024-08-27 09:35:21 -04:00
Jeremy Stretch
420613daed Fixes #16385: Restore support for white, gray, and black background colors 2024-08-27 09:12:40 -04:00
github-actions
fd013d6c5c Update source translation strings 2024-08-27 05:02:05 +00:00
bctiemann
a7f83de8c4 Fixes: #16973 - Resolve $user token to User.id for use in permissions based on custom fields (#17268)
* Resolve $user token to User.id for use in permissions based on custom fields

* Cleaner type check

* Simplify User object check by updating tokens instead of resolved values
2024-08-26 16:41:58 -04:00
Jeremy Stretch
ee0af15073 Update changelog 2024-08-26 15:41:12 -04:00
Arthur Hanson
35e2cf9cec 17117 change caret to html entity to fix safari performacne issue (#17246)
* change caret to html entity to fix safari performacne issue

* change caret to html entity to fix safari performacne issue

* 17117 use material icon

* 17117 use material icon

* 17117 use material icon

* 17117 fix vertical align

* Add comments & tweak padding

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-08-26 15:36:04 -04:00
bctiemann
1d2ea90fd4 Fixes: #17108 - Update isotime and isodate filters to be timezone-aware (#17267)
* Update isotime and isodate filters to be timezone-aware for display in templates (particularly Scripts)

* Handle naive datetimes gracefully
2024-08-26 14:37:20 -04:00
bctiemann
dab27695b9 Add styling to rendered-markdown class to clamp height and add scrollbar (#17237)
* Add clamp_height to the markdown filter which limits max height to 200 (scrollable) on the container div

* Remove clamp_height option, apply scrolling style to all markdown divs inside td's
2024-08-23 09:35:01 -04:00
Jeremy Stretch
91df0c0686 Closes #17221: Extend ObjectEditView to support HTMX requests 2024-08-23 08:26:19 -04:00
github-actions
d4dd86eb04 Update source translation strings 2024-08-23 05:02:05 +00:00
Brian Tiemann
5bd4fc862d Add a Devices column with device_count hyperlink (via count_related) to SiteListView 2024-08-22 13:03:49 -04:00
Brian Tiemann
85f8364cd7 Set ImageAttachmentSerializer.image_width and image_height to read_only 2024-08-22 10:59:52 -04:00
bctiemann
8903e4649c Fixes: #17072 - Make active links of phone and email in Contact Assignments table (#17233)
* Add linkify_email and apply with linkify_phone to ContactAssignmentTable

* Use EmailColumn instead of linkify_email
2024-08-22 10:57:53 -04:00
Brian Tiemann
38a26a7908 Add "facility" field to LocationFilterForm 2024-08-22 10:56:27 -04:00
Arthur Hanson
fb6c7d7619 17174 add version to plugin catalog (#17192)
* 17174 add version to plugin catalog

* Retain the created & updated columns for optional use

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-08-19 12:01:54 -04:00
Arthur Hanson
3412139acc 17193 update logo in Readme (#17194)
* 17193 update logo in Readme

* 17193 update logo in Readme

* 17193 remove .png

* 17194 favicon, apple
2024-08-19 11:22:40 -04:00
Jeremy Stretch
96802b4edb Remove automatic triage assignment 2024-08-19 08:31:31 -04:00
github-actions
e656e2da24 Update source translation strings 2024-08-17 05:03:01 +00:00
Arthur Hanson
c457f01b19 16640 fix JSON custom field save nul (#16713)
* 16640 fix JSON custom field save nul

* 16640 explicitly check against None

* 16640 convert JSON field from str to dict
2024-08-16 10:29:57 -04:00
Jeremy Stretch
8e70e3ad20 Update release checklist for v4.1 2024-08-15 12:59:44 -04:00
Jeremy Stretch
33e8e04281 Update change log 2024-08-15 12:36:53 -04:00
Jeremy Stretch
1504fb5ac2 Closes #17173: Update GraphQL web front end dependencies 2024-08-15 12:05:26 -04:00
Jeremy Stretch
dd3166a4ed Fixes #17097: Record static object representation when calling NotificationGroup.notify() (#17161)
* Fixes #17097: Record static object representation when calling NotificationGroup.notify()

* Redirect to notifications list when marking as read notifications for deleted objects

* Link to object in notifications dropdown only for non-destructive events
2024-08-15 12:04:19 -04:00
Jeremy Stretch
b6071a80d9 Closes #16574: Add last_synced time to REST API serializer for data sources 2024-08-15 10:20:21 -04:00
Arthur Hanson
93a13ce311 17169 use config_name for matching in plugin catalog (#17172)
* 17169 use config_name for matching

* 17169 remove cache buster
2024-08-15 10:18:34 -04:00
Arthur Hanson
122522a625 17163 Fix services list on IPAddress view 2024-08-15 10:06:37 -04:00
Arthur Hanson
a3befd0abe 17159 fix plugin install instructions 2024-08-15 09:59:31 -04:00
Jeremy Stretch
51c3d94725 Merge branch 'develop' into feature 2024-08-15 08:46:54 -04:00
Jeremy Stretch
e3d681be54 Closes #17143: Deprecate the use of dedicated nested API serializers 2024-08-15 08:00:33 -04:00
Jeremy Stretch
234b4027b0 Fixes #17098: Prevent automatic deletion of related notifications when deleting an object 2024-08-15 07:59:16 -04:00
Jeremy Stretch
277b7039d8 Closes #16245: Update frontend developer dependencies 2024-08-15 07:56:54 -04:00
github-actions
e2dfa63df6 Update source translation strings 2024-08-15 05:02:02 +00:00
Alexander Haase
c6c0ab2828 Fix job details view 2024-08-14 12:01:43 -04:00
Jeremy Stretch
8b91fb8d2d Merge branch 'develop' into feature 2024-08-14 10:54:31 -04:00
Jeremy Stretch
370c1209f4 PRVB 2024-08-14 10:27:03 -04:00
Jeremy Stretch
09d6b9c62f Merge pull request #17157 from netbox-community/develop
Release v4.0.9
2024-08-14 10:23:47 -04:00
Jeremy Stretch
4747cdef0b Bump version to v4.0.9 for release 2024-08-14 10:06:44 -04:00
Jeremy Stretch
f3f1aa3841 Release v4.0.9 2024-08-14 09:30:41 -04:00
transifex-integration[bot]
727cb65c50 Updates for project NetBox (#17153)
* Translate django.po in ru

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

* Translate django.po in de

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

* Translate django.po in ja [Manual Sync]

98% of minimum 1% translated source file: 'django.po'
on 'ja'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format

* Translate django.po in fr [Manual Sync]

98% of minimum 1% translated source file: 'django.po'
on 'fr'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format

* Translate django.po in es [Manual Sync]

98% of minimum 1% translated source file: 'django.po'
on 'es'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format

* Translate django.po in pt [Manual Sync]

98% of minimum 1% translated source file: 'django.po'
on 'pt'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format

* Translate django.po in tr [Manual Sync]

98% of minimum 1% translated source file: 'django.po'
on 'tr'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format

* Translate django.po in it [Manual Sync]

98% of minimum 1% translated source file: 'django.po'
on 'it'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format

* Translate django.po in zh [Manual Sync]

99% of minimum 1% translated source file: 'django.po'
on 'zh'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format

* Translate django.po in pl [Manual Sync]

98% of minimum 1% translated source file: 'django.po'
on 'pl'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format

* Translate django.po in nl [Manual Sync]

98% of minimum 1% translated source file: 'django.po'
on 'nl'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format

* Translate django.po in uk [Manual Sync]

99% of minimum 1% translated source file: 'django.po'
on 'uk'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format

* Translate django.po in cs [Manual Sync]

98% of minimum 1% translated source file: 'django.po'
on 'cs'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format

* Translate django.po in da [Manual Sync]

98% of minimum 1% translated source file: 'django.po'
on 'da'.

Sync of partially translated files: 
untranslated content is included with an empty translation 
or source language content depending on file format

* Translate django.po in zh

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

* Translate django.po in cs

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

* Translate django.po in da

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

* Translate django.po in nl

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

* Translate django.po in fr

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

* Translate django.po in it

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

* Translate django.po in ja

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

* Translate django.po in pl

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

* Translate django.po in pt

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

* 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'.

* Translate django.po in uk

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

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-08-14 09:28:28 -04:00
Arthur Hanson
872af72b8e 16073 set default custom fields on CSV import (#17152)
* 16073 set default custom fields on CSV import

* 16073 add test case

* Remove second for loop

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-08-14 08:22:14 -04:00
Jeremy Stretch
5a9f9af2fa Fixes #16871: Sanitize device ID when bulk editing components to prevent exception 2024-08-14 07:43:10 -04:00
github-actions
09d36469dd Update source translation strings 2024-08-14 05:02:17 +00:00
Jeremy Stretch
8789aaaa39 Changelog for #16692, #17006, #17124, #17131, #17144 2024-08-13 16:12:58 -04:00
Jeremy Stretch
d5c1a5acda Fixes #17144: Avoid displaying duplicate pop-up messages 2024-08-13 15:36:15 -04:00
PieterL75
6feb8bf0e3 add 'vlan' to prefix bulk edit (#17142)
* add 'vlan' to prefix bulk edit

* Move VLAN fields to a separate field set in bulk edit form

---------

Co-authored-by: Pieter Lambrecht <pieter.lambrecht@accenture.com>
Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-08-13 09:54:07 -04:00
Jeremy Stretch
9e54cfe340 Fixes #17131: Fix exception when creating object-type custom field without selecting related object type 2024-08-13 08:14:16 -04:00
github-actions
6a663e2a3e Update source translation strings 2024-08-13 05:02:12 +00:00
Matthew Mehrtens
7c9a77b77f 17006 Add Wi-Fi 7 IEEE 802.11be (#17125)
* Add .devcontainer to .gitignore

* Closes #17006: Add Wi-Fi 7 IEEE 802.11be

* Revert out-of-scope change

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-08-12 08:03:46 -04:00
github-actions
81fe12a7d9 Update source translation strings 2024-08-11 05:02:11 +00:00
Jeremy Stretch
9c7002f691 Fixes #17124: BaseTable should follow reverse one-to-one relationships when prefetching related objects 2024-08-10 11:58:14 -04:00
Jeremy Stretch
20967bf88d Changelog for #13459, #16176, #17038, #17064 2024-08-10 11:49:34 -04:00
Arthur Hanson
34d20fccd5 17036 international messages (#17041)
* 17036 international messages

* 17036 fix typo

* 17036 fix _

* Misc cleanup & fixes

* More cleanup

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-08-10 11:47:06 -04:00
Arthur Hanson
f6c1642116 16176 Add UI for multi-termination cables 2024-08-10 10:30:56 -04:00
Arthur Hanson
b7b0ab16f5 17064 fix markdown padding for first line 2024-08-10 10:28:01 -04:00
Arthur Hanson
6ae3af2f26 13459 Fix OpenAPI type for TreeNodeMultipleChoiceFilter (#17095)
* 13459 Correct OpenAPI type for TreeNodeMultipleChoiceFilter

* 13459 Correct OpenAPI type for TreeNodeMultipleChoiceFilter
2024-08-10 10:24:02 -04:00
Jeremy Stretch
6c845bd5de Update CONTRIBUTING.md
Add warning about intentionally submitting duplicate issues
2024-08-06 07:53:43 -04:00
Jeremy Stretch
23490d3336 Release v4.1-beta1 2024-08-05 13:34:05 -04:00
github-actions
75a0b53aeb Update source translation strings 2024-08-05 13:14:41 -04:00
Arthur Hanson
796b9e84af Closes #10500: Introduce support for nested modules (#16983)
* 10500 add ModularComponentModel

* 10500 add ModularComponentModel

* 10500 add to forms

* 10500 add to serializer, tables

* 10500 template

* 10500 add docs

* 10500 check recursion

* 10500 fix graphql

* 10500 fix conflicting migration from merge

* 10500 token resolution

* 10500 don't return reverse

* 10500 don't return reverse / optimize

* Add ModuleTypeModuleBaysView

* Fix replication of module bays on new modules

* Clean up tables & templates

* Adjust uniqueness constraints

* Correct URL

* Clean up docs

* Fix up serializers

* 10500 add filterset tests

* 10500 add nested validation to Module

* Misc cleanup

* 10500 ModuleBay recursion Test

* 10500 ModuleBay recursion Test

* 10500 ModuleBay recursion Test

* 10500 ModuleBay recursion Test

* Enable MPTT for module bays

* Fix tests

* Fix validation of module token in component names

* Misc cleanup

* Merge migrations

* Fix table ordering

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-08-05 13:13:59 -04:00
Jeremy Stretch
57fe2071a4 #15621: Support notifications for deletion of a subscribed object 2024-08-02 11:34:56 -04:00
Arthur Hanson
ca63aed9ee 17061 add new models to docs (#17062)
* 17061 add new models to docs

* Reorder; remove CircuitGroupAssignment

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-08-02 10:00:51 -04:00
Jeremy Stretch
02dd1a3970 Feature UI (#17069)
* 16649 general contrast issues (#16759)

* fixes #16647: navigation contrast issues updated

* fixes #16651: table contrast issues new

* fixed #16649: general contrast issues

* fixes #16649: feedback changes

---------

Co-authored-by: Andrew Gormley <Andrew@MacBook-Pro-3.local>
Co-authored-by: Andrew Gormley <Andrew@MacBook-Pro-3.broadband>

* 16648 dashboard contrast issues (#16824)

* fixed #16648: dashboard contrast issues

* reinstate amendment to 16649

* fixed #16648: created gridstack override and removed inline bug fix

---------

Co-authored-by: Andrew Gormley <Andrew@MacBook-Pro-3.local>

* fixed #16853: accessibility issues

* fixed #16847: updated font (#16848)

* fixed #16847: updated font

* fixed #16847: changed font to local and added current font as fallback

* fixed #16847: removed inter and added padding to page header

---------

Co-authored-by: Andrew Gormley <Andrew@MacBook-Pro-3.local>
Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>

* 16849 document hierarchy issues (#16875)

* fixed #16849: h elements not in sequential order

* fixed #16849: Lists do not contain only li elements

* fixed #16849: fixed h hierarchy on rack object pages

* Remove standalone h5 classes

* Remove unnecessary line breaks

---------

Co-authored-by: Andrew Gormley <Andrew@MacBook-Pro-3.local>
Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>

* 16650 button contrast issues (#16845)

* fixed #16650: button contrast issues

* fixed #16650: green bg text contrast issue

* Revert errant JS resource updates

* Revert custom button colors

* Fix indentation

---------

Co-authored-by: Andrew Gormley <Andrew@MacBook-Pro-3.local>
Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>

* 16907 web UI refresh (#16915)

* closes #16907: web ui refresh

* closes #16907: changed default widget color to primary color

* closes #16907: removed comma

* Revert dashboard widget color changes

* Rename logo images for consistency

* Restore original dashboard widget config

* Remove .navbar-brand-autodark from logo

* Restore logo file names

---------

Co-authored-by: Andrew Gormley <Andrew@MacBook-Pro-3.local>
Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>

* 16394 distinguish product edition (#16924)

* closes #16907: web ui refresh

* closes #16907: changed default widget color to primary color

* closes #16907: removed comma

* closes #16394: distinguish product edition

* Revert dashboard changes

* Clean up redundant styling (merge error)

* removed labs logo and added sub text for all editions

* fixed motif bug

* Fix "flashing" of side nav under dark mode

* Use title case for edition label

* altered edition text style

---------

Co-authored-by: Andrew Gormley <Andrew@MacBook-Pro-3.local>
Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>

* Query release features to toggle commercial theme

* fixes dark mode primary button contrast issue

* fixes #16913: hidden admin nav link (#16978)

Co-authored-by: Andrew Gormley <Andrew@MacBook-Pro-3.local>
Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>

* fixed 16852: misc accessbility problems (#16977)

* fixed 16852: misc accessbility problems

* Restore tooltip text

* Add translation support

* Add missing i18n

---------

Co-authored-by: Andrew Gormley <Andrew@MacBook-Pro-3.local>
Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>

* fixes issues in #16850 (#16986)

* fixes issues in #16850: issue 3 and 5

* Add link text for 'clear' button on table column

* Translate aria label

---------

Co-authored-by: Andrew Gormley <Andrew@MacBook-Pro-3.local>
Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>

* Use alternate footer links for commercial releases

* Remove Inter font

* Adjust base font weight to 500

* Retain default text color for hyperlinks inside tables

* Logo & edition cleanup

* Move dashboard styling

* Misc cleanup

* Remove unused styles

* Misc cleanup & refactoring

---------

Co-authored-by: Andrew Gormley <andrewgormley91@gmail.com>
Co-authored-by: Andrew Gormley <Andrew@MacBook-Pro-3.local>
Co-authored-by: Andrew Gormley <Andrew@MacBook-Pro-3.broadband>
2024-08-02 09:36:38 -04:00
Jeremy Stretch
f3da529399 Closes #14656: Dynamically render form fields depending on custom field type (#17068) 2024-08-02 08:14:01 -04:00
Jeremy Stretch
472fbdc654 Closes #17051: Introduce the ISOLATED_DEPLOYMENT config parameter (#17067)
* Closes #17051: Introduce ISOLATED_DEPLOYMENT config parameter

* Revert omission of external footer links
2024-08-02 08:05:08 -04:00
github-actions
597fc926c0 Update source translation strings 2024-08-02 05:02:14 +00:00
Jeremy Stretch
80fc9abb03 16927: Move JobRunner from utilities to netbox 2024-08-01 14:18:33 -04:00
Jeremy Stretch
8e6987edbf #16388: Move change logging signal handlers into core 2024-08-01 12:58:11 -04:00
Arthur Hanson
e32cfdf88c 17034 Add error handling to Plugins Catalog API (#17035)
* 17034 add error handling to plugins catalog API

* 17034 refactor from review feedback

* 17034 refactor from review feedback

* Misc cleanup

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-08-01 10:30:41 -04:00
Jeremy Stretch
bf17485290 Fix export of system data & include plugins 2024-08-01 09:36:23 -04:00
Jeremy Stretch
149c80a10e Merge branch 'develop' into feature 2024-08-01 09:16:01 -04:00
Jeremy Stretch
0b77b3372c Rename DeviceType.get_full_name to full_name 2024-08-01 09:14:03 -04:00
bubu
fa2b3bcfcc Adjust HTML template for Chinese translation order. 2024-08-01 09:08:06 -04:00
Arthur Hanson
d6f2fc7d29 17058 RackType name -> model (#17059)
* 17058 RackType name -> model

* 17058 RackType name -> model

* 17058 fix tests

* 17058 fix tests
2024-08-01 09:06:51 -04:00
Arthur Hanson
dc173a5508 17038 fix export system data 2024-08-01 08:47:26 -04:00
Arthur Hanson
408f8b4964 17054 upgrade sass to upgrade braces 2024-08-01 08:43:50 -04:00
Jeremy Stretch
c51e91dddd Closes #17048: Replace all calls to get_user_model() with direct imports of User 2024-08-01 08:17:51 -04:00
Arthur Hanson
1a6406632a For #12826 add tests to RackTest filtersets for manufacturer and DeviceType 2024-08-01 13:59:20 +07:00
Arthur Hanson
77d0e9032a fix internationalization for string 2024-08-01 13:12:58 +07:00
Jeremy Stretch
28b867bde4 Documentation updates for v4.1 2024-07-31 16:26:21 -04:00
Jeremy Stretch
a49a74236b #16886: Misc cleanup 2024-07-31 16:02:20 -04:00
Jeremy Stretch
6e59db7310 #16886: Updated documentation for event types 2024-07-31 15:54:31 -04:00
Jeremy Stretch
d1e16013f0 #16886: Rename Event to EventType 2024-07-31 15:39:05 -04:00
Jeremy Stretch
5cfa2bb561 #15621: Add feature documentation for user notifications 2024-07-31 15:11:40 -04:00
Jeremy Stretch
2168a73a2d Display manufacturer & model of module type on module view 2024-07-31 14:40:05 -04:00
Jeremy Stretch
8d585031d8 Misc cleanup 2024-07-31 14:06:35 -04:00
Jeremy Stretch
da7d47d3ed Add manufacturer filter for Rack; extend RackFilterForm 2024-07-31 14:06:07 -04:00
Jeremy Stretch
047d717532 Add missing table columns 2024-07-31 14:04:51 -04:00
Jeremy Stretch
52692d49b6 #9627: Fix calculation of available VIDs 2024-07-31 12:28:19 -04:00
Arthur Hanson
a3edc0472a 17032 Convert Virtual Disk Size from GB -> MB (#17033)
* 17032 Convert Virtual Disk size from GB -> MB

* 17032 update migration

* 17032 update migration

* 17032 update migration

* 17032 update migration

* Omit redundant method

* Merge migrations

* Tweak column header

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-07-31 10:03:25 -04:00
Jeremy Stretch
954eadcdc5 #15908: Introduce FeatureSet dataclass for tracking release features 2024-07-30 16:39:46 -04:00
Jeremy Stretch
845888c24e #14731: Misc cleanup 2024-07-30 15:11:35 -04:00
Jeremy Stretch
a63bb6dba6 #8984: Tweak ordering of log levels; fix invalid level handling 2024-07-30 14:49:42 -04:00
Jeremy Stretch
5f87724072 #8198: Fix validation bug 2024-07-30 14:25:47 -04:00
Jeremy Stretch
dbd503bcc8 #7025: Misc cleanup 2024-07-30 13:50:42 -04:00
Alexander Haase
d3a3a6ba46 15692: Introduce background jobs (#16927)
* Introduce reusable BackgroundJob framework

A new abstract class can be used to implement job function classes. It
handles the necessary logic for starting and stopping jobs, including
exception handling and rescheduling of recurring jobs.

This commit also includes the migration of data source jobs to the new
framework.

* Restore using import_string for jobs

Using the 'import_string()' utility from Django allows the job script
class to be simplified, as module imports no longer need to avoid loops.
This should make it easier to queue and maintain jobs.

* Use SyncDataSourceJob for management command

Instead of maintaining two separate job execution logics, the same job
is now used for both background and interactive execution.

* Implement BackgroundJob for running scripts

The independent implementations of interactive and background script
execution have been merged into a single BackgroundJob implementation.

* Fix documentation of model features

* Ensure consitent code style

* Introduce reusable ScheduledJob

A new abstract class can be used to implement job function classes that
specialize in scheduling. These use the same logic as regular
BackgroundJobs, but ensure that they are only scheduled once at any given
time.

* Introduce reusable SystemJob

A new abstract class can be used to implement job function classes that
specialize in system background tasks (e.g. synchronization or
housekeeping). In addition to the features of the BackgroundJob and
ScheduledJob classes, these implement additional logic to not need to be
bound to an existing NetBox object and to setup job schedules on plugin
load instead of an interactive request.

* Add documentation for jobs framework

* Revert "Use SyncDataSourceJob for management"

This partially reverts commit db591d4. The 'run_now' parameter of
'enqueue()' remains, as its being used by following commits.

* Merge enqueued status into JobStatusChoices

* Fix logger for ScriptJob

* Remove job name for scripts

Because scripts are already linked through the Job Instance field, the
name is displayed twice. Removing this reduces redundancy and opens up
the possibility of simplifying the BackgroundJob framework in future
commits.

* Merge ScheduledJob into BackgroundJob

Instead of using separate classes, the logic of ScheduledJob is now
merged into the generic BackgroundJob class. This allows reusing the
same logic, but dynamically deciding whether to enqueue the same job
once or multiple times.

* Add name attribute for BackgroundJob

Instead of defining individual names on enqueue, BackgroundJob classes
can now set a job name in their meta class. This is equivalent to other
Django classes and NetBox scripts.

* Drop enqueue_sync_job() method from DataSource

* Import ScriptJob directly

* Relax requirement for Jobs to reference a specific object

* Rename 'run_now' arg on Job.enqueue() to 'immediate'

* Fix queue lookup in Job enqueue

* Collapse SystemJob into BackgroundJob

* Remove legacy JobResultStatusChoices

ChoiceSet was moved to core in 40572b5.

* Use queue 'low' for system jobs by default

System jobs usually perform low-priority background tasks and therefore
can use a different queue than 'default', which is used for regular jobs
related to specific objects.

* Add test cases for BackgroundJob handling

* Fix enqueue interval jobs

As the job's name is set by enqueue(), it must not be passed in handle()
to avoid duplicate kwargs with the same name.

* Honor schedule_at for job's enqueue_once

Not only can a job's interval change, but so can the time at which it is
scheduled to run. If a specific scheduled time is set, it will also be
checked against the current job schedule. If there are any changes, the
job is rescheduled with the new time.

* Switch BackgroundJob to regular methods

Instead of using a class method for run(), a regular method is used for
this purpose. This gives the possibility to add more convenience methods
in the future, e.g. for interacting with the job object or for logging,
as implemented for scripts.

* Fix background tasks documentation

* Test enqueue in combination with enqueue_once

* Rename background jobs to tasks (to differentiate from RQ)

* Touch up docs

* Revert "Use queue 'low' for system jobs by default"

This reverts commit b17b2050df.

* Remove system background job

This commit reverts commits 4880d81 and 0b15ecf. Using the database
'connection_created' signal for job registration feels a little wrong at
this point, as it would trigger registration very often. However, the
background job framework is prepared for this use case and can be used
by plugins once the auto-registration of jobs is solved.

* Fix runscript management command

Defining names for background jobs was disabled with fb75389. The
preceeding changes in 257976d did forget the management command.

* Use regular imports for ScriptJob

* Rename BackgroundJob to JobRunner

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-07-30 13:31:21 -04:00
Jeremy Stretch
dde84b47da Flesh out v4.1 changelog 2024-07-29 20:58:29 -04:00
samk-acw
650898719e Fixes #16782: Add object filtering for custom fields (#16994)
* Fixes #16782: Add object filtering for custom fields

* Add validation for related_object_filter

* Extend documentation & misc cleanup

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-07-29 15:45:48 -04:00
github-actions
f949aa334b Update source translation strings 2024-07-27 05:02:14 +00:00
Jeremy Stretch
a12fdd6e2d Merge branch 'develop' into feature 2024-07-26 16:39:58 -04:00
Jeremy Stretch
8bfcb1c816 PRVB 2024-07-26 16:25:32 -04:00
Jeremy Stretch
630c6fb43d Merge branch 'master' into develop 2024-07-26 16:24:55 -04:00
Jeremy Stretch
7b5e8d5f2a Update static assets 2024-07-26 15:47:57 -04:00
Jeremy Stretch
303c1ce00c Release v4.0.8 2024-07-26 15:37:05 -04:00
Jeremy Stretch
b4240cdd67 Update import statements for Strawberry 0.236.0 2024-07-26 15:35:08 -04:00
transifex-integration[bot]
1e7a71969e Updates for project NetBox (#17004)
* Translate django.po in de

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

* Translate django.po in pt

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

* Translate django.po in zh

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

* Translate django.po in pl

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

* Translate django.po in ja

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

* Translate django.po in nl

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

* Translate django.po in cs

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

* Translate django.po in uk

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

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-07-26 15:34:47 -04:00
Jonathan Senecal
f3d1924453 Use provider_id instead of account_id in url_params 2024-07-26 15:10:25 -04:00
Arthur Hanson
0a1ce79154 16992 remove TODO for NetBox 4.1 (#16993)
* 16992 remove TODO for NetBox 4.1

* 16992 fix custom validator

* Remove obsolete LegacyScriptRedirectView

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-07-26 09:46:16 -04:00
Jeremy Stretch
4d55d7d964 Changelog for #16933, #16943, #16964 2024-07-26 08:01:08 -04:00
Jeremy Stretch
d8c7282fdb Fixes #16964: Ensure configured password validators are enforced (#16990)
* Closes #16964: Validate password when creating a new user or updating password for an existing user

* Add serializer validation & tests

---------

Co-authored-by: Nishant Gaglani <nishantgaglani@gmail.com>
2024-07-26 07:58:14 -04:00
Jeremy Stretch
22319b2cce Closes #16988: Move rack nav menu items to a dedicated section 2024-07-26 07:57:29 -04:00
Arthur Hanson
e62a42286a 11969 airflow (#16967)
* 11960 Add airflow

* 11960 Add airflow

* 11960 fix tests

* 11960 fix racktype form

* 11969 different choices type

* 11969 update docs

* 11969 fix racktype copy

* 11969 fix

* Misc cleanup & reordering of form fields

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-07-25 15:14:02 -04:00
Arthur Hanson
1d6987bca0 14731 plugins catalog (#16763)
* 14731 plugin catalog

* 14731 detal page

* 14731 plugin table

* 14731 cleanup

* 14731 cache API results

* 14731 fix install name

* 14731 filtering

* 14731 filtering

* 14731 fix detail view

* 14731 fix detail view

* 14731 sort / status

* 14731 sort / status

* 14731 cleanup detail view

* 14731 htmx plugin list

* 14731 align quicksearch

* 14731 remove pytz

* 14731 change to table

* 14731 change to table

* 14731 remove status from table

* 14731 quick search

* 14731 cleanup

* 14731 cleanup

* Employ datetime_from_timestamp() to parse timestamps

* 14731 review changes

* 14731 move to plugins.py file

* 14731 use dataclasses

* 14731 review changes

* Tweak table columns

* Use is_staff (for now) to evaluate user permission for plugin views

* Use table for ordering

* 7025 change to api fields

* 14731 tweaks

* Remove filtering for is_netboxlabs_supported

* Misc cleanup

* Update logic for determining whether to display plugin installation instructions

* 14731 review changes

* 14731 review changes

* 14731 review changes

* 14731 add user agent string, proxy settings

* Clean up templates

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-07-25 14:58:48 -04:00
Jeremy Stretch
8409ca9fd2 Closes #16928: Add help center button to top nav 2024-07-25 13:16:31 -04:00
Jeremy Stretch
909ee7d543 Closes #14861: Standardize URL path for virtual disks 2024-07-25 13:12:53 -04:00
Jeremy Stretch
cc72a58c1e Remove jeffgdotorg from triage rotation 2024-07-24 17:15:53 -04:00
Arthur Hanson
8237c6accc 7025 circuit redundancy (#16945)
* 7025 CircuitRedundancyGroups

* 7025 CircuitRedundancyGroups api

* 7025 CircuitRedundancyGroups api

* 7025 CircuitRedundancyGroups tests

* 7025 CircuitRedundancyGroup -> CircuitGroup

* 7025 add tenancy

* 7025 linkify name

* 7025 missing file

* 7025 circuitgroupassignment

* 7025 base group assignment working

* 7025 assignments

* 7025 fix forms/tests for CircuitGroup

* 7025 fix api tests

* 7025 view tests

* 7025 CircuitGroupAssignment tests

* 7025 fix typo

* 7025 fix typo

* 7025 fix tests

* 7025 remove m2m

* 7025 add count to serializer

* 7025 fix test

* 7025 documentation

* 7025 review comments

* 7025 review comments

* 7025 add search index

* Make CircuitPriorityChoices extensible

* Add group assignment table to circuit view

* Misc cleanup

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-07-24 12:24:44 -04:00
Jeremy Stretch
f7fdfdd925 #16866: Fix handling of job start/stop events 2024-07-22 13:47:48 -04:00
Jeremy Stretch
44a9350986 Closes #16886: Dynamic event types (#16938)
* Initial work on #16886

* Restore GraphQL filter

* Remove namespace

* Add Event documentation

* Use MultipleChoiceField for event_types

* Fix event_types field class on EventRuleImportForm

* Fix tests

* Simplify event queue handling logic

* Misc cleanup
2024-07-22 08:12:04 -04:00
github-actions
36df9228a6 Update source translation strings 2024-07-20 05:02:12 +00:00
Jeremy Stretch
4a53a96541 #15908: Add features attribute to ReleaseInfo 2024-07-19 17:04:06 -04:00
Jeremy Stretch
1acb9902e7 #16359: Include plugin nav content on mobile view 2024-07-19 16:21:46 -04:00
Jeremy Stretch
424dda5be6 Closes #16933: Enable toggling true/false marks on BooleanColumn 2024-07-19 07:54:41 -04:00
Jeremy Stretch
3028f262cc Closes #16943: Expand navigation breadcrumbs on job view to include parent object 2024-07-19 07:52:27 -04:00
github-actions
11cadf3a8a Update source translation strings 2024-07-19 05:02:01 +00:00
Jeremy Stretch
965e78125a Closes #16884: Remove ID column from ObjectChangeTable defaults 2024-07-18 21:44:12 -04:00
Jeremy Stretch
954d0cfcd0 Closes #16929: Add version & user details as data attributes (#16939)
* Closes #16929: Add version & user details as data attributes

* Fix typo
2024-07-18 12:39:23 -04:00
Benjamin Dale
0830ebb34a Update CONTRIBUTING.md
The GitHub reactions icon has been moved from the top right to the bottom left of messages in Issues - I was going insane trying to find it, so this might help someone in the future ; )
2024-07-16 20:12:17 -04:00
Jeremy Stretch
b2c5a4639c Introduce datetime_from_timestamp() utility function 2024-07-16 12:21:50 -04:00
Arthur Hanson
cf1024a12b 9627 use ArrayField on VLANGroup for allowed VLANs (#16710)
* 9627 initial commit

* 9627 numeric range field

* 9627 numeric range field

* 9627 numeric range field

* 9627 numeric range field

* 9627 add stuff for utilization calc

* 9627 update views

* 9627 fixes

* 9627 available_vlans

* 9627 available_vlans

* 9627 fixes

* 9627 bulk import / edit

* 9627 test fixes

* 9627 test fixes

* 9627 update validation

* 9627 fix tests

* 9627 fix tests

* 9627 fix tests

* 9627 fix tests

* 9627 fix tests

* 9627 fix tests

* 9627 review changes

* 9627 temp vlan_id filter

* Clean up labels

* Remove annotate_vlan_ranges() from VLANGroupQuerySet

* Misc cleanup

* Implement contains_vid filter

* Serialize VID ranges as integer lists in REST API

* Remove default value from vlan_id_ranges

* 9627 fix typo in test

* Require vlan_id_ranges & set default value

* Fix logic for upper range boundaries

* Add field to VLANGroup model documentation

* Rename vlan_id_ranges to vid_ranges

* Fix computation of available VLAN IDs

* Clean up migration

* Add tests for range utility functions

* Clean up add_available_vlans()

* Misc cleanup, add test for VID validation

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-07-16 10:47:18 -04:00
Arthur Hanson
5a6ffde67e 12826 Add Rack Type (#16739)
* 12826 add RackType

* 12826 add forms, filters, tables

* 12826 add to menu

* 12826 remove role

* 12826 add api/serializers

* 12826 add tests and fixes

* 12826 fix tests

* 12826 fix tests

* 12826 fix tests

* 12826 fix tests

* 12826 add device_type to device and instantiation

* 12826 test device creation

* 12826 add slug

* 12826 fix tests

* 12826 fix slug field

* 12826 prevent modification of rack fields if rack_type set

* 12826 update rack fields on rack_type edit

* Misc cleanup

* Update model docs

* Add manufacturer field to RackType

* Add test for mounting_depth

* Rename 'type' to 'form_factor'

* Create base classes for Rack & RackType models, serializers

* Hide RackType-defined fields on RackForm when a rack type is set

* Establish a base filter form for Rack & RackType

* Clean up RackType attr inheritance

* Clean up templates

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-07-16 08:58:22 -04:00
Jeremy Stretch
b0e7294bc1 Closes #15621: User notifications (#16800)
* Initial work on #15621

* Signal receiver should ignore models which don't support notifications

* Flesh out NotificationGroup functionality

* Add NotificationGroup filters for users & groups

* Separate read & dimiss actions

* Enable one-click dismissals from notifications list

* Include total notification count in dropdown

* Drop 'kind' field from Notification model

* Register event types in the registry; add colors & icons

* Enable event rules to target notification groups

* Define dynamic choices for Notification.event_name

* Move event registration to core

* Add more job events

* Misc cleanup

* Misc cleanup

* Correct absolute URLs for notifications & subscriptions

* Optimize subscriber notifications

* Use core event types when queuing events

* Standardize queued event attribute to event_type; change content_type to object_type

* Rename Notification.event_name to event_type

* Restore NotificationGroupBulkEditView

* Add API tests

* Add view & filterset tests

* Add model documentation

* Fix tests

* Update notification bell when notifications have been cleared

* Ensure subscribe button appears only on relevant models

* Notifications/subscriptions cannot be ordered by object

* Misc cleanup

* Add event icon & type to notifications table

* Adjust icon sizing

* Mute color of read notifications

* Misc cleanup
2024-07-15 14:24:11 -04:00
Jeremy Stretch
95cb7b2c34 Changelog for #16402, #16536, #16624, #16819 2024-07-15 09:12:36 -04:00
Arthur Hanson
dde77c83b4 16819 highlight parent device in rack (#16881)
* 16819 highlight parent device in rack

* 16819 review changes
2024-07-15 09:09:26 -04:00
Eric Oswald
e216bebd41 Fix incorrect import in rest-api.md 2024-07-13 10:28:43 -04:00
Jeremy Stretch
1c2336be60 Closes #16776: Extend PluginTemplateExtension to render custom alerts for objects (#16889)
* Closes #16776: Extend PluginTemplateExtension to render custom alerts for objects

* Fix bug in _get_registered_content()
2024-07-12 09:52:07 -04:00
Thomas Fargeix
d39acfd3f2 Fixes 16536 - Fix filtering of device component by device role (#16553)
* Fixes 16536 - Fix filtering of device component by device role

Rename role and role_id fields to device_role and device_role_id in
DeviceComponentFilterSet

* Update tests for DeviceComponentFilterSet

* Use device_role filter name for DeviceComponentFilterSetTests

* Add test_device_role test in InventoryItemTestCase
2024-07-12 09:13:33 -04:00
Fabian Geisberger
4ea4e57f33 Fixes #16624: Set allow_null=True on method fields that can return None 2024-07-12 09:03:40 -04:00
Arthur Hanson
377543cd9c 16402 remove links from script result table 2024-07-12 08:31:32 -04:00
github-actions
a8827c8472 Update source translation strings 2024-07-12 05:02:03 +00:00
Jeremy Stretch
b2ca6df50a Changelog for #14640, #14792, #15660, #15696, #16793 2024-07-11 14:21:41 -04:00
transifex-integration[bot]
ab6ddd50a8 Updates for project NetBox (#16888)
* Translate django.po in pt

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

* Translate django.po in es

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

* Translate django.po in de

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

* Translate django.po in fr

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

* Translate django.po in ru

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

* Translate django.po in ja

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

* Translate django.po in it

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

* Translate django.po in cs

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

* Translate django.po in zh

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

* Translate django.po in nl

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

* Translate django.po in da

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

* Translate django.po in uk

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

* Translate django.po in pl

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

* 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-07-11 14:20:06 -04:00
Jeff Gehlbach
499da4fdcf Added CS, DA, IT, NL, and PL, minus the .po and .mo starting point files (#16810)
* Added CS, DA, IT, NL, and PL, minus the .po and .mo starting point files

* Add initial PO files for new languages

* Revert updates to EN django.po

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-07-11 14:04:36 -04:00
github-actions
4fa396716e Update source translation strings 2024-07-11 05:02:02 +00:00
Jeremy Stretch
6f3a2a599f Changelog for #15375, #16357, #16760, #16838, #16867 2024-07-10 09:16:23 -04:00
Jeremy Stretch
960d2b82b7 Update contact email 2024-07-10 09:14:20 -04:00
the.friendly.net
f2e1de027f Fixes #16760: datasource git on local file system fails (#16872)
* Fixes #16760: datasource git on local file system fails

* Fixes #16760: datasource git on local file system fails

* Set depth & quiet parameters only if using a remote URL

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-07-10 09:10:28 -04:00
Jeff Gehlbach
bf97138c78 Small additions and tweaks to release checklist from releasing v4.0.7 2024-07-10 08:45:36 -04:00
Arthur Hanson
30d711d24a 16838 show extra_buttons if no actions defined 2024-07-10 08:43:27 -04:00
Arthur Hanson
2a8bec1cbf 16867 render dashboard if model no longer available 2024-07-10 08:39:40 -04:00
Arthur Hanson
013139aa20 16357 clone tenant and type for cable 2024-07-10 08:34:48 -04:00
Théophile Bastian
4ca1494127 SSO: custom name for identity providers (#16732) 2024-07-10 13:09:03 +07:00
github-actions
70311a9db5 Update source translation strings 2024-07-10 05:02:10 +00:00
Jeremy Stretch
02ae91589d Merge branch 'develop' into feature 2024-07-09 15:16:05 -04:00
Jeremy Stretch
dd413b248a PRVB 2024-07-09 13:49:24 -04:00
Jeremy Stretch
12cd2a7d6f Changelog for #8984, #15156, #16580, #16726 2024-07-03 16:30:02 -04:00
Jeremy Stretch
616f6f6165 #16726: Fix cotent registration bug 2024-07-03 16:20:00 -04:00
Arthur Hanson
5ac5135dbc 8984 Allow script log to be filtered (#16446)
* 8984 filter by script log level

* 8984 filter log list

* 8984 add dropdown

* 8984 add dropdown

* 8984 fix button color

* Update netbox/extras/views.py

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

* 8984 review changes

* 8984 review changes

* 8984 review changes

* Clean up log threshold selector

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-07-01 16:04:29 -04:00
Jeremy Stretch
82d8de32df Closes #16726: Extend PluginTemplateExtension to allow registering multiple models 2024-07-01 15:25:27 -04:00
Jeremy Stretch
c22463f4aa Closes #16580: Remove AUTH_EXEMPT_PATHS (#16662)
* Closes #16580: Remove AUTH_EXEMPT_PATHS

* Misc cleanup
2024-06-26 12:05:38 -04:00
Arthur Hanson
52546608f6 15156 Add display_url to REST API (#16412)
* 15156 add display_url to REST API

* 15156 fix view name

* 15156 fix typo

* 15156 fix tests

* 15156 add url display_url to base class

* 15156 add url display_url to base class

* 15156 add url display_url to base class

* 15156 review changes

* 15156 review changes

* 15156 review changes

* 15156 review changes

* 15156 remove bogus code

* 15156 remove bogus code

* 15156 review changes

* 15156 review changes

* 15156 review changes

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-06-25 09:59:58 -04:00
Jeremy Stretch
2b4577e365 Closes #8198: Custom field uniqueness (#16661)
* Closes #8198: Implement ability to enforce custom field uniqueness

* Add missing form fields & table columns for validation attributes

* Remove obsolete code
2024-06-25 08:37:10 -04:00
Jeremy Stretch
08ac5cd52c Merge branch 'develop' into feature 2024-06-24 15:40:43 -04:00
Jeremy Stretch
6819186bb6 Start release notes for v4.1 2024-06-18 13:43:33 -04:00
Jeremy Stretch
388ba3d736 #16388: Rename database indexes & constraints 2024-06-17 13:47:30 -04:00
Arthur Hanson
91dcecbd07 15106 Add Length Field to Wireless Link (#16528)
* 15106 add wireles link length

* 15106 add wireles link length

* 15106 add wireless link length

* 15106 add tests

* 15106 rename length -> distance

* 15106 rename length -> distance

* 15106 review comments

* 15106 review comments

* 15106 fix form

* 15106 length -> distance
2024-06-17 09:19:49 -04:00
Jeremy Stretch
e12edd7420 #16388: Fix migration bug 2024-06-17 08:36:03 -04:00
Jeremy Stretch
853d990c03 Closes #16388: Move change logging resources from extras to core (#16545)
* Initial work on #16388

* Misc cleanup
2024-06-17 08:03:06 -04:00
Arthur Hanson
c6553c45dd 7537 add serial number to virtual machines (#16407)
* 7537 add serial number to virtual machines

* 7537 add migration

* 7537 add sn to search

* 7537 add to model documentation

* 8984 move serializer field

* 8984 add to detail view and search index

* 7537 serial_number -> serial

* 7537 fix migration

* Add missing serial field

* Give serial field higher precedence for search

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-06-12 10:15:16 -04:00
Arthur Hanson
c1d7696b2a 14692 Convert VM disk size (#16434)
* 14692 convert disk size to MB

* 14692 fix list display

* 14692 fix migration

* Update netbox/virtualization/migrations/0039_convert_disk_size.py

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

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
2024-06-11 09:19:57 -04:00
Jeremy Stretch
4da5bd6ed1 #15908: Update upgrade.sh 2024-06-07 14:36:41 -04:00
Arthur Hanson
c7176e86fc 15410 removed deprecated filters (#16410)
* 15410 removed legacy filters

* 15410 fix tests

* 15410 fix tests
2024-06-07 14:08:30 -04:00
Jeremy Stretch
c6bd714a04 Closes #15908: Establish canonical & local sources for release info (#16420)
* Closes #15908: Establish canonical & local sources for release info

* Update references to settings.VERSION
2024-06-07 13:41:48 -04:00
Arthur
1952d3e63a Merge branch 'develop' into feature 2024-06-06 11:17:43 -07:00
Jeremy Stretch
50169365a9 Closes #16359: Add navbar() method to PluginTemplateExtension 2024-06-05 12:34:12 -04:00
648 changed files with 71215 additions and 51999 deletions

View File

@@ -26,7 +26,7 @@ body:
attributes:
label: NetBox Version
description: What version of NetBox are you currently running?
placeholder: v4.0.8
placeholder: v4.1.0
validations:
required: true
- type: dropdown

View File

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

View File

@@ -1,21 +0,0 @@
# auto-assign-issue (https://github.com/marketplace/actions/auto-assign-issue)
name: Issue assignment
on:
issues:
types: [opened]
permissions:
issues: write
jobs:
auto-assign:
runs-on: ubuntu-latest
steps:
- uses: pozil/auto-assign-issue@v2
if: "contains(github.event.issue.labels.*.name, 'status: needs triage')"
with:
# Weighted assignments
assignees: arthanson:3, jeremystretch:3, DanSheps
numOfAssignee: 1
abortIfPreviousAssignees: true

View File

@@ -40,7 +40,7 @@ NetBox users are welcome to participate in either role, on stage or in the crowd
* First, ensure that you're running the [latest stable version](https://github.com/netbox-community/netbox/releases) of NetBox. If you're running an older version, it's likely that the bug has already been fixed.
* Next, search our [issues list](https://github.com/netbox-community/netbox/issues?q=is%3Aissue) to see if the bug you've found has already been reported. If you come across a bug report that seems to match, please click "add a reaction" in the bottom left corner of the issue and add a thumbs up (:thumbsup:). This will help draw more attention to it. Any comments you can add to provide additional information or context would also be much appreciated.
* Next, search our [issues list](https://github.com/netbox-community/netbox/issues?q=is%3Aissue) to see if the bug you've found has already been reported. If you come across a bug report that seems to match, please click "add a reaction" in the bottom left corner of the issue and add a thumbs up ( :thumbsup: ). This will help draw more attention to it. Any comments you can add to provide additional information or context would also be much appreciated.
* If you can't find any existing issues (open or closed) that seem to match yours, you're welcome to [submit a new bug report](https://github.com/netbox-community/netbox/issues/new?label=type%3A+bug&template=bug_report.yaml). Be sure to complete the entire report template, including detailed steps that someone triaging your issue can follow to confirm the reported behavior. (If we're not able to replicate the bug based on the information provided, we'll ask for additional detail.)
@@ -56,7 +56,9 @@ intake policy](https://github.com/netbox-community/netbox/wiki/Issue-Intake-Poli
## :bulb: Feature Requests
* First, check the GitHub [issues list](https://github.com/netbox-community/netbox/issues?q=is%3Aissue) to see if the feature you have in mind has already been proposed. If you happen to find an open feature request that matches your idea, click "add a reaction" in the top right corner of the issue and add a thumbs up (:thumbsup:). This ensures that the issue has a better chance of receiving attention. Also feel free to add a comment with any additional justification for the feature.
* First, check the GitHub [issues list](https://github.com/netbox-community/netbox/issues?q=is%3Aissue) to see if the feature you have in mind has already been proposed. If you happen to find an open feature request that matches your idea, click "add a reaction" in the top right corner of the issue and add a thumbs up ( :thumbsup: ). This ensures that the issue has a better chance of receiving attention. Also feel free to add a comment with any additional justification for the feature.
* Please don't submit duplicate issues! Sometimes we reject feature requests, for various reasons. Even if you disagree with those reasons, please **do not** submit a duplicate feature request. It is very disrepectful of the maintainers' time, and you may be barred from opening future issues.
* If you have a rough idea that's not quite ready for formal submission yet, start a [GitHub discussion](https://github.com/netbox-community/netbox/discussions) instead. This is a great way to test the viability and narrow down the scope of a new feature prior to submitting a formal proposal, and can serve to generate interest in your idea from other community members.

View File

@@ -10,7 +10,7 @@ django-cors-headers
# https://github.com/jazzband/django-debug-toolbar/blob/main/docs/changes.rst
# Pinned for DNS looukp bug; see https://github.com/netbox-community/netbox/issues/16454
# and https://github.com/jazzband/django-debug-toolbar/issues/1927
django-debug-toolbar==4.3.0
django-debug-toolbar
# Library for writing reusable URL query filters
# https://github.com/carltongibson/django-filter/blob/main/CHANGES.rst

View File

@@ -377,6 +377,7 @@
"ieee802.11ad",
"ieee802.11ax",
"ieee802.11ay",
"ieee802.11be",
"ieee802.15.1",
"other-wireless",
"gsm",

View File

@@ -5,7 +5,7 @@
Default: False
This setting enables debugging. Debugging should be enabled only during development or troubleshooting. Note that only
clients which access NetBox from a recognized [internal IP address](#internal_ips) will see debugging tools in the user
clients which access NetBox from a recognized [internal IP address](./system.md#internal_ips) will see debugging tools in the user
interface.
!!! warning

View File

@@ -0,0 +1,17 @@
# GraphQL API Parameters
## GRAPHQL_ENABLED
!!! tip "Dynamic Configuration Parameter"
Default: True
Setting this to False will disable the GraphQL API.
---
## GRAPHQL_MAX_ALIASES
Default: 10
The maximum number of queries that a GraphQL API request may contain.

View File

@@ -25,7 +25,7 @@ Some configuration parameters are primarily controlled via NetBox's admin interf
* [`CUSTOM_VALIDATORS`](./data-validation.md#custom_validators)
* [`DEFAULT_USER_PREFERENCES`](./default-values.md#default_user_preferences)
* [`ENFORCE_GLOBAL_UNIQUE`](./miscellaneous.md#enforce_global_unique)
* [`GRAPHQL_ENABLED`](./miscellaneous.md#graphql_enabled)
* [`GRAPHQL_ENABLED`](./graphql-api.md#graphql_enabled)
* [`JOB_RETENTION`](./miscellaneous.md#job_retention)
* [`MAINTENANCE_MODE`](./miscellaneous.md#maintenance_mode)
* [`MAPS_URL`](./miscellaneous.md#maps_url)

View File

@@ -122,16 +122,6 @@ The maximum amount (in bytes) of uploaded data that will be held in memory befor
---
## GRAPHQL_ENABLED
!!! tip "Dynamic Configuration Parameter"
Default: True
Setting this to False will disable the GraphQL API.
---
## JOB_RETENTION
!!! tip "Dynamic Configuration Parameter"

View File

@@ -20,19 +20,29 @@ A list of permitted URL schemes referenced when rendering links within NetBox. N
## AUTH_PASSWORD_VALIDATORS
This parameter acts as a pass-through for configuring Django's built-in password validators for local user accounts. If configured, these will be applied whenever a user's password is updated to ensure that it meets minimum criteria such as length or complexity. An example is provided below. For more detail on the available options, please see [the Django documentation](https://docs.djangoproject.com/en/stable/topics/auth/passwords/#password-validation).
This parameter acts as a pass-through for configuring Django's built-in password validators for local user accounts. These rules are applied whenever a user's password is created or updated to ensure that it meets minimum criteria such as length or complexity. The default configuration is shown below.
```python
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
'OPTIONS': {
'min_length': 10,
}
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
"OPTIONS": {
"min_length": 12,
},
},
{
"NAME": "utilities.password_validation.AlphanumericPasswordValidator",
},
]
```
The default configuration enforces the follow criteria:
* A password must be at least 12 characters in length.
* A password must have at least one uppercase letter, one lowercase letter, and one numeric digit.
Although it is not recommended, the default validation rules can be disabled by setting `AUTH_PASSWORD_VALIDATORS = []` in the configuration file. For more detail on customizing password validation, please see [the Django documentation](https://docs.djangoproject.com/en/stable/topics/auth/passwords/#password-validation).
---
## CORS_ORIGIN_ALLOW_ALL

View File

@@ -83,7 +83,20 @@ Default: `('127.0.0.1', '::1')`
A list of IP addresses recognized as internal to the system, used to control the display of debugging output. For
example, the debugging toolbar will be viewable only when a client is accessing NetBox from one of the listed IP
addresses (and [`DEBUG`](#debug) is true).
addresses (and [`DEBUG`](./development.md#debug) is true).
---
## ISOLATED_DEPLOYMENT
!!! info "This feature was introduced in NetBox v4.1."
Default: False
Set this configuration parameter to True for NetBox deployments which do not have Internet access. This will disable miscellaneous functionality which depends on access to the Internet.
!!! note
If Internet access is available via a proxy, set [`HTTP_PROXIES`](#http_proxies) instead.
---
@@ -106,7 +119,7 @@ JINJA2_FILTERS = {
## LOGGING
By default, all messages of INFO severity or higher will be logged to the console. Additionally, if [`DEBUG`](#debug) is False and email access has been configured, ERROR and CRITICAL messages will be emailed to the users defined in [`ADMINS`](#admins).
By default, all messages of INFO severity or higher will be logged to the console. Additionally, if [`DEBUG`](./development.md#debug) is False and email access has been configured, ERROR and CRITICAL messages will be emailed to the users defined in [`ADMINS`](./miscellaneous.md#admins).
The Django framework on which NetBox runs allows for the customization of logging format and destination. Please consult the [Django logging documentation](https://docs.djangoproject.com/en/stable/topics/logging/) for more information on configuring this setting. Below is an example which will write all INFO and higher messages to a local file:
@@ -143,7 +156,7 @@ LOGGING = {
## MEDIA_ROOT
Default: $INSTALL_ROOT/netbox/media/
Default: `$INSTALL_ROOT/netbox/media/`
The file path to the location where media files (such as image attachments) are stored. By default, this is the `netbox/media/` directory within the base NetBox installation path.

View File

@@ -74,6 +74,8 @@ If a default value is specified for a selection field, it must exactly match one
An object or multi-object custom field can be used to refer to a particular NetBox object or objects as the "value" for a custom field. These custom fields must define an `object_type`, which determines the type of object to which custom field instances point.
By default, an object choice field will make all objects of that type available for selection in the drop-down. The list choices can be filtered to show only objects with certain values by providing a `query_params` dict in the Related Object Filter field, as a JSON value. More information about `query_params` can be found [here](./custom-scripts.md#objectvar).
## Custom Fields in Templates
Several features within NetBox, such as export templates and webhooks, utilize Jinja2 templating. For convenience, objects which support custom field assignment expose custom field data through the `cf` property. This is a bit cleaner than accessing custom field data through the actual field (`custom_field_data`).

View File

@@ -86,8 +86,6 @@ CUSTOM_VALIDATORS = {
#### 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
@@ -104,8 +102,6 @@ CUSTOM_VALIDATORS = {
#### 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

View File

@@ -71,7 +71,6 @@ Add the relevant navigation menu items in `netbox/netbox/navigation/menu.py`.
Create the following for each model:
* Detailed (full) model serializer in `api/serializers.py`
* Nested serializer in `api/nested_serializers.py`
* API view in `api/views.py`
* Endpoint route in `api/urls.py`

View File

@@ -50,7 +50,7 @@ If you're adding a relational field (e.g. `ForeignKey`) and intend to include th
## 5. Update API serializer
Extend the model's API serializer in `<app>.api.serializers` to include the new field. In most cases, it will not be necessary to also extend the nested serializer, which produces a minimal representation of the model.
Extend the model's API serializer in `<app>.api.serializers` to include the new field.
## 6. Add fields to forms

View File

@@ -18,7 +18,7 @@ Depending on its classification, each NetBox model may support various features
| [Custom links](../customization/custom-links.md) | `CustomLinksMixin` | `custom_links` | These models support the assignment of custom links |
| [Custom validation](../customization/custom-validation.md) | `CustomValidationMixin` | - | Supports the enforcement of custom validation rules |
| [Export templates](../customization/export-templates.md) | `ExportTemplatesMixin` | `export_templates` | Users can create custom export templates for these models |
| [Job results](../features/background-jobs.md) | `JobsMixin` | `jobs` | Users can create custom export templates for these models |
| [Job results](../features/background-jobs.md) | `JobsMixin` | `jobs` | Background jobs can be scheduled for these models |
| [Journaling](../features/journaling.md) | `JournalingMixin` | `journaling` | These models support persistent historical commentary |
| [Synchronized data](../integrations/synchronized-data.md) | `SyncedDataMixin` | `synced_data` | Certain model data can be automatically synchronized from a remote data source |
| [Tagging](../models/extras/tag.md) | `TagsMixin` | `tags` | The models can be tagged with user-defined tags |
@@ -34,7 +34,9 @@ These are considered the "core" application models which are used to model netwo
* [circuits.Provider](../models/circuits/provider.md)
* [circuits.ProviderAccount](../models/circuits/provideraccount.md)
* [circuits.ProviderNetwork](../models/circuits/providernetwork.md)
* [core.DataFile](../models/core/datafile.md)
* [core.DataSource](../models/core/datasource.md)
* [core.Job](../models/core/job.md)
* [dcim.Cable](../models/dcim/cable.md)
* [dcim.Device](../models/dcim/device.md)
* [dcim.DeviceType](../models/dcim/devicetype.md)
@@ -44,12 +46,14 @@ These are considered the "core" application models which are used to model netwo
* [dcim.PowerPanel](../models/dcim/powerpanel.md)
* [dcim.Rack](../models/dcim/rack.md)
* [dcim.RackReservation](../models/dcim/rackreservation.md)
* [dcim.RackType](../models/dcim/racktype.md)
* [dcim.Site](../models/dcim/site.md)
* [dcim.VirtualChassis](../models/dcim/virtualchassis.md)
* [dcim.VirtualDeviceContext](../models/dcim/virtualdevicecontext.md)
* [ipam.Aggregate](../models/ipam/aggregate.md)
* [ipam.ASN](../models/ipam/asn.md)
* [ipam.FHRPGroup](../models/ipam/fhrpgroup.md)
* [ipam.FHRPGroupAssignment](../models/ipam/fhrpgroupassignment.md)
* [ipam.IPAddress](../models/ipam/ipaddress.md)
* [ipam.IPRange](../models/ipam/iprange.md)
* [ipam.Prefix](../models/ipam/prefix.md)
@@ -76,6 +80,7 @@ These are considered the "core" application models which are used to model netwo
Organization models are used to organize and classify primary models.
* [circuits.CircuitGroup](../models/circuits/circuitgroup.md)
* [circuits.CircuitType](../models/circuits/circuittype.md)
* [dcim.DeviceRole](../models/dcim/devicerole.md)
* [dcim.Manufacturer](../models/dcim/manufacturer.md)
@@ -88,6 +93,7 @@ Organization models are used to organize and classify primary models.
* [tenancy.ContactRole](../models/tenancy/contactrole.md)
* [virtualization.ClusterGroup](../models/virtualization/clustergroup.md)
* [virtualization.ClusterType](../models/virtualization/clustertype.md)
* [vpn.TunnelGroup](../models/vpn/tunnelgroup.md)
### Nested Group Models
@@ -131,3 +137,10 @@ These function as templates to effect the replication of device and virtual mach
* [dcim.PowerOutletTemplate](../models/dcim/poweroutlettemplate.md)
* [dcim.PowerPortTemplate](../models/dcim/powerporttemplate.md)
* [dcim.RearPortTemplate](../models/dcim/rearporttemplate.md)
### Connection Models
Connection models are used to model the connections, or connection endpoints between models.
* [circuits.CircuitTermination](../models/circuits/circuittermination.md)
* [vpn.TunnelTermination](../models/vpn/tunneltermination.md)

View File

@@ -2,9 +2,9 @@
This documentation describes the process of packaging and publishing a new NetBox release. There are three types of release:
* Major release (e.g. v2.11 to v3.0)
* Minor release (e.g. v3.2 to v3.3)
* Patch release (e.g. v3.3.0 to v3.3.1)
* Major release (e.g. v3.7.8 to v4.0.0)
* Minor release (e.g. v4.0.10 to v4.1.0)
* Patch release (e.g. v4.1.0 to v4.1.1)
While major releases generally introduce some very substantial change to the application, they are typically treated the same as minor version increments for the purpose of release packaging.
@@ -19,7 +19,7 @@ Sometimes it becomes necessary to constrain dependencies to a particular version
djangorestframework==3.8.1
```
These version constraints are added to `base_requirements.txt` to ensure that newer packages are not installed when updating the pinned dependencies in `requirements.txt` (see the [Update Requirements](#update-requirements) section below). Before each new minor version of NetBox is released, all such constraints on dependent packages should be addressed if feasible. This guards against the collection of stale constraints over time.
These version constraints are added to `base_requirements.txt` to ensure that newer packages are not installed when updating the pinned dependencies in `requirements.txt` (see the [Update Requirements](#update-python-dependencies) section below). Before each new minor version of NetBox is released, all such constraints on dependent packages should be addressed if feasible. This guards against the collection of stale constraints over time.
### Close the Release Milestone
@@ -90,7 +90,7 @@ Updated language translations should be pulled from [Transifex](https://app.tran
### Update Version and Changelog
* Update the `VERSION` constant in `settings.py` to the new release version.
* Update the version and published date in `release.yaml` with the current version & date. Add a designation (e.g.g `beta1`) if applicable.
* Update the example version numbers in the feature request and bug report templates under `.github/ISSUE_TEMPLATES/`.
* Replace the "FUTURE" placeholder in the release notes with the current date.
@@ -117,16 +117,6 @@ Create a [new release](https://github.com/netbox-community/netbox/releases/new)
Once created, the release will become available for users to install.
### Update the Development Version
On the `develop` branch, update `VERSION` in `settings.py` to point to the next release. For example, if you just released v3.3.1, set:
```
VERSION = 'v3.3.2-dev'
```
Commit this change with the comment "PRVB" (for _post-release version bump_) and push the commit upstream.
### Update the Public Documentation
After a release has been published, the public NetBox documentation needs to be updated. This is accomplished by running two actions on the [netboxlabs-docs](https://github.com/netboxlabs/netboxlabs-docs) repository.

View File

@@ -41,7 +41,7 @@ Line breaks are permitted following binary operators.
### Enforcing Code Style
The [`pycodestyle`](https://pypi.org/project/pycodestyle/) utility (formerly `pep8`) is used by the CI process to enforce code style. A [pre-commit hook](./getting-started.md#2-enable-pre-commit-hooks) which runs this automatically is included with NetBox. To invoke `pycodestyle` manually, run:
The [`pycodestyle`](https://pypi.org/project/pycodestyle/) utility (formerly `pep8`) is used by the CI process to enforce code style. A [pre-commit hook](./getting-started.md#3-enable-pre-commit-hooks) which runs this automatically is included with NetBox. To invoke `pycodestyle` manually, run:
```
pycodestyle --ignore=W504,E501 netbox/

View File

@@ -1,9 +1,10 @@
# Event Rules
NetBox includes the ability to execute certain functions in response to internal object changes. These include:
NetBox includes the ability to automatically perform certain functions in response to internal events. These include:
* [Scripts](../customization/custom-scripts.md) execution
* [Webhooks](../integrations/webhooks.md) execution
* Executing a [custom script](../customization/custom-scripts.md)
* Sending a [webhook](../integrations/webhooks.md)
* Generating [user notifications](../features/notifications.md)
For example, suppose you want to automatically configure a monitoring system to start monitoring a device when its operational status is changed to active, and remove it from monitoring for any other status. You can create a webhook in NetBox for the device model and craft its content and destination URL to effect the desired change on the receiving system. You can then associate an event rule with this webhook and the webhook will be sent automatically by NetBox whenever the configured constraints are met.

View File

@@ -56,6 +56,10 @@ A site typically represents a building within a region and/or site group. Each s
A location can be any logical subdivision within a building, such as a floor or room. Like regions and site groups, locations can be nested into a self-recursive hierarchy for maximum flexibility. And like sites, each location has an operational status assigned to it.
## Rack Types
A rack type represents a unique specification of a rack which exists in the real world. Each rack type can be setup with weight, height, and unit ordering. New racks of this type can then be created in NetBox, and any associated specifications will be automatically replicated from the device type.
## Racks
Finally, NetBox models each equipment rack as a discrete object within a site and location. These are physical objects into which devices are installed. Each rack can be assigned an operational status, type, facility ID, and other attributes related to inventory tracking. Each rack also must define a height (in rack units) and width, and may optionally specify its physical dimensions.

View File

@@ -0,0 +1,10 @@
# Notifications
!!! info "This feature was introduced in NetBox v4.1."
NetBox includes a system for generating user notifications, which can be marked as read or deleted by individual users. There are two built-in mechanisms for generating a notification:
* A user can subscribe to an object. When that object is modified, a notification is created to inform the user of the change.
* An [event rule](./event-rules.md) can be defined to automatically generate a notification for one or more users in response to specific system events.
Additionally, NetBox plugins can generate notifications for their own purposes.

View File

@@ -112,4 +112,4 @@ Authorization: Token $TOKEN
## Disabling the GraphQL API
If not needed, the GraphQL API can be disabled by setting the [`GRAPHQL_ENABLED`](../configuration/miscellaneous.md#graphql_enabled) configuration parameter to False and restarting NetBox.
If not needed, the GraphQL API can be disabled by setting the [`GRAPHQL_ENABLED`](../configuration/graphql-api.md#graphql_enabled) configuration parameter to False and restarting NetBox.

View File

@@ -101,7 +101,7 @@ See the [filtering documentation](../reference/filtering.md) for more details on
## Serialization
The REST API employs two types of serializers to represent model data: base serializers and nested serializers. The base serializer is used to present the complete view of a model. This includes all database table fields which comprise the model, and may include additional metadata. A base serializer includes relationships to parent objects, but **does not** include child objects. For example, the `VLANSerializer` includes a nested representation its parent VLANGroup (if any), but does not include any assigned Prefixes.
The REST API generally represents objects in one of two ways: complete or brief. The base serializer is used to present the complete view of an object. This includes all database table fields which comprise the model, and may include additional metadata. A base serializer includes relationships to parent objects, but **does not** include child objects. For example, the `VLANSerializer` includes a nested representation its parent VLANGroup (if any), but does not include any assigned Prefixes. Serializers employ a minimal "brief" representation of related objects, which includes only the attributes prudent for identifying the object.
```json
{
@@ -139,7 +139,7 @@ The REST API employs two types of serializers to represent model data: base seri
### Related Objects
Related objects (e.g. `ForeignKey` fields) are represented using nested serializers. A nested serializer provides a minimal representation of an object, including only its direct URL and enough information to display the object to a user. When performing write API actions (`POST`, `PUT`, and `PATCH`), related objects may be specified by either numeric ID (primary key), or by a set of attributes sufficiently unique to return the desired object.
Related objects (e.g. `ForeignKey` fields) are included using nested brief representations. This is a minimal representation of an object, including only its direct URL and enough information to display the object to a user. When performing write API actions (`POST`, `PUT`, and `PATCH`), related objects may be specified by either numeric ID (primary key), or by a set of attributes sufficiently unique to return the desired object.
For example, when creating a new device, its rack can be specified by NetBox ID (PK):
@@ -151,7 +151,7 @@ For example, when creating a new device, its rack can be specified by NetBox ID
}
```
Or by a set of nested attributes which uniquely identify the rack:
Or by a set of attributes which uniquely identify the rack:
```json
{

Binary file not shown.

Before

Width:  |  Height:  |  Size: 193 KiB

After

Width:  |  Height:  |  Size: 403 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 422 KiB

After

Width:  |  Height:  |  Size: 548 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 433 KiB

After

Width:  |  Height:  |  Size: 481 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 510 KiB

After

Width:  |  Height:  |  Size: 562 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 341 KiB

After

Width:  |  Height:  |  Size: 372 KiB

View File

@@ -0,0 +1,15 @@
# Circuit Groups
!!! info "This feature was introduced in NetBox v4.1."
[Circuits](./circuit.md) can be arranged into administrative groups for organization. The assignment of a circuit to a group is optional.
## Fields
### Name
A unique human-friendly name.
### Slug
A unique URL-friendly identifier. (This value can be used for filtering.)

View File

@@ -0,0 +1,25 @@
# Circuit Group Assignments
Circuits can be assigned to [circuit groups](./circuitgroup.md) for correlation purposes. For instance, three circuits, each belonging to a different provider, may each be assigned to the same circuit group. Each assignment may optionally include a priority designation.
## Fields
### Group
The [circuit group](./circuitgroup.md) being assigned.
### Circuit
The [circuit](./circuit.md) that is being assigned to the group.
### Priority
The circuit's operation priority relative to its peers within the group. The assignment of a priority is optional. Choices include:
* Primary
* Secondary
* Tertiary
* Inactive
!!! tip
Additional priority choices may be defined by setting `CircuitGroupAssignment.priority` under the [`FIELD_CHOICES`](../../configuration/data-validation.md#field_choices) configuration parameter.

View File

@@ -14,6 +14,12 @@ Module bays represent a space or slot within a device in which a field-replaceab
The device to which this module bay belongs.
### Module
!!! info "This feature was introduced in NetBox v4.1."
The module to which this bay belongs (optional).
### Name
The module bay's name. Must be unique to the parent device.

View File

@@ -39,3 +39,9 @@ An alternative part number to uniquely identify the module type.
### Weight
The numeric weight of the module, including a unit designation (e.g. 3 kilograms or 1 pound).
### Airflow
!!! info "The `airflow` field was introduced in NetBox v4.1."
The direction in which air circulates through the device chassis for cooling.

View File

@@ -20,6 +20,10 @@ The [location](./location.md) within a site where the rack has been installed (o
The rack's name or identifier. Must be unique to the rack's location, if assigned.
### Rack Type
The [physical type](./racktype.md) of this rack. The rack type defines physical attributes such as height and weight.
### Status
Operational status.
@@ -43,44 +47,5 @@ The unique physical serial number assigned to this rack.
A unique, locally-administered label used to identify hardware resources.
### Type
A rack can be designated as one of the following types:
* 2-post frame
* 4-post frame
* 4-post cabinet
* Wall-mounted frame
* Wall-mounted cabinet
### Width
The canonical distance between the two vertical rails on a face. (This is typically 19 inches, however other standard widths exist.)
### Height
The height of the rack, measured in units.
### Starting Unit
The number of the numerically lowest unit in the rack. This value defaults to one, but may be higher in certain situations. For example, you may want to model only a select range of units within a shared physical rack (e.g. U13 through U24).
### Outer Dimensions
The external width and depth of the rack can be tracked to aid in floorplan calculations. These measurements must be designated in either millimeters or inches.
### Mounting Depth
The maximum depth of a mounted device that the rack can accommodate, in millimeters. For four-post frames or cabinets, this is the horizontal distance between the front and rear vertical rails. (Note that this measurement does _not_ include space between the rails and the cabinet doors.)
### Weight
The numeric weight of the rack, including a unit designation (e.g. 10 kilograms or 20 pounds).
### Maximum Weight
The maximum total weight capacity for all installed devices, inclusive of the rack itself.
### Descending Units
If selected, the rack's elevation will display unit 1 at the top of the rack. (Most racks use ascending numbering, with unit 1 assigned to the bottommost position.)
!!! note
Some additional fields pertaining to physical attributes such as height and weight can also be defined on each rack, but should generally be defined instead on the [rack type](./racktype.md).

View File

@@ -0,0 +1,61 @@
# Rack Types
!!! info "This feature was introduced in NetBox v4.1."
A rack type defines the physical characteristics of a particular model of [rack](./rack.md).
## Fields
### Manufacturer
The [manufacturer](./manufacturer.md) which produces this type of rack.
### Model
The model number assigned to this rack type by its manufacturer. Must be unique to the manufacturer.
### Slug
A unique URL-friendly representation of the model identifier. (This value can be used for filtering.)
### Form Factor
A rack can be designated as one of the following form factors:
* 2-post frame
* 4-post frame
* 4-post cabinet
* Wall-mounted frame
* Wall-mounted cabinet
### Width
The canonical distance between the two vertical rails on a face. (This is typically 19 inches, however other standard widths exist.)
### Height
The height of the rack, measured in units.
### Starting Unit
The number of the numerically lowest unit in the rack. This value defaults to one, but may be higher in certain situations. For example, you may want to model only a select range of units within a shared physical rack (e.g. U13 through U24).
### Outer Dimensions
The external width and depth of the rack can be tracked to aid in floorplan calculations. These measurements must be designated in either millimeters or inches.
### Mounting Depth
The maximum depth of a mounted device that the rack can accommodate, in millimeters. For four-post frames or cabinets, this is the horizontal distance between the front and rear vertical rails. (Note that this measurement does _not_ include space between the rails and the cabinet doors.)
### Weight
The numeric weight of the rack, including a unit designation (e.g. 10 kilograms or 20 pounds).
### Maximum Weight
The maximum total weight capacity for all installed devices, inclusive of the rack itself.
### Descending Units
If selected, the rack's elevation will display unit 1 at the top of the rack. (Most racks use ascending numbering, with unit 1 assigned to the bottommost position.)

View File

@@ -42,13 +42,26 @@ The type of data this field holds. This must be one of the following:
For object and multiple-object fields only. Designates the type of NetBox object being referenced.
### Related Object Filter
!!! info "This field was introduced in NetBox v4.1."
For object and multi-object custom fields, a filter may be defined to limit the available objects when populating a field value. This filter maps object attributes to values. For example, `{"status": "active"}` will include only objects with a status of "active."
!!! warning
This setting is employed for convenience only, and should not be relied upon to enforce data integrity.
### Weight
A numeric weight used to override alphabetic ordering of fields by name. Custom fields with a lower weight will be listed before those with a higher weight. (Note that weight applies within the context of a custom field group, if defined.)
### Required
If checked, this custom field must be populated with a valid value for the object to pass validation.
If enabled, this custom field must be populated with a valid value for the object to pass validation.
### Unique
If enabled, each object must have a unique value set for this custom field (per object type).
### Description

View File

@@ -18,17 +18,22 @@ The type(s) of object in NetBox that will trigger the rule.
If not selected, the event rule will not be processed.
### Events
### Events Types
The events which will trigger the rule. At least one event type must be selected.
The event types which will trigger the rule. At least one event type must be selected.
| Name | Description |
|------------|--------------------------------------|
| Creations | A new object has been created |
| Updates | An existing object has been modified |
| Deletions | An object has been deleted |
| Job starts | A job for an object starts |
| Job ends | A job for an object terminates |
| Name | Description |
|----------------|---------------------------------------------|
| Object created | A new object has been created |
| Object updated | An existing object has been modified |
| Object deleted | An object has been deleted |
| Job started | A background job is initiated |
| Job completed | A background job completes successfully |
| Job failed | A background job fails |
| Job errored | A background job is aborted due to an error |
!!! tip "Custom Event Types"
The above list includes only built-in event types. NetBox plugins can also register their own custom event types.
### Conditions

View File

@@ -0,0 +1,17 @@
# Notification
A notification alerts a user that a specific action has taken place in NetBox, such as an object being modified or a background job completing. A notification may be generated via a user's [subscription](./subscription.md) to a particular object, or by an event rule targeting a [notification group](./notificationgroup.md) of which the user is a member.
## Fields
### User
The recipient of the notification.
### Object
The object to which the notification relates.
### Event Type
The type of event indicated by the notification.

View File

@@ -0,0 +1,17 @@
# Notification Group
A set of NetBox users and/or groups of users identified as recipients for certain [notifications](./notification.md).
## Fields
### Name
The name of the notification group.
### Users
One or more users directly designated as members of the notification group.
### Groups
All users of any selected groups are considered as members of the notification group.

View File

@@ -0,0 +1,15 @@
# Subscription
A record indicating that a user is to be notified of any changes to a particular NetBox object. A notification maps exactly one user to exactly one object.
When an object to which a user is subscribed changes, a [notification](./notification.md) is generated for the user.
## Fields
### User
The subscribed user.
### Object
The object to which the user is subscribed.

View File

@@ -14,9 +14,11 @@ A unique human-friendly name.
A unique URL-friendly identifier. (This value can be used for filtering.)
### Minimum & Maximum VLAN IDs
### VLAN ID Ranges
A minimum and maximum child VLAN ID must be set for each group. (These default to 1 and 4094 respectively.) VLANs created within a group must have a VID that falls between these values (inclusive).
!!! info "This field replaced the legacy `min_vid` and `max_vid` fields in NetBox v4.1."
The set of VLAN IDs which are encompassed by the group. By default, this will be the entire range of valid IEEE 802.1Q VLAN IDs (1 to 4094, inclusive). VLANs created within a group must have a VID that falls within one of these ranges. Ranges may not overlap.
### Scope

View File

@@ -10,4 +10,4 @@ A human-friendly name that is unique to the assigned virtual machine.
### Size
The allocated disk size, in gigabytes.
The allocated disk size, in megabytes.

View File

@@ -50,4 +50,13 @@ The amount of running memory provisioned, in megabytes.
### Disk
The amount of disk storage provisioned, in gigabytes.
The amount of disk storage provisioned, in megabytes.
!!! warning
This field may be directly modified only on virtual machines which do not define discrete [virtual disks](./virtualdisk.md). Otherwise, it will report the sum of all attached disks.
### Serial Number
!!! info "This field was introduced in NetBox v4.1."
Optional serial number assigned to this virtual machine. Unlike devices, uniqueness is not enforced for virtual machine serial numbers.

View File

@@ -20,6 +20,12 @@ The operational status of the link. Options include:
The service set identifier (SSID) for the wireless link (optional).
### Distance
!!! info "This field was introduced in NetBox v4.1."
The distance between the link's two endpoints, including a unit designation (e.g. 100 meters or 25 feet).
### Authentication Type
The type of wireless authentication in use. Options include:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -1,21 +1,24 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1100 320">
<g fill="#9cc8f8" stroke="#9cc8f8">
<circle cx="37" cy="284" r="23"/>
<circle cx="101" cy="37" r="23"/>
<circle cx="101" cy="220" r="23"/>
<circle cx="284" cy="220" r="23"/>
<rect x="93" y="37" width="16" height="180"/>
<rect x="101" y="212" width="180" height="16"/>
<rect x="93" y="212" width="16" height="90" transform="rotate(45 101 220)"/>
</g>
<g fill="#1685fc" stroke="#1685fc">
<circle cx="284" cy="37" r="23"/>
<circle cx="37" cy="101" r="23"/>
<circle cx="220" cy="101" r="23"/>
<circle cx="220" cy="284" r="23"/>
<rect x="37" y="93" width="180" height="16"/>
<rect x="212" y="101" width="16" height="180"/>
<rect x="212" y="93" width="16" height="90" transform="rotate(225 220 101)"/>
<path transform="translate(380, 8)" d="M13.60 200L13.60 104L36.40 104L36.40 119.40L36.80 119.40Q40.20 112.20 47.20 106.90Q54.20 101.60 66.20 101.60L66.20 101.60Q75.80 101.60 82.50 104.80Q89.20 108 93.40 113.20Q97.60 118.40 99.40 125.20Q101.20 132 101.20 139.40L101.20 139.40L101.20 200L77.20 200L77.20 151.40Q77.20 147.40 76.80 142.50Q76.40 137.60 74.70 133.30Q73 129 69.40 126.10Q65.80 123.20 59.60 123.20L59.60 123.20Q53.60 123.20 49.50 125.20Q45.40 127.20 42.70 130.60Q40 134 38.80 138.40Q37.60 142.80 37.60 147.60L37.60 147.60L37.60 200L13.60 200ZM224.80 160.40L151.60 160.40Q152.80 171.20 160 177.20Q167.20 183.20 177.40 183.20L177.40 183.20Q186.40 183.20 192.50 179.50Q198.60 175.80 203.20 170.20L203.20 170.20L220.40 183.20Q212 193.60 201.60 198Q191.20 202.40 179.80 202.40L179.80 202.40Q169 202.40 159.40 198.80Q149.80 195.20 142.80 188.60Q135.80 182 131.70 172.70Q127.60 163.40 127.60 152L127.60 152Q127.60 140.60 131.70 131.30Q135.80 122 142.80 115.40Q149.80 108.80 159.40 105.20Q169 101.60 179.80 101.60L179.80 101.60Q189.80 101.60 198.10 105.10Q206.40 108.60 212.30 115.20Q218.20 121.80 221.50 131.50Q224.80 141.20 224.80 153.80L224.80 153.80L224.80 160.40ZM151.60 142.40L200.80 142.40Q200.60 131.80 194.20 125.70Q187.80 119.60 176.40 119.60L176.40 119.60Q165.60 119.60 159.30 125.80Q153 132 151.60 142.40L151.60 142.40ZM259.80 124.40L240.00 124.40L240.00 104L259.80 104L259.80 76.20L283.80 76.20L283.80 104L310.20 104L310.20 124.40L283.80 124.40L283.80 166.40Q283.80 173.60 286.50 177.80Q289.20 182 297.20 182L297.20 182Q300.40 182 304.20 181.30Q308 180.60 310.20 179L310.20 179L310.20 199.20Q306.40 201 300.90 201.70Q295.40 202.40 291.20 202.40L291.20 202.40Q281.60 202.40 275.50 200.30Q269.40 198.20 265.90 193.90Q262.40 189.60 261.10 183.20Q259.80 176.80 259.80 168.40L259.80 168.40L259.80 124.40ZM333.20 200L333.20 48.80L357.20 48.80L357.20 116.20L357.80 116.20Q359.60 113.80 362.40 111.30Q365.20 108.80 369.20 106.60Q373.20 104.40 378.40 103Q383.60 101.60 390.40 101.60L390.40 101.60Q400.60 101.60 409.20 105.50Q417.80 109.40 423.90 116.20Q430 123 433.40 132.20Q436.80 141.40 436.80 152L436.80 152Q436.80 162.60 433.60 171.80Q430.40 181 424.20 187.80Q418 194.60 409.20 198.50Q400.40 202.40 389.40 202.40L389.40 202.40Q379.20 202.40 370.40 198.40Q361.60 194.40 356.40 185.60L356.40 185.60L356 185.60L356 200L333.20 200ZM412.80 152L412.80 152Q412.80 146.40 410.90 141.20Q409 136 405.30 132Q401.60 128 396.40 125.60Q391.20 123.20 384.60 123.20L384.60 123.20Q378 123.20 372.80 125.60Q367.60 128 363.90 132Q360.20 136 358.30 141.20Q356.40 146.40 356.40 152L356.40 152Q356.40 157.60 358.30 162.80Q360.20 168 363.90 172Q367.60 176 372.80 178.40Q378 180.80 384.60 180.80L384.60 180.80Q391.20 180.80 396.40 178.40Q401.60 176 405.30 172Q409 168 410.90 162.80Q412.80 157.60 412.80 152ZM458.40 152L458.40 152Q458.40 140.60 462.50 131.30Q466.60 122 473.60 115.40Q480.60 108.80 490.20 105.20Q499.80 101.60 510.60 101.60L510.60 101.60Q521.40 101.60 531 105.20Q540.60 108.80 547.60 115.40Q554.60 122 558.70 131.30Q562.80 140.60 562.80 152L562.80 152Q562.80 163.40 558.70 172.70Q554.60 182 547.60 188.60Q540.60 195.20 531 198.80Q521.40 202.40 510.60 202.40L510.60 202.40Q499.80 202.40 490.20 198.80Q480.60 195.20 473.60 188.60Q466.60 182 462.50 172.70Q458.40 163.40 458.40 152ZM482.40 152L482.40 152Q482.40 157.60 484.30 162.80Q486.20 168 489.90 172Q493.60 176 498.80 178.40Q504 180.80 510.60 180.80L510.60 180.80Q517.20 180.80 522.40 178.40Q527.60 176 531.30 172Q535 168 536.90 162.80Q538.80 157.60 538.80 152L538.80 152Q538.80 146.40 536.90 141.20Q535 136 531.30 132Q527.60 128 522.40 125.60Q517.20 123.20 510.60 123.20L510.60 123.20Q504 123.20 498.80 125.60Q493.60 128 489.90 132Q486.20 136 484.30 141.20Q482.40 146.40 482.40 152ZM575.40 200L614 148.40L580.80 104L610 104L629.20 132.80L650 104L677.40 104L644.60 148.40L683.20 200L654 200L629 165.60L603.80 200L575.40 200Z"/>
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1299.6 366">
<defs>
<style>
.cls-1 {
fill: #00857d;
}
.cls-1, .cls-2 {
stroke-width: 0px;
}
.cls-2 {
fill: #001423;
}
</style>
</defs>
<g id="Layer_1-2" data-name="Layer 1">
<g>
<path class="cls-2" d="M337.27,228.59c-12.35,0-22.88,7.8-26.94,18.74h-174.71c-2.9-7.83-9.12-14.04-16.95-16.95V55.67c10.94-4.06,18.74-14.59,18.74-26.94,0-15.87-12.86-28.73-28.73-28.73s-28.73,12.86-28.73,28.73c0,12.35,7.8,22.88,18.74,26.94v174.71c-10.94,4.06-18.74,14.59-18.74,26.94,0,4.28.94,8.33,2.62,11.98l-41.85,41.85c-3.65-1.68-7.7-2.62-11.98-2.62-15.87,0-28.73,12.86-28.73,28.73s12.86,28.73,28.73,28.73,28.73-12.86,28.73-28.73c0-4.28-.94-8.33-2.62-11.98l41.85-41.85c3.65,1.68,7.7,2.62,11.98,2.62,12.35,0,22.88-7.8,26.94-18.74h174.71c4.06,10.94,14.59,18.74,26.94,18.74,15.87,0,28.73-12.86,28.73-28.73s-12.86-28.73-28.73-28.73Z"/>
<path class="cls-1" d="M366,28.73c0,15.87-12.86,28.73-28.73,28.73-4.28,0-8.33-.94-11.98-2.62l-41.85,41.85c1.68,3.65,2.62,7.7,2.62,11.98,0,12.35-7.8,22.88-18.74,26.94v174.71c10.94,4.06,18.74,14.59,18.74,26.94,0,15.87-12.86,28.73-28.73,28.73s-28.73-12.86-28.73-28.73c0-12.35,7.8-22.88,18.74-26.94v-174.71c-7.83-2.9-14.04-9.12-16.95-16.95H55.67c-4.06,10.94-14.59,18.74-26.94,18.74-15.87,0-28.73-12.86-28.73-28.73s12.86-28.73,28.73-28.73c12.35,0,22.88,7.8,26.94,18.74h174.71c4.06-10.94,14.59-18.74,26.94-18.74,4.28,0,8.33.94,11.98,2.62l41.85-41.85c-1.68-3.65-2.62-7.7-2.62-11.98,0-15.87,12.86-28.73,28.73-28.73s28.73,12.86,28.73,28.73ZM579.76,136.45c-4.63-4.38-10.18-7.68-16.24-9.66-6.09-2.07-12.48-3.11-18.91-3.08-9.75-.17-19.37,2.17-27.95,6.78-2.68,1.56-5.23,3.35-7.61,5.34v-9.04h-34.53v134.64h34.53v-69.06c-.08-5.7.68-11.38,2.26-16.86,1.26-4.03,3.36-7.74,6.17-10.89,2.41-2.69,5.44-4.74,8.84-5.96,3.71-1.26,7.6-1.89,11.51-1.85,2.99,0,5.97.41,8.84,1.23,2.62.91,5,2.38,6.99,4.32,2.11,2.28,3.78,4.93,4.93,7.81,1.32,4.12,1.95,8.42,1.85,12.74v78.52h34.53v-85.1c.22-7.94-1.18-15.84-4.11-23.23-2.37-6.33-6.16-12.03-11.1-16.65ZM744.41,169.34c2.28,8.16,3.46,16.6,3.49,25.08v13.77h-98.46c.38,2.33,1.22,4.57,2.47,6.58,1.83,3.77,4.51,7.08,7.81,9.66,3.42,2.8,7.32,4.96,11.51,6.37,4.42,1.57,9.08,2.33,13.77,2.26,5.63.24,11.21-1.19,16.03-4.11,5.19-3.31,9.78-7.48,13.57-12.33l3.49-4.11,26.31,20.14-3.29,4.52c-14.18,18.09-34.12,27.34-59.2,27.34-9.78.09-19.49-1.72-28.57-5.34-8.34-3.34-15.84-8.46-21.99-15.01-6.02-6.49-10.7-14.1-13.77-22.4-3.18-8.83-4.78-18.16-4.73-27.54-.02-9.49,1.72-18.9,5.14-27.75,3.36-8.35,8.32-15.96,14.59-22.4,6.24-6.44,13.72-11.54,21.99-15.01,8.74-3.58,18.1-5.4,27.54-5.34,11.92,0,21.99,2.06,30.42,6.37,7.92,3.9,14.87,9.52,20.35,16.44,5.36,6.74,9.28,14.5,11.51,22.82ZM711.31,178.39c-.43-2.36-.98-4.69-1.64-6.99-1.14-3.45-3.04-6.61-5.55-9.25-2.45-2.78-5.56-4.9-9.04-6.17-8.68-3.42-18.36-3.27-26.93.41-3.87,1.69-7.37,4.13-10.28,7.19-2.81,2.83-5.05,6.18-6.58,9.87-.73,1.58-1.28,3.23-1.64,4.93h61.66ZM827.24,230.8c-2.56.57-5.18.84-7.81.82-2.41.12-4.82-.37-6.99-1.44-1.42-1.08-2.55-2.49-3.29-4.11-.93-2.36-1.42-4.87-1.44-7.4-.21-3.29-.41-6.58-.41-9.87v-50.57h33.71v-31.45h-33.71v-34.53h-34.53v34.53h-21.79v31.45h21.79v58.79c-.04,5.15.24,10.3.82,15.42.38,5.56,1.99,10.97,4.73,15.83,3.21,5.18,7.85,9.32,13.36,11.92,5.76,2.88,13.36,4.32,23.43,4.32,3.71-.04,7.42-.31,11.1-.82,4.47-.56,8.79-1.95,12.74-4.11l2.88-1.44v-34.33l-8.43,4.93c-1.93,1.02-4.01,1.72-6.17,2.06ZM997.03,166.46c3.16,8.91,4.76,18.3,4.73,27.75.04,9.32-1.56,18.57-4.73,27.34-3.07,8.3-7.75,15.92-13.77,22.4-6.1,6.56-13.53,11.74-21.79,15.21-8.94,3.62-18.51,5.44-28.16,5.34-9.17-.04-18.22-2.07-26.52-5.96-4.12-1.71-7.93-4.07-11.31-6.99v9.87h-34.53V53.41h34.53v83.04c3.23-2.59,6.75-4.8,10.48-6.58,8.54-4.07,17.88-6.18,27.34-6.17,9.65-.09,19.22,1.72,28.16,5.34,8.18,3.52,15.58,8.62,21.79,15.01,5.91,6.58,10.57,14.17,13.77,22.4ZM963.11,178.8c-1.41-4.39-3.8-8.39-6.99-11.72-3.07-3.26-6.78-5.85-10.89-7.61-9.47-3.57-19.92-3.57-29.39,0-4.12,1.76-7.83,4.35-10.89,7.61-3.12,3.37-5.5,7.37-6.99,11.72-1.71,4.96-2.55,10.17-2.47,15.42-.05,5.24.78,10.45,2.47,15.42,1.54,4.27,3.91,8.18,6.99,11.51,3.01,3.32,6.74,5.92,10.89,7.61,9.42,3.83,19.97,3.83,29.39,0,4.16-1.68,7.88-4.28,10.89-7.61,3.15-3.28,5.54-7.21,6.99-11.51,1.68-4.96,2.52-10.18,2.47-15.42.07-5.24-.77-10.46-2.47-15.42ZM1136.6,244.16c-28.24,27.15-72.89,27.15-101.13,0-13.17-13.29-20.56-31.24-20.55-49.95-.1-28.4,16.95-54.05,43.17-64.95,17.9-7.4,38.01-7.4,55.91,0,26.14,11,43.15,36.59,43.17,64.95,0,18.71-7.38,36.66-20.55,49.95ZM1118.51,178.8c-1.42-4.34-3.73-8.33-6.78-11.72-3.1-3.22-6.8-5.8-10.89-7.61-9.55-3.56-20.05-3.56-29.6,0-4.09,1.81-7.79,4.39-10.89,7.61-3.05,3.39-5.36,7.38-6.78,11.72-1.88,4.92-2.79,10.15-2.67,15.42-.08,5.26.82,10.49,2.67,15.42,1.47,4.25,3.77,8.17,6.78,11.51,3.05,3.28,6.77,5.87,10.89,7.61,9.49,3.84,20.11,3.84,29.6,0,4.13-1.74,7.84-4.33,10.89-7.61,3.01-3.34,5.32-7.26,6.78-11.51,1.75-4.95,2.66-10.16,2.67-15.42,0-5.25-.9-10.47-2.67-15.42ZM1291.58,126.79h-42.34l-26.52,39.47-26.93-39.47h-44.4l48.1,63.1-54.27,71.53h42.96l33.5-47.69,33.71,47.69h44.19l-54.27-71.53,46.25-63.1Z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -0,0 +1,99 @@
# Background Jobs
!!! info "This feature was introduced in NetBox v4.1."
NetBox plugins can defer certain operations by enqueuing [background jobs](../../features/background-jobs.md), which are executed asynchronously by background workers. This is helpful for decoupling long-running processes from the user-facing request-response cycle.
For example, your plugin might need to fetch data from a remote system. Depending on the amount of data and the responsiveness of the remote server, this could take a few minutes. Deferring this task to a queued job ensures that it can be completed in the background, without interrupting the user. The data it fetches can be made available once the job has completed.
## Job Runners
A background job implements a basic [Job](../../models/core/job.md) executor for all kinds of tasks. It has logic implemented to handle the management of the associated job object, rescheduling of periodic jobs in the given interval and error handling. Adding custom jobs is done by subclassing NetBox's `JobRunner` class.
::: netbox.jobs.JobRunner
#### Example
```python title="jobs.py"
from netbox.jobs import JobRunner
class MyTestJob(JobRunner):
class Meta:
name = "My Test Job"
def run(self, *args, **kwargs):
obj = self.job.object
# your logic goes here
```
You can schedule the background job from within your code (e.g. from a model's `save()` method or a view) by calling `MyTestJob.enqueue()`. This method passes through all arguments to `Job.enqueue()`. However, no `name` argument must be passed, as the background job name will be used instead.
### Attributes
`JobRunner` attributes are defined under a class named `Meta` within the job. These are optional, but encouraged.
#### `name`
This is the human-friendly names of your background job. If omitted, the class name will be used.
### Scheduled Jobs
As described above, jobs can be scheduled for immediate execution or at any later time using the `enqueue()` method. However, for management purposes, the `enqueue_once()` method allows a job to be scheduled exactly once avoiding duplicates. If a job is already scheduled for a particular instance, a second one won't be scheduled, respecting thread safety. An example use case would be to schedule a periodic task that is bound to an instance in general, but not to any event of that instance (such as updates). The parameters of the `enqueue_once()` method are identical to those of `enqueue()`.
!!! tip
It is not forbidden to `enqueue()` additional jobs while an interval schedule is active. An example use of this would be to schedule a periodic daily synchronization, but also trigger additional synchronizations on demand when the user presses a button.
#### Example
```python title="jobs.py"
from netbox.jobs import JobRunner
class MyHousekeepingJob(JobRunner):
class Meta:
name = "Housekeeping"
def run(self, *args, **kwargs):
# your logic goes here
```
```python title="__init__.py"
from netbox.plugins import PluginConfig
class MyPluginConfig(PluginConfig):
def ready(self):
from .jobs import MyHousekeepingJob
MyHousekeepingJob.setup(interval=60)
```
## Task queues
Three task queues of differing priority are defined by default:
* High
* Default
* Low
Any tasks in the "high" queue are completed before the default queue is checked, and any tasks in the default queue are completed before those in the "low" queue.
Plugins can also add custom queues for their own needs by setting the `queues` attribute under the PluginConfig class. An example is included below:
```python
class MyPluginConfig(PluginConfig):
name = 'myplugin'
...
queues = [
'foo',
'bar',
]
```
The `PluginConfig` above creates two custom queues with the following names `my_plugin.foo` and `my_plugin.bar`. (The plugin's name is prepended to each queue to avoid conflicts between plugins.)
!!! warning "Configuring the RQ worker process"
By default, NetBox's RQ worker process only services the high, default, and low queues. Plugins which introduce custom queues should advise users to either reconfigure the default worker, or run a dedicated worker specifying the necessary queues. For example:
```
python manage.py rqworker my_plugin.foo my_plugin.bar
```

View File

@@ -1,30 +0,0 @@
# Background Tasks
NetBox supports the queuing of tasks that need to be performed in the background, decoupled from the request-response cycle, using the [Python RQ](https://python-rq.org/) library. Three task queues of differing priority are defined by default:
* High
* Default
* Low
Any tasks in the "high" queue are completed before the default queue is checked, and any tasks in the default queue are completed before those in the "low" queue.
Plugins can also add custom queues for their own needs by setting the `queues` attribute under the PluginConfig class. An example is included below:
```python
class MyPluginConfig(PluginConfig):
name = 'myplugin'
...
queues = [
'foo',
'bar',
]
```
The PluginConfig above creates two custom queues with the following names `my_plugin.foo` and `my_plugin.bar`. (The plugin's name is prepended to each queue to avoid conflicts between plugins.)
!!! warning "Configuring the RQ worker process"
By default, NetBox's RQ worker process only services the high, default, and low queues. Plugins which introduce custom queues should advise users to either reconfigure the default worker, or run a dedicated worker specifying the necessary queues. For example:
```
python manage.py rqworker my_plugin.foo my_plugin.bar
```

View File

@@ -0,0 +1,18 @@
# Event Types
!!! info "This feature was introduced in NetBox v4.1."
Plugins can register their own custom event types for use with NetBox [event rules](../../models/extras/eventrule.md). This is accomplished by calling the `register()` method on an instance of the `EventType` class. This can be done anywhere within the plugin. An example is provided below.
```python
from django.utils.translation import gettext_lazy as _
from netbox.events import EventType, EVENT_TYPE_KIND_SUCCESS
EventType(
name='ticket_opened',
text=_('Ticket opened'),
kind=EVENT_TYPE_KIND_SUCCESS
).register()
```
::: netbox.events.EventType

View File

@@ -47,6 +47,7 @@ project-name/
- __init__.py
- filtersets.py
- graphql.py
- jobs.py
- models.py
- middleware.py
- navigation.py

View File

@@ -130,6 +130,8 @@ For more information about database migrations, see the [Django documentation](h
::: netbox.models.features.ExportTemplatesMixin
::: netbox.models.features.JobsMixin
::: netbox.models.features.JournalingMixin
::: netbox.models.features.TagsMixin

View File

@@ -25,9 +25,11 @@ project-name/
Serializers are responsible for converting Python objects to JSON data suitable for conveying to consumers, and vice versa. NetBox provides the `NetBoxModelSerializer` class for use by plugins to handle the assignment of tags and custom field data. (These features can also be included ad hoc via the `CustomFieldModelSerializer` and `TaggableModelSerializer` classes.)
The default nested representation of an object is defined by the `brief_fields` attributes under the serializer's `Meta` class. (Older versions of NetBox required the definition of a separate nested serializer.)
#### Example
To create a serializer for a plugin model, subclass `NetBoxModelSerializer` in `api/serializers.py`. Specify the model class and the fields to include within the serializer's `Meta` class. It is generally advisable to include a `url` attribute on each serializer. This will render the direct link to access the object being rendered.
To create a serializer for a plugin model, subclass `NetBoxModelSerializer` in `api/serializers.py`. Specify the model class and the fields to include within the serializer's `Meta` class.
```python
# api/serializers.py
@@ -36,40 +38,12 @@ from netbox.api.serializers import NetBoxModelSerializer
from my_plugin.models import MyModel
class MyModelSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='plugins-api:myplugin-api:mymodel-detail'
)
foo = SiteSerializer(nested=True, allow_null=True)
class Meta:
model = MyModel
fields = ('id', 'foo', 'bar', 'baz')
```
### Nested Serializers
There are two cases where it is generally desirable to show only a minimal representation of an object:
1. When displaying an object related to the one being viewed (for example, the region to which a site is assigned)
2. Listing many objects using "brief" mode
To accommodate these, it is recommended to create nested serializers accompanying the "full" serializer for each model. NetBox provides the `WritableNestedSerializer` class for just this purpose. This class accepts a primary key value on write, but displays an object representation for read requests. It also includes a read-only `display` attribute which conveys the string representation of the object.
#### Example
```python
# api/serializers.py
from rest_framework import serializers
from netbox.api.serializers import WritableNestedSerializer
from my_plugin.models import MyModel
class NestedMyModelSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='plugins-api:myplugin-api:mymodel-detail'
)
class Meta:
model = MyModel
fields = ('id', 'display', 'foo')
brief_fields = ('id', 'url', 'display', 'bar')
```
## Viewsets

View File

@@ -191,19 +191,25 @@ class MyView(generic.ObjectView):
### Extra Template Content
Plugins can inject custom content into certain areas of core NetBox views. This is accomplished by subclassing `PluginTemplateExtension`, designating a particular NetBox model, and defining the desired method(s) to render custom content. Five methods are available:
Plugins can inject custom content into certain areas of core NetBox views. This is accomplished by subclassing `PluginTemplateExtension`, optionally designating one or more particular NetBox models, and defining the desired method(s) to render custom content. Five methods are available:
| Method | View | Description |
|---------------------|-------------|-----------------------------------------------------|
| `navbar()` | All | Inject content inside the top navigation bar |
| `list_buttons()` | List view | Add buttons to the top of the page |
| `buttons()` | Object view | Add buttons to the top of the page |
| `alerts()` | Object view | Inject content at the top of the page |
| `left_page()` | Object view | Inject content on the left side of the page |
| `right_page()` | Object view | Inject content on the right side of the page |
| `full_width_page()` | Object view | Inject content across the entire bottom of the page |
| `buttons()` | Object view | Add buttons to the top of the page |
| `list_buttons()` | List view | Add buttons to the top of the page |
!!! info "The `navbar()` and `alerts()` methods were introduced in NetBox v4.1."
Additionally, a `render()` method is available for convenience. This method accepts the name of a template to render, and any additional context data you want to pass. Its use is optional, however.
When a PluginTemplateExtension is instantiated, context data is assigned to `self.context`. Available data include:
To control where the custom content is injected, plugin authors can specify an iterable of models by overriding the `models` attribute on the subclass. Extensions which do not specify a set of models will be invoked on every view, where supported.
When a PluginTemplateExtension is instantiated, context data is assigned to `self.context`. Available data includes:
* `object` - The object being viewed (object views only)
* `model` - The model of the list view (list views only)
@@ -220,7 +226,7 @@ from netbox.plugins import PluginTemplateExtension
from .models import Animal
class SiteAnimalCount(PluginTemplateExtension):
model = 'dcim.site'
models = ['dcim.site']
def right_page(self):
return self.render('netbox_animal_sounds/inc/animal_count.html', extra_context={

View File

@@ -1,5 +1,72 @@
# NetBox v4.0
## v4.0.11 (2024-09-03)
### Bug Fixes
* [#17310](https://github.com/netbox-community/netbox/issues/17310) - Enforce restricted queryset for related objects in GraphQL API requests
* [#17321](https://github.com/netbox-community/netbox/issues/17321) - Ensure the job is attributed to the specified user when using the `runscript` management command
* [#17323](https://github.com/netbox-community/netbox/issues/17323) - Associate job with script object when executed using the `runscript` management command
* [#17337](https://github.com/netbox-community/netbox/issues/17337) - Fix ordering of virtual device contexts by device name
* [#17341](https://github.com/netbox-community/netbox/issues/17341) - Avoid `NoReverseMatch` exceptions with specific dashboard widget configurations
## v4.0.10 (2024-08-29)
### Enhancements
* [#16857](https://github.com/netbox-community/netbox/issues/16857) - Scroll long rendered Markdown content within tables
* [#16905](https://github.com/netbox-community/netbox/issues/16905) - Enable filtering of device components by device status
* [#16949](https://github.com/netbox-community/netbox/issues/16949) - Add device count column to sites table
* [#17072](https://github.com/netbox-community/netbox/issues/17072) - Linkify email addresses & phone numbers in contact assignments list
* [#17177](https://github.com/netbox-community/netbox/issues/17177) - Add facility field to locations filter form
### Bug Fixes
* [#16292](https://github.com/netbox-community/netbox/issues/16292) - Ensure consistent evaluation of queryset for both individual and list GraphQL API queries
* [#16385](https://github.com/netbox-community/netbox/issues/16385) - Restore support for white, gray, and black background colors
* [#16640](https://github.com/netbox-community/netbox/issues/16640) - Fix potential corruption of JSON values in custom fields that are not UI-editable
* [#16670](https://github.com/netbox-community/netbox/issues/16670) - Fix conflicts within OpenAPI schema definition regarding nested serializers
* [#16733](https://github.com/netbox-community/netbox/issues/16733) - Fix bulk edit/delete of objects when using "select all" widget
* [#16756](https://github.com/netbox-community/netbox/issues/16756) - Fix dynamic pagination of custom script results table
* [#16825](https://github.com/netbox-community/netbox/issues/16825) - Avoid `NoReverseMatch` exception when displaying count of related object type with no list view
* [#16946](https://github.com/netbox-community/netbox/issues/16946) - GraphQL API requests with an invalid filter should return an empty set
* [#16959](https://github.com/netbox-community/netbox/issues/16959) - Fix function of "reset" button on objects filter form
* [#16973](https://github.com/netbox-community/netbox/issues/16973) - Fix support for evaluating user token (`$user`) against custom field values in permission constraints
* [#17007](https://github.com/netbox-community/netbox/issues/17007) - Center SSO authentication icon when backend is unnamed
* [#17070](https://github.com/netbox-community/netbox/issues/17070) - Image height & width values should not be required when creating an image attachment via the REST API
* [#17108](https://github.com/netbox-community/netbox/issues/17108) - Ensure template date & time filters always return localtime-aware values
* [#17117](https://github.com/netbox-community/netbox/issues/17117) - Work around Safari rendering bug
* [#17186](https://github.com/netbox-community/netbox/issues/17186) - Fix display of custom links with default style under dark mode
* [#17219](https://github.com/netbox-community/netbox/issues/17219) - Fix system config view exception when custom validator classes are employed
* [#17230](https://github.com/netbox-community/netbox/issues/17230) - Ensure consistent rendering for all dashboard widget colors
* [#17256](https://github.com/netbox-community/netbox/issues/17256) - Fix VLAN group scope selection for non-English languages
* [#17278](https://github.com/netbox-community/netbox/issues/17278) - Ensure hierarchy is recalculated when bulk editing recursively nested object types (e.g. tenant groups)
* [#17279](https://github.com/netbox-community/netbox/issues/17279) - Do not regenerate key when updating a token via REST API
* [#17286](https://github.com/netbox-community/netbox/issues/17286) - Fix exception when adding member device to virtual chassis via web UI
---
## v4.0.9 (2024-08-14)
### Enhancements
* [#16692](https://github.com/netbox-community/netbox/issues/16692) - Enable modifying VLAN assignment while bulk editing prefixes
* [#17006](https://github.com/netbox-community/netbox/issues/17006) - Add IEEE 802.11be interface type
### Bug Fixes
* [#13459](https://github.com/netbox-community/netbox/issues/13459) - Correct OpenAPI schema type for `TreeNodeMultipleChoiceFilter`
* [#16073](https://github.com/netbox-community/netbox/issues/16073) - Respect default values for custom fields during bulk import of objects
* [#16176](https://github.com/netbox-community/netbox/issues/16176) - Restore ability to select multiple terminating devices when connecting a cable
* [#16871](https://github.com/netbox-community/netbox/issues/16871) - Sanitize device ID query parameter when bulk editing components to prevent exception
* [#17038](https://github.com/netbox-community/netbox/issues/17038) - Fix AttributeError exception when attempting to export system status data
* [#17064](https://github.com/netbox-community/netbox/issues/17064) - Fix misaligned text within rendered Markdown code blocks
* [#17124](https://github.com/netbox-community/netbox/issues/17124) - `BaseTable` should follow reverse one-to-one relationships when prefetching related objects
* [#17131](https://github.com/netbox-community/netbox/issues/17131) - Fix exception when creating object-type custom field without selecting related object type
* [#17144](https://github.com/netbox-community/netbox/issues/17144) - Avoid showing duplicated pop-up messages
---
## v4.0.8 (2024-07-26)
### Enhancements

View File

@@ -0,0 +1,126 @@
# NetBox v4.1
## v4.1.0 (2024-09-03)
### Breaking Changes
* Several filters deprecated in v4.0 have been removed (see [#15410](https://github.com/netbox-community/netbox/issues/15410)).
* The unit size for `VirtualMachine.disk` and `VirtualDisk.size` has been changed from 1 gigabyte to 1 megabyte. Existing values will be adjusted automatically during the upgrade process.
* The `min_vid` and `max_vid` fields on the VLAN group model have been replaced with `vid_ranges`, an array of starting and ending VLAN ID pairs.
* The five individual event type fields on the EventRule model have been replaced by a single `event_types` array field, which lists applicable event types by name.
* All UI views & API endpoints associated with change records have been moved from `/extras` to `/core`.
* The `validate()` method on CustomValidator subclasses now **must** accept the request argument (deprecated in v4.0 by [#14279](https://github.com/netbox-community/netbox/issues/14279/)).
### New Features
#### Circuit Groups ([#7025](https://github.com/netbox-community/netbox/issues/7025))
Circuits can now be assigned to groups for administrative purposes. Each circuit may be assigned to multiple groups, and each assignment may optionally indicate a priority (primary, secondary, or tertiary).
#### VLAN Group ID Ranges ([#9627](https://github.com/netbox-community/netbox/issues/9627))
The VLAN group model has been enhanced to support multiple VLAN ID (VID) ranges, whereas previously it could track only a single beginning and ending VID pair. VID ranges are stored as an array of beginning and ending (inclusive) integer pairs, e.g. `1-100,1000-1999`.
#### Nested Device Modules ([#10500](https://github.com/netbox-community/netbox/issues/10500))
Module bays can now be added to modules to effect a hierarchical arrangement of submodules within a device. A module installed within a device's module bay may itself have module bays into which child modules may be installed.
#### Rack Types ([#12826](https://github.com/netbox-community/netbox/issues/12826))
A new rack type model has been introduced, which functions similarly to device types. Users can now define a common make and model of equipment rack, the attributes of which are automatically populated when creating a new rack of that type. Backward compatibility for racks with individually defined characteristics is fully retained.
#### Plugins Catalog Integration ([#14731](https://github.com/netbox-community/netbox/issues/14731))
The NetBox UI now integrates directly with the canonical [plugins catalog](https://netboxlabs.com/netbox-plugins/) hosted by [NetBox Labs](https://netboxlabs.com). Users can now explore available plugins and check for newer releases natively within the NetBox user interface.
#### User Notifications ([#15621](https://github.com/netbox-community/netbox/issues/15621))
NetBox now includes a user notification system. Users can subscribe to individual objects and be alerted to changes within the web interface. Additionally, event rules can be created to trigger notifications for specific users and/or groups. Plugins can also employ this notification system for their own purposes.
### Enhancements
* [#7537](https://github.com/netbox-community/netbox/issues/7537) - Add a serial number field for virtual machines
* [#8198](https://github.com/netbox-community/netbox/issues/8198) - Enable uniqueness enforcement for custom field values
* [#8984](https://github.com/netbox-community/netbox/issues/8984) - Enable filtering of custom script output by log level
* [#11969](https://github.com/netbox-community/netbox/issues/11969) - Support for tracking airflow on racks and module types
* [#14656](https://github.com/netbox-community/netbox/issues/14656) - Dynamically render the custom field edit form depending on the selected field type
* [#15106](https://github.com/netbox-community/netbox/issues/15106) - Add `distance` and `distance_unit` fields for wireless links
* [#15156](https://github.com/netbox-community/netbox/issues/15156) - Add `display_url` field to all REST API serializers, which links to the corresponding UI view for an object
* [#16574](https://github.com/netbox-community/netbox/issues/16574) - Add `last_synced` time to REST API serializer for data sources
* [#16580](https://github.com/netbox-community/netbox/issues/16580) - Enable plugin views to enforce `LOGIN_REQUIRED` selectively (remove `AUTH_EXEMPT_PATHS`)
* [#16782](https://github.com/netbox-community/netbox/issues/16782) - Enable filtering of selection choices for object and multi-object custom fields
* [#16907](https://github.com/netbox-community/netbox/issues/16907) - Update user interface styling
* [#17051](https://github.com/netbox-community/netbox/issues/17051) - Introduce `ISOLATED_DEPLOYMENT` config parameter for denoting Internet isolation
* [#17221](https://github.com/netbox-community/netbox/issues/17221) - `ObjectEditView` now supports HTMX-based object editing
* [#17288](https://github.com/netbox-community/netbox/issues/17288) - Introduce a configurable limit on the number of aliases within a GraphQL API request
* [#17289](https://github.com/netbox-community/netbox/issues/17289) - Enforce a standard policy for local passwords by default
* [#17318](https://github.com/netbox-community/netbox/issues/17318) - Include the assigned provider in nested API representation of circuits
### Bug Fixes (From Beta1)
* [#17086](https://github.com/netbox-community/netbox/issues/17086) - Fix exception when viewing a job with no related object
* [#17097](https://github.com/netbox-community/netbox/issues/17097) - Record static object representation when calling `NotificationGroup.notify()`
* [#17098](https://github.com/netbox-community/netbox/issues/17098) - Prevent automatic deletion of related notifications when deleting an object
* [#17159](https://github.com/netbox-community/netbox/issues/17159) - Correct file paths in plugin installation instructions
* [#17163](https://github.com/netbox-community/netbox/issues/17163) - Fix filtering of related services under IP address view
* [#17169](https://github.com/netbox-community/netbox/issues/17169) - Avoid duplicating catalog listings for installed plugins
* [#17301](https://github.com/netbox-community/netbox/issues/17301) - Correct styling of the edit & delete buttons for custom script modules
* [#17302](https://github.com/netbox-community/netbox/issues/17302) - Fix log level filtering support for custom script messages
* [#17306](https://github.com/netbox-community/netbox/issues/17306) - Correct rounding of reported VLAN group utilization
### Plugins
* [#15692](https://github.com/netbox-community/netbox/issues/15692) - Introduce improved plugin support for background jobs
* [#16359](https://github.com/netbox-community/netbox/issues/16359) - Enable plugins to embed content in the top navigation bar
* [#16726](https://github.com/netbox-community/netbox/issues/16726) - Extend `PluginTemplateExtension` to enable registering multiple models
* [#16776](https://github.com/netbox-community/netbox/issues/16776) - Add an `alerts()` method to `PluginTemplateExtension` for embedding important information on object views
* [#16886](https://github.com/netbox-community/netbox/issues/16886) - Introduce a mechanism for plugins to register custom event types (for use with user notifications)
### Other Changes
* [#14692](https://github.com/netbox-community/netbox/issues/14692) - Change the atomic unit for virtual disks from 1GB to 1MB
* [#14861](https://github.com/netbox-community/netbox/issues/14861) - The URL path for UI views concerning virtual disks has been standardized to `/virtualization/virtual-disks/`
* [#15410](https://github.com/netbox-community/netbox/issues/15410) - Remove various deprecated query filters
* [#15908](https://github.com/netbox-community/netbox/issues/15908) - Indicate product edition in release data
* [#16388](https://github.com/netbox-community/netbox/issues/16388) - Move all change logging resources from `extras` to `core`
* [#16884](https://github.com/netbox-community/netbox/issues/16884) - Remove the ID column from the default table configuration for changelog records
* [#16988](https://github.com/netbox-community/netbox/issues/16988) - Relocate rack items in navigation menu
* [#17143](https://github.com/netbox-community/netbox/issues/17143) - The use of legacy "nested" serializer classes has been deprecated
### REST API Changes
* The `/api/extras/object-changes/` endpoint has moved to `/api/core/object-changes/`.
* Most object representations now include a read-only `display_url` field, which links to the object's corresponding UI view.
* Added the following endpoints:
* `/api/circuits/circuit-groups/`
* `/api/circuits/circuit-group-assignments/`
* `/api/dcim/rack-types/`
* `/api/extras/notification-groups/`
* `/api/extras/notifications/`
* `/api/extras/subscriptions/`
* circuits.Circuit
* Added the `assignments` field, which lists all group assignments
* core.DataSource
* Added the read-only `last_synced` field
* dcim.ModuleBay
* Added the optional `module` foreign key field
* dcim.ModuleBayTemplate
* Added the optional `module_type` foreign key field
* dcim.ModuleType
* Added the optional `airflow` choice field
* dcim.Rack
* Added the optional `rack_type` foreign key field
* Added the optional `airflow` choice field
* extras.CustomField
* Added the `related_object_filter` JSON field for object and multi-object custom fields
* Added the `validation_unique` boolean field
* extras.EventRule
* Removed the `type_create`, `type_update`, `type_delete`, `type_job_start`, and `type_job_end` boolean fields
* Added the `event_types` array field
* ipam.VLANGroup
* Removed the `min_vid` and `max_vid` fields
* Added the `vid_ranges` field, an array of starting & ending VLAN IDs
* virtualization.VirtualMachine
* Added the optional `serial` field
* wireless.WirelessLink
* Added the optional `distance` and `distance_unit` fields

View File

@@ -86,6 +86,7 @@ nav:
- Change Logging: 'features/change-logging.md'
- Journaling: 'features/journaling.md'
- Event Rules: 'features/event-rules.md'
- Notifications: 'features/notifications.md'
- Background Jobs: 'features/background-jobs.md'
- Auth & Permissions: 'features/authentication-permissions.md'
- API & Integration: 'features/api-integration.md'
@@ -108,6 +109,7 @@ nav:
- Required Parameters: 'configuration/required-parameters.md'
- System: 'configuration/system.md'
- Security: 'configuration/security.md'
- GraphQL API: 'configuration/graphql-api.md'
- Remote Authentication: 'configuration/remote-authentication.md'
- Data & Validation: 'configuration/data-validation.md'
- Default Values: 'configuration/default-values.md'
@@ -142,10 +144,11 @@ nav:
- Forms: 'plugins/development/forms.md'
- Filters & Filter Sets: 'plugins/development/filtersets.md'
- Search: 'plugins/development/search.md'
- Event Types: 'plugins/development/event-types.md'
- Data Backends: 'plugins/development/data-backends.md'
- REST API: 'plugins/development/rest-api.md'
- GraphQL API: 'plugins/development/graphql-api.md'
- Background Tasks: 'plugins/development/background-tasks.md'
- Background Jobs: 'plugins/development/background-jobs.md'
- Dashboard Widgets: 'plugins/development/dashboard-widgets.md'
- Staged Changes: 'plugins/development/staged-changes.md'
- Exceptions: 'plugins/development/exceptions.md'
@@ -163,6 +166,8 @@ nav:
- Data Model:
- Circuits:
- Circuit: 'models/circuits/circuit.md'
- CircuitGroup: 'models/circuits/circuitgroup.md'
- CircuitGroupAssignment: 'models/circuits/circuitgroupassignment.md'
- Circuit Termination: 'models/circuits/circuittermination.md'
- Circuit Type: 'models/circuits/circuittype.md'
- Provider: 'models/circuits/provider.md'
@@ -206,6 +211,7 @@ nav:
- Rack: 'models/dcim/rack.md'
- RackReservation: 'models/dcim/rackreservation.md'
- RackRole: 'models/dcim/rackrole.md'
- RackType: 'models/dcim/racktype.md'
- RearPort: 'models/dcim/rearport.md'
- RearPortTemplate: 'models/dcim/rearporttemplate.md'
- Region: 'models/dcim/region.md'
@@ -225,8 +231,11 @@ nav:
- ExportTemplate: 'models/extras/exporttemplate.md'
- ImageAttachment: 'models/extras/imageattachment.md'
- JournalEntry: 'models/extras/journalentry.md'
- Notification: 'models/extras/notification.md'
- NotificationGroup: 'models/extras/notificationgroup.md'
- SavedFilter: 'models/extras/savedfilter.md'
- StagedChange: 'models/extras/stagedchange.md'
- Subscription: 'models/extras/subscription.md'
- Tag: 'models/extras/tag.md'
- Webhook: 'models/extras/webhook.md'
- IPAM:
@@ -296,6 +305,7 @@ nav:
- git Cheat Sheet: 'development/git-cheat-sheet.md'
- Release Notes:
- Summary: 'release-notes/index.md'
- Version 4.1: 'release-notes/version-4.1.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'

View File

@@ -9,6 +9,8 @@ urlpatterns = [
# Account views
path('profile/', views.ProfileView.as_view(), name='profile'),
path('bookmarks/', views.BookmarkListView.as_view(), name='bookmarks'),
path('notifications/', views.NotificationListView.as_view(), name='notifications'),
path('subscriptions/', views.SubscriptionListView.as_view(), name='subscriptions'),
path('preferences/', views.UserConfigView.as_view(), name='preferences'),
path('password/', views.ChangePasswordView.as_view(), name='change_password'),
path('api-tokens/', views.UserTokenListView.as_view(), name='usertoken_list'),

View File

@@ -19,8 +19,10 @@ from django.views.generic import View
from social_core.backends.utils import load_backends
from account.models import UserToken
from extras.models import Bookmark, ObjectChange
from extras.tables import BookmarkTable, ObjectChangeTable
from core.models import ObjectChange
from core.tables import ObjectChangeTable
from extras.models import Bookmark
from extras.tables import BookmarkTable, NotificationTable, SubscriptionTable
from netbox.authentication import get_auth_backend_display, get_saml_idps
from netbox.config import get_config
from netbox.views import generic
@@ -109,7 +111,7 @@ class LoginView(View):
# Authenticate user
auth_login(request, form.get_user())
logger.info(f"User {request.user} successfully authenticated")
messages.success(request, f"Logged in as {request.user}.")
messages.success(request, _("Logged in as {user}.").format(user=request.user))
# Ensure the user has a UserConfig defined. (This should normally be handled by
# create_userconfig() on user creation.)
@@ -159,7 +161,7 @@ class LogoutView(View):
username = request.user
auth_logout(request)
logger.info(f"User {username} has logged out")
messages.info(request, "You have logged out.")
messages.info(request, _("You have logged out."))
# Delete session key & language cookies (if set) upon logout
response = HttpResponseRedirect(resolve_url(settings.LOGOUT_REDIRECT_URL))
@@ -234,7 +236,7 @@ class ChangePasswordView(LoginRequiredMixin, View):
def get(self, request):
# LDAP users cannot change their password here
if getattr(request.user, 'ldap_username', None):
messages.warning(request, "LDAP-authenticated user credentials cannot be changed within NetBox.")
messages.warning(request, _("LDAP-authenticated user credentials cannot be changed within NetBox."))
return redirect('account:profile')
form = PasswordChangeForm(user=request.user)
@@ -249,7 +251,7 @@ class ChangePasswordView(LoginRequiredMixin, View):
if form.is_valid():
form.save()
update_session_auth_hash(request, form.user)
messages.success(request, "Your password has been changed successfully.")
messages.success(request, _("Your password has been changed successfully."))
return redirect('account:profile')
return render(request, self.template_name, {
@@ -275,6 +277,36 @@ class BookmarkListView(LoginRequiredMixin, generic.ObjectListView):
}
#
# Notifications & subscriptions
#
class NotificationListView(LoginRequiredMixin, generic.ObjectListView):
table = NotificationTable
template_name = 'account/notifications.html'
def get_queryset(self, request):
return request.user.notifications.all()
def get_extra_context(self, request):
return {
'active_tab': 'notifications',
}
class SubscriptionListView(LoginRequiredMixin, generic.ObjectListView):
table = SubscriptionTable
template_name = 'account/subscriptions.html'
def get_queryset(self, request):
return request.user.subscriptions.all()
def get_extra_context(self, request):
return {
'active_tab': 'subscriptions',
}
#
# User views for token management
#

View File

@@ -1,9 +1,11 @@
import warnings
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
from .serializers_.nested import NestedProviderAccountSerializer
__all__ = [
'NestedCircuitSerializer',
@@ -14,17 +16,22 @@ __all__ = [
'NestedProviderAccountSerializer',
]
# TODO: Remove in v4.2
warnings.warn(
f"Dedicated nested serializers will be removed in NetBox v4.2. Use Serializer(nested=True) instead.",
DeprecationWarning
)
#
# Provider networks
#
class NestedProviderNetworkSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:providernetwork-detail')
class Meta:
model = ProviderNetwork
fields = ['id', 'url', 'display', 'name']
fields = ['id', 'url', 'display_url', 'display', 'name']
#
@@ -35,24 +42,11 @@ class NestedProviderNetworkSerializer(WritableNestedSerializer):
exclude_fields=('circuit_count',),
)
class NestedProviderSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:provider-detail')
circuit_count = RelatedObjectCountField('circuits')
class Meta:
model = Provider
fields = ['id', 'url', 'display', 'name', 'slug', 'circuit_count']
#
# Provider Accounts
#
class NestedProviderAccountSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:provideraccount-detail')
class Meta:
model = ProviderAccount
fields = ['id', 'url', 'display', 'name', 'account']
fields = ['id', 'url', 'display_url', 'display', 'name', 'slug', 'circuit_count']
#
@@ -63,26 +57,23 @@ class NestedProviderAccountSerializer(WritableNestedSerializer):
exclude_fields=('circuit_count',),
)
class NestedCircuitTypeSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittype-detail')
circuit_count = RelatedObjectCountField('circuits')
class Meta:
model = CircuitType
fields = ['id', 'url', 'display', 'name', 'slug', 'circuit_count']
fields = ['id', 'url', 'display_url', 'display', 'name', 'slug', 'circuit_count']
class NestedCircuitSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuit-detail')
class Meta:
model = Circuit
fields = ['id', 'url', 'display', 'cid']
fields = ['id', 'url', 'display_url', 'display', 'cid']
class NestedCircuitTerminationSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittermination-detail')
circuit = NestedCircuitSerializer()
class Meta:
model = CircuitTermination
fields = ['id', 'url', 'display', 'circuit', 'term_side', 'cable', '_occupied']
fields = ['id', 'url', 'display_url', 'display', 'circuit', 'term_side', 'cable', '_occupied']

View File

@@ -1,3 +1,2 @@
from .serializers_.providers import *
from .serializers_.circuits import *
from .nested_serializers import *

View File

@@ -1,7 +1,5 @@
from rest_framework import serializers
from circuits.choices import CircuitStatusChoices
from circuits.models import Circuit, CircuitTermination, CircuitType
from circuits.choices import CircuitPriorityChoices, CircuitStatusChoices
from circuits.models import Circuit, CircuitGroup, CircuitGroupAssignment, CircuitTermination, CircuitType
from dcim.api.serializers_.cables import CabledObjectSerializer
from dcim.api.serializers_.sites import SiteSerializer
from netbox.api.fields import ChoiceField, RelatedObjectCountField
@@ -12,13 +10,14 @@ from .providers import ProviderAccountSerializer, ProviderNetworkSerializer, Pro
__all__ = (
'CircuitSerializer',
'CircuitGroupAssignmentSerializer',
'CircuitGroupSerializer',
'CircuitTerminationSerializer',
'CircuitTypeSerializer',
)
class CircuitTypeSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittype-detail')
# Related object counts
circuit_count = RelatedObjectCountField('circuits')
@@ -26,27 +25,53 @@ class CircuitTypeSerializer(NetBoxModelSerializer):
class Meta:
model = CircuitType
fields = [
'id', 'url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields', 'created',
'last_updated', 'circuit_count',
'id', 'url', 'display_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',
'id', 'url', 'display_url', 'display', 'site', 'provider_network', 'port_speed', 'upstream_speed',
'xconnect_id', 'description',
]
class CircuitGroupSerializer(NetBoxModelSerializer):
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
circuit_count = RelatedObjectCountField('assignments')
class Meta:
model = CircuitGroup
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'description', 'tenant',
'tags', 'custom_fields', 'created', 'last_updated', 'circuit_count'
]
brief_fields = ('id', 'url', 'display', 'name')
class CircuitGroupAssignmentSerializer_(NetBoxModelSerializer):
"""
Base serializer for group assignments under CircuitSerializer.
"""
group = CircuitGroupSerializer(nested=True)
priority = ChoiceField(choices=CircuitPriorityChoices, allow_blank=True, required=False)
class Meta:
model = CircuitGroupAssignment
fields = [
'id', 'url', 'display_url', 'display', 'group', 'priority', 'tags', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'group', 'priority')
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)
@@ -54,19 +79,19 @@ class CircuitSerializer(NetBoxModelSerializer):
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)
assignments = CircuitGroupAssignmentSerializer_(nested=True, many=True, required=False)
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',
'id', 'url', 'display_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', 'assignments',
]
brief_fields = ('id', 'url', 'display', 'cid', 'description')
brief_fields = ('id', 'url', 'display', 'provider', '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)
@@ -74,8 +99,19 @@ class CircuitTerminationSerializer(NetBoxModelSerializer, CabledObjectSerializer
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',
'id', 'url', 'display_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')
class CircuitGroupAssignmentSerializer(CircuitGroupAssignmentSerializer_):
circuit = CircuitSerializer(nested=True)
class Meta:
model = CircuitGroupAssignment
fields = [
'id', 'url', 'display_url', 'display', 'group', 'circuit', 'priority', 'tags', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'group', 'circuit', 'priority')

View File

@@ -0,0 +1,13 @@
from circuits.models import ProviderAccount
from netbox.api.serializers import WritableNestedSerializer
__all__ = (
'NestedProviderAccountSerializer',
)
class NestedProviderAccountSerializer(WritableNestedSerializer):
class Meta:
model = ProviderAccount
fields = ['id', 'url', 'display_url', 'display', 'name', 'account']

View File

@@ -5,7 +5,7 @@ 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 *
from .nested import NestedProviderAccountSerializer
__all__ = (
'ProviderAccountSerializer',
@@ -15,7 +15,6 @@ __all__ = (
class ProviderSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:provider-detail')
accounts = SerializedPKRelatedField(
queryset=ProviderAccount.objects.all(),
serializer=NestedProviderAccountSerializer,
@@ -36,34 +35,32 @@ class ProviderSerializer(NetBoxModelSerializer):
class Meta:
model = Provider
fields = [
'id', 'url', 'display', 'name', 'slug', 'accounts', 'description', 'comments', 'asns', 'tags',
'custom_fields', 'created', 'last_updated', 'circuit_count',
'id', 'url', 'display_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',
'id', 'url', 'display_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',
'id', 'url', 'display_url', 'display', 'provider', 'name', 'service_id', 'description', 'comments', 'tags',
'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'name', 'description')

View File

@@ -14,6 +14,8 @@ router.register('provider-networks', views.ProviderNetworkViewSet)
router.register('circuit-types', views.CircuitTypeViewSet)
router.register('circuits', views.CircuitViewSet)
router.register('circuit-terminations', views.CircuitTerminationViewSet)
router.register('circuit-groups', views.CircuitGroupViewSet)
router.register('circuit-group-assignments', views.CircuitGroupAssignmentViewSet)
app_name = 'circuits-api'
urlpatterns = router.urls

View File

@@ -55,6 +55,26 @@ class CircuitTerminationViewSet(PassThroughPortMixin, NetBoxModelViewSet):
filterset_class = filtersets.CircuitTerminationFilterSet
#
# Circuit Groups
#
class CircuitGroupViewSet(NetBoxModelViewSet):
queryset = CircuitGroup.objects.all()
serializer_class = serializers.CircuitGroupSerializer
filterset_class = filtersets.CircuitGroupFilterSet
#
# Circuit Group Assignments
#
class CircuitGroupAssignmentViewSet(NetBoxModelViewSet):
queryset = CircuitGroupAssignment.objects.all()
serializer_class = serializers.CircuitGroupAssignmentSerializer
filterset_class = filtersets.CircuitGroupAssignmentFilterSet
#
# Provider accounts
#

View File

@@ -76,3 +76,19 @@ class CircuitTerminationPortSpeedChoices(ChoiceSet):
(1544, 'T1 (1.544 Mbps)'),
(2048, 'E1 (2.048 Mbps)'),
]
class CircuitPriorityChoices(ChoiceSet):
key = 'CircuitGroupAssignment.priority'
PRIORITY_PRIMARY = 'primary'
PRIORITY_SECONDARY = 'secondary'
PRIORITY_TERTIARY = 'tertiary'
PRIORITY_INACTIVE = 'inactive'
CHOICES = [
(PRIORITY_PRIMARY, _('Primary')),
(PRIORITY_SECONDARY, _('Secondary')),
(PRIORITY_TERTIARY, _('Tertiary')),
(PRIORITY_INACTIVE, _('Inactive')),
]

View File

@@ -13,6 +13,8 @@ from .models import *
__all__ = (
'CircuitFilterSet',
'CircuitGroupAssignmentFilterSet',
'CircuitGroupFilterSet',
'CircuitTerminationFilterSet',
'CircuitTypeFilterSet',
'ProviderNetworkFilterSet',
@@ -303,3 +305,60 @@ class CircuitTerminationFilterSet(NetBoxModelFilterSet, CabledObjectFilterSet):
Q(pp_info__icontains=value) |
Q(description__icontains=value)
).distinct()
class CircuitGroupFilterSet(OrganizationalModelFilterSet, TenancyFilterSet):
class Meta:
model = CircuitGroup
fields = ('id', 'name', 'slug', 'description')
class CircuitGroupAssignmentFilterSet(NetBoxModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
)
provider_id = django_filters.ModelMultipleChoiceFilter(
field_name='circuit__provider',
queryset=Provider.objects.all(),
label=_('Provider (ID)'),
)
provider = django_filters.ModelMultipleChoiceFilter(
field_name='circuit__provider__slug',
queryset=Provider.objects.all(),
to_field_name='slug',
label=_('Provider (slug)'),
)
circuit_id = django_filters.ModelMultipleChoiceFilter(
queryset=Circuit.objects.all(),
label=_('Circuit (ID)'),
)
circuit = django_filters.ModelMultipleChoiceFilter(
field_name='circuit__cid',
queryset=Circuit.objects.all(),
to_field_name='cid',
label=_('Circuit (CID)'),
)
group_id = django_filters.ModelMultipleChoiceFilter(
queryset=CircuitGroup.objects.all(),
label=_('Circuit group (ID)'),
)
group = django_filters.ModelMultipleChoiceFilter(
field_name='group__slug',
queryset=CircuitGroup.objects.all(),
to_field_name='slug',
label=_('Circuit group (slug)'),
)
class Meta:
model = CircuitGroupAssignment
fields = ('id', 'priority')
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(circuit__cid__icontains=value) |
Q(group__name__icontains=value)
)

View File

@@ -1,7 +1,7 @@
from django import forms
from django.utils.translation import gettext_lazy as _
from circuits.choices import CircuitCommitRateChoices, CircuitStatusChoices
from circuits.choices import CircuitCommitRateChoices, CircuitPriorityChoices, CircuitStatusChoices
from circuits.models import *
from dcim.models import Site
from ipam.models import ASN
@@ -14,6 +14,8 @@ from utilities.forms.widgets import BulkEditNullBooleanSelect, DatePicker, Numbe
__all__ = (
'CircuitBulkEditForm',
'CircuitGroupAssignmentBulkEditForm',
'CircuitGroupBulkEditForm',
'CircuitTerminationBulkEditForm',
'CircuitTypeBulkEditForm',
'ProviderBulkEditForm',
@@ -219,3 +221,40 @@ class CircuitTerminationBulkEditForm(NetBoxModelBulkEditForm):
FieldSet('port_speed', 'upstream_speed', name=_('Termination Details')),
)
nullable_fields = ('description')
class CircuitGroupBulkEditForm(NetBoxModelBulkEditForm):
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)
tenant = DynamicModelChoiceField(
label=_('Tenant'),
queryset=Tenant.objects.all(),
required=False
)
model = CircuitGroup
nullable_fields = (
'description', 'tenant',
)
class CircuitGroupAssignmentBulkEditForm(NetBoxModelBulkEditForm):
circuit = DynamicModelChoiceField(
label=_('Circuit'),
queryset=Circuit.objects.all(),
required=False
)
priority = forms.ChoiceField(
label=_('Priority'),
choices=add_blank_choice(CircuitPriorityChoices),
required=False
)
model = CircuitGroupAssignment
fieldsets = (
FieldSet('circuit', 'priority'),
)
nullable_fields = ('priority',)

View File

@@ -11,6 +11,8 @@ from utilities.forms.fields import CSVChoiceField, CSVModelChoiceField, SlugFiel
__all__ = (
'CircuitImportForm',
'CircuitGroupAssignmentImportForm',
'CircuitGroupImportForm',
'CircuitTerminationImportForm',
'CircuitTerminationImportRelatedForm',
'CircuitTypeImportForm',
@@ -150,3 +152,24 @@ class CircuitTerminationImportForm(NetBoxModelImportForm, BaseCircuitTermination
'circuit', 'term_side', 'site', 'provider_network', 'port_speed', 'upstream_speed', 'xconnect_id',
'pp_info', 'description', 'tags'
]
class CircuitGroupImportForm(NetBoxModelImportForm):
tenant = CSVModelChoiceField(
label=_('Tenant'),
queryset=Tenant.objects.all(),
required=False,
to_field_name='name',
help_text=_('Assigned tenant')
)
class Meta:
model = CircuitGroup
fields = ('name', 'slug', 'description', 'tenant', 'tags')
class CircuitGroupAssignmentImportForm(NetBoxModelImportForm):
class Meta:
model = CircuitGroupAssignment
fields = ('circuit', 'group', 'priority')

View File

@@ -1,7 +1,7 @@
from django import forms
from django.utils.translation import gettext as _
from circuits.choices import CircuitCommitRateChoices, CircuitStatusChoices, CircuitTerminationSideChoices
from circuits.choices import CircuitCommitRateChoices, CircuitPriorityChoices, CircuitStatusChoices, CircuitTerminationSideChoices
from circuits.models import *
from dcim.models import Region, Site, SiteGroup
from ipam.models import ASN
@@ -13,6 +13,8 @@ from utilities.forms.widgets import DatePicker, NumberWithOptions
__all__ = (
'CircuitFilterForm',
'CircuitGroupAssignmentFilterForm',
'CircuitGroupFilterForm',
'CircuitTerminationFilterForm',
'CircuitTypeFilterForm',
'ProviderFilterForm',
@@ -230,3 +232,41 @@ class CircuitTerminationFilterForm(NetBoxModelFilterSetForm):
label=_('Provider')
)
tag = TagFilterField(model)
class CircuitGroupFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
model = CircuitGroup
fieldsets = (
FieldSet('q', 'filter_id', 'tag'),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
)
tag = TagFilterField(model)
class CircuitGroupAssignmentFilterForm(NetBoxModelFilterSetForm):
model = CircuitGroupAssignment
fieldsets = (
FieldSet('q', 'filter_id', 'tag'),
FieldSet('provider_id', 'circuit_id', 'group_id', 'priority', name=_('Assignment')),
)
provider_id = DynamicModelMultipleChoiceField(
queryset=Provider.objects.all(),
required=False,
label=_('Provider')
)
circuit_id = DynamicModelMultipleChoiceField(
queryset=Circuit.objects.all(),
required=False,
label=_('Circuit')
)
group_id = DynamicModelMultipleChoiceField(
queryset=CircuitGroup.objects.all(),
required=False,
label=_('Group')
)
priority = forms.MultipleChoiceField(
label=_('Priority'),
choices=CircuitPriorityChoices,
required=False
)
tag = TagFilterField(model)

View File

@@ -12,6 +12,8 @@ from utilities.forms.widgets import DatePicker, NumberWithOptions
__all__ = (
'CircuitForm',
'CircuitGroupAssignmentForm',
'CircuitGroupForm',
'CircuitTerminationForm',
'CircuitTypeForm',
'ProviderForm',
@@ -171,3 +173,36 @@ class CircuitTerminationForm(NetBoxModelForm):
options=CircuitTerminationPortSpeedChoices
),
}
class CircuitGroupForm(TenancyForm, NetBoxModelForm):
slug = SlugField()
fieldsets = (
FieldSet('name', 'slug', 'description', 'tags', name=_('Circuit Group')),
FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
)
class Meta:
model = CircuitGroup
fields = [
'name', 'slug', 'description', 'tenant_group', 'tenant', 'tags',
]
class CircuitGroupAssignmentForm(NetBoxModelForm):
group = DynamicModelChoiceField(
label=_('Group'),
queryset=CircuitGroup.objects.all(),
)
circuit = DynamicModelChoiceField(
label=_('Circuit'),
queryset=Circuit.objects.all(),
selector=True
)
class Meta:
model = CircuitGroupAssignment
fields = [
'group', 'circuit', 'priority', 'tags',
]

View File

@@ -7,6 +7,8 @@ from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin
__all__ = (
'CircuitTerminationFilter',
'CircuitFilter',
'CircuitGroupAssignmentFilter',
'CircuitGroupFilter',
'CircuitTypeFilter',
'ProviderFilter',
'ProviderAccountFilter',
@@ -32,6 +34,18 @@ class CircuitTypeFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.CircuitGroup, lookups=True)
@autotype_decorator(filtersets.CircuitGroupFilterSet)
class CircuitGroupFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.CircuitGroupAssignment, lookups=True)
@autotype_decorator(filtersets.CircuitGroupAssignmentFilterSet)
class CircuitGroupAssignmentFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.Provider, lookups=True)
@autotype_decorator(filtersets.ProviderFilterSet)
class ProviderFilter(BaseFilterMixin):

View File

@@ -3,38 +3,31 @@ from typing import List
import strawberry
import strawberry_django
from circuits import models
from .types import *
@strawberry.type
@strawberry.type(name="Query")
class CircuitsQuery:
@strawberry.field
def circuit(self, id: int) -> CircuitType:
return models.Circuit.objects.get(pk=id)
circuit: CircuitType = strawberry_django.field()
circuit_list: List[CircuitType] = strawberry_django.field()
@strawberry.field
def circuit_termination(self, id: int) -> CircuitTerminationType:
return models.CircuitTermination.objects.get(pk=id)
circuit_termination: CircuitTerminationType = strawberry_django.field()
circuit_termination_list: List[CircuitTerminationType] = strawberry_django.field()
@strawberry.field
def circuit_type(self, id: int) -> CircuitTypeType:
return models.CircuitType.objects.get(pk=id)
circuit_type: CircuitTypeType = strawberry_django.field()
circuit_type_list: List[CircuitTypeType] = strawberry_django.field()
@strawberry.field
def provider(self, id: int) -> ProviderType:
return models.Provider.objects.get(pk=id)
circuit_group: CircuitGroupType = strawberry_django.field()
circuit_group_list: List[CircuitGroupType] = strawberry_django.field()
circuit_group_assignment: CircuitGroupAssignmentType = strawberry_django.field()
circuit_group_assignment_list: List[CircuitGroupAssignmentType] = strawberry_django.field()
provider: ProviderType = strawberry_django.field()
provider_list: List[ProviderType] = strawberry_django.field()
@strawberry.field
def provider_account(self, id: int) -> ProviderAccountType:
return models.ProviderAccount.objects.get(pk=id)
provider_account: ProviderAccountType = strawberry_django.field()
provider_account_list: List[ProviderAccountType] = strawberry_django.field()
@strawberry.field
def provider_network(self, id: int) -> ProviderNetworkType:
return models.ProviderNetwork.objects.get(pk=id)
provider_network: ProviderNetworkType = strawberry_django.field()
provider_network_list: List[ProviderNetworkType] = strawberry_django.field()

View File

@@ -6,13 +6,15 @@ import strawberry_django
from circuits import models
from dcim.graphql.mixins import CabledObjectMixin
from extras.graphql.mixins import ContactsMixin, CustomFieldsMixin, TagsMixin
from netbox.graphql.types import NetBoxObjectType, ObjectType, OrganizationalObjectType
from netbox.graphql.types import BaseObjectType, NetBoxObjectType, ObjectType, OrganizationalObjectType
from tenancy.graphql.types import TenantType
from .filters import *
__all__ = (
'CircuitTerminationType',
'CircuitType',
'CircuitGroupAssignmentType',
'CircuitGroupType',
'CircuitTypeType',
'ProviderType',
'ProviderAccountType',
@@ -91,3 +93,22 @@ class CircuitType(NetBoxObjectType, ContactsMixin):
tenant: TenantType | None
terminations: List[CircuitTerminationType]
@strawberry_django.type(
models.CircuitGroup,
fields='__all__',
filters=CircuitGroupFilter
)
class CircuitGroupType(OrganizationalObjectType):
tenant: TenantType | None
@strawberry_django.type(
models.CircuitGroupAssignment,
fields='__all__',
filters=CircuitGroupAssignmentFilter
)
class CircuitGroupAssignmentType(TagsMixin, BaseObjectType):
group: Annotated["CircuitGroupType", strawberry.lazy('circuits.graphql.types')]
circuit: Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]

View File

@@ -0,0 +1,88 @@
import django.db.models.deletion
import taggit.managers
import utilities.json
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('circuits', '0043_circuittype_color'),
('extras', '0119_notifications'),
('tenancy', '0015_contactassignment_rename_content_type'),
]
operations = [
migrations.CreateModel(
name='CircuitGroup',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
('created', models.DateTimeField(auto_now_add=True, null=True)),
('last_updated', models.DateTimeField(auto_now=True, null=True)),
(
'custom_field_data',
models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder),
),
('name', models.CharField(max_length=100, unique=True)),
('slug', models.SlugField(max_length=100, unique=True)),
('description', models.CharField(blank=True, max_length=200)),
('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
(
'tenant',
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name='circuit_groups',
to='tenancy.tenant',
),
),
],
options={
'verbose_name': 'Circuit group',
'verbose_name_plural': 'Circuit group',
'ordering': ('name',),
},
),
migrations.CreateModel(
name='CircuitGroupAssignment',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
('created', models.DateTimeField(auto_now_add=True, null=True)),
('last_updated', models.DateTimeField(auto_now=True, null=True)),
(
'custom_field_data',
models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder),
),
('priority', models.CharField(blank=True, max_length=50)),
(
'circuit',
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name='assignments',
to='circuits.circuit',
),
),
(
'group',
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name='assignments',
to='circuits.circuitgroup',
),
),
('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
],
options={
'verbose_name': 'Circuit group assignment',
'verbose_name_plural': 'Circuit group assignments',
'ordering': ('group', 'circuit', 'priority', 'pk'),
},
),
migrations.AddConstraint(
model_name='circuitgroupassignment',
constraint=models.UniqueConstraint(
fields=('circuit', 'group'), name='circuits_circuitgroupassignment_unique_circuit_group'
),
),
]

View File

@@ -6,11 +6,13 @@ from django.utils.translation import gettext_lazy as _
from circuits.choices import *
from dcim.models import CabledObjectModel
from netbox.models import ChangeLoggedModel, OrganizationalModel, PrimaryModel
from netbox.models.features import ContactsMixin, CustomFieldsMixin, CustomLinksMixin, ImageAttachmentsMixin, TagsMixin
from netbox.models.features import ContactsMixin, CustomFieldsMixin, CustomLinksMixin, ExportTemplatesMixin, ImageAttachmentsMixin, TagsMixin
from utilities.fields import ColorField
__all__ = (
'Circuit',
'CircuitGroup',
'CircuitGroupAssignment',
'CircuitTermination',
'CircuitType',
)
@@ -151,6 +153,75 @@ class Circuit(ContactsMixin, ImageAttachmentsMixin, PrimaryModel):
raise ValidationError({'provider_account': "The assigned account must belong to the assigned provider."})
class CircuitGroup(OrganizationalModel):
"""
An administrative grouping of Circuits.
"""
tenant = models.ForeignKey(
to='tenancy.Tenant',
on_delete=models.PROTECT,
related_name='circuit_groups',
blank=True,
null=True
)
class Meta:
ordering = ('name',)
verbose_name = _('circuit group')
verbose_name_plural = _('circuit groups')
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('circuits:circuitgroup', args=[self.pk])
class CircuitGroupAssignment(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, ChangeLoggedModel):
"""
Assignment of a Circuit to a CircuitGroup with an optional priority.
"""
circuit = models.ForeignKey(
Circuit,
on_delete=models.CASCADE,
related_name='assignments'
)
group = models.ForeignKey(
CircuitGroup,
on_delete=models.CASCADE,
related_name='assignments'
)
priority = models.CharField(
verbose_name=_('priority'),
max_length=50,
choices=CircuitPriorityChoices,
blank=True
)
prerequisite_models = (
'circuits.Circuit',
'circuits.CircuitGroup',
)
class Meta:
ordering = ('group', 'circuit', 'priority', 'pk')
constraints = (
models.UniqueConstraint(
fields=('circuit', 'group'),
name='%(app_label)s_%(class)s_unique_circuit_group'
),
)
verbose_name = _('Circuit group assignment')
verbose_name_plural = _('Circuit group assignments')
def __str__(self):
if self.priority:
return f"{self.group} ({self.get_priority_display()})"
return str(self.group)
def get_absolute_url(self):
return reverse('circuits:circuitgroupassignment', args=[self.pk])
class CircuitTermination(
CustomFieldsMixin,
CustomLinksMixin,

View File

@@ -13,6 +13,17 @@ class CircuitIndex(SearchIndex):
display_attrs = ('provider', 'provider_account', 'type', 'status', 'tenant', 'description')
@register_search
class CircuitGroupIndex(SearchIndex):
model = models.CircuitGroup
fields = (
('name', 100),
('slug', 110),
('description', 500),
)
display_attrs = ('description',)
@register_search
class CircuitTerminationIndex(SearchIndex):
model = models.CircuitTermination

View File

@@ -9,6 +9,8 @@ from netbox.tables import NetBoxTable, columns
from .columns import CommitRateColumn
__all__ = (
'CircuitGroupAssignmentTable',
'CircuitGroupTable',
'CircuitTable',
'CircuitTerminationTable',
'CircuitTypeTable',
@@ -75,18 +77,22 @@ class CircuitTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
verbose_name=_('Commit Rate')
)
comments = columns.MarkdownColumn(
verbose_name=_('Comments'),
verbose_name=_('Comments')
)
tags = columns.TagColumn(
url_name='circuits:circuit_list'
)
assignments = columns.ManyToManyColumn(
verbose_name=_('Assignments'),
linkify_item=True
)
class Meta(NetBoxTable.Meta):
model = Circuit
fields = (
'pk', 'id', 'cid', 'provider', 'provider_account', 'type', 'status', 'tenant', 'tenant_group',
'termination_a', 'termination_z', 'install_date', 'termination_date', 'commit_rate', 'description',
'comments', 'contacts', 'tags', 'created', 'last_updated',
'comments', 'contacts', 'tags', 'created', 'last_updated', 'assignments',
)
default_columns = (
'pk', 'cid', 'provider', 'type', 'status', 'tenant', 'termination_a', 'termination_z', 'description',
@@ -119,3 +125,55 @@ class CircuitTerminationTable(NetBoxTable):
'xconnect_id', 'pp_info', 'description', 'created', 'last_updated', 'actions',
)
default_columns = ('pk', 'id', 'circuit', 'provider', 'term_side', 'description')
class CircuitGroupTable(NetBoxTable):
name = tables.Column(
verbose_name=_('Name'),
linkify=True
)
circuit_group_assignment_count = columns.LinkedCountColumn(
viewname='circuits:circuitgroupassignment_list',
url_params={'group_id': 'pk'},
verbose_name=_('Circuits')
)
tags = columns.TagColumn(
url_name='circuits:circuitgroup_list'
)
class Meta(NetBoxTable.Meta):
model = CircuitGroup
fields = (
'pk', 'name', 'description', 'circuit_group_assignment_count', 'tags',
'created', 'last_updated', 'actions',
)
default_columns = ('pk', 'name', 'description', 'circuit_group_assignment_count')
class CircuitGroupAssignmentTable(NetBoxTable):
group = tables.Column(
verbose_name=_('Group'),
linkify=True
)
provider = tables.Column(
accessor='circuit__provider',
verbose_name=_('Provider'),
linkify=True
)
circuit = tables.Column(
verbose_name=_('Circuit'),
linkify=True
)
priority = tables.Column(
verbose_name=_('Priority'),
)
tags = columns.TagColumn(
url_name='circuits:circuitgroupassignment_list'
)
class Meta(NetBoxTable.Meta):
model = CircuitGroupAssignment
fields = (
'pk', 'id', 'group', 'provider', 'circuit', 'priority', 'created', 'last_updated', 'actions', 'tags',
)
default_columns = ('pk', 'group', 'provider', 'circuit', 'priority')

View File

@@ -92,10 +92,11 @@ class CircuitTypeTest(APIViewTestCases.APIViewTestCase):
class CircuitTest(APIViewTestCases.APIViewTestCase):
model = Circuit
brief_fields = ['cid', 'description', 'display', 'id', 'url']
brief_fields = ['cid', 'description', 'display', 'id', 'provider', 'url']
bulk_update_data = {
'status': 'planned',
}
user_permissions = ('circuits.view_provider', 'circuits.view_circuittype')
@classmethod
def setUpTestData(cls):
@@ -150,6 +151,7 @@ class CircuitTest(APIViewTestCases.APIViewTestCase):
class CircuitTerminationTest(APIViewTestCases.APIViewTestCase):
model = CircuitTermination
brief_fields = ['_occupied', 'cable', 'circuit', 'description', 'display', 'id', 'term_side', 'url']
user_permissions = ('circuits.view_circuit', )
@classmethod
def setUpTestData(cls):
@@ -206,9 +208,42 @@ class CircuitTerminationTest(APIViewTestCases.APIViewTestCase):
}
class CircuitGroupTest(APIViewTestCases.APIViewTestCase):
model = CircuitGroup
brief_fields = ['display', 'id', 'name', 'url']
bulk_update_data = {
'description': 'New description',
}
@classmethod
def setUpTestData(cls):
circuit_groups = (
CircuitGroup(name="Circuit Group 1", slug='circuit-group-1'),
CircuitGroup(name="Circuit Group 2", slug='circuit-group-2'),
CircuitGroup(name="Circuit Group 3", slug='circuit-group-3'),
)
CircuitGroup.objects.bulk_create(circuit_groups)
cls.create_data = [
{
'name': 'Circuit Group 4',
'slug': 'circuit-group-4',
},
{
'name': 'Circuit Group 5',
'slug': 'circuit-group-5',
},
{
'name': 'Circuit Group 6',
'slug': 'circuit-group-6',
},
]
class ProviderAccountTest(APIViewTestCases.APIViewTestCase):
model = ProviderAccount
brief_fields = ['account', 'description', 'display', 'id', 'name', 'url']
user_permissions = ('circuits.view_provider',)
@classmethod
def setUpTestData(cls):
@@ -249,9 +284,82 @@ class ProviderAccountTest(APIViewTestCases.APIViewTestCase):
}
class CircuitGroupAssignmentTest(APIViewTestCases.APIViewTestCase):
model = CircuitGroupAssignment
brief_fields = ['circuit', 'display', 'group', 'id', 'priority', 'url']
bulk_update_data = {
'priority': CircuitPriorityChoices.PRIORITY_INACTIVE,
}
user_permissions = ('circuits.view_circuit', 'circuits.view_circuitgroup')
@classmethod
def setUpTestData(cls):
circuit_groups = (
CircuitGroup(name='Circuit Group 1', slug='circuit-group-1'),
CircuitGroup(name='Circuit Group 2', slug='circuit-group-2'),
CircuitGroup(name='Circuit Group 3', slug='circuit-group-3'),
CircuitGroup(name='Circuit Group 4', slug='circuit-group-4'),
CircuitGroup(name='Circuit Group 5', slug='circuit-group-5'),
CircuitGroup(name='Circuit Group 6', slug='circuit-group-6'),
)
CircuitGroup.objects.bulk_create(circuit_groups)
provider = Provider.objects.create(name='Provider 1', slug='provider-1')
circuittype = CircuitType.objects.create(name='Circuit Type 1', slug='circuit-type-1')
circuits = (
Circuit(cid='Circuit 1', provider=provider, type=circuittype),
Circuit(cid='Circuit 2', provider=provider, type=circuittype),
Circuit(cid='Circuit 3', provider=provider, type=circuittype),
Circuit(cid='Circuit 4', provider=provider, type=circuittype),
Circuit(cid='Circuit 5', provider=provider, type=circuittype),
Circuit(cid='Circuit 6', provider=provider, type=circuittype),
)
Circuit.objects.bulk_create(circuits)
assignments = (
CircuitGroupAssignment(
group=circuit_groups[0],
circuit=circuits[0],
priority=CircuitPriorityChoices.PRIORITY_PRIMARY
),
CircuitGroupAssignment(
group=circuit_groups[1],
circuit=circuits[1],
priority=CircuitPriorityChoices.PRIORITY_SECONDARY
),
CircuitGroupAssignment(
group=circuit_groups[2],
circuit=circuits[2],
priority=CircuitPriorityChoices.PRIORITY_TERTIARY
),
)
CircuitGroupAssignment.objects.bulk_create(assignments)
cls.create_data = [
{
'group': circuit_groups[3].pk,
'circuit': circuits[3].pk,
'priority': CircuitPriorityChoices.PRIORITY_PRIMARY,
},
{
'group': circuit_groups[4].pk,
'circuit': circuits[4].pk,
'priority': CircuitPriorityChoices.PRIORITY_SECONDARY,
},
{
'group': circuit_groups[5].pk,
'circuit': circuits[5].pk,
'priority': CircuitPriorityChoices.PRIORITY_TERTIARY,
},
]
class ProviderNetworkTest(APIViewTestCases.APIViewTestCase):
model = ProviderNetwork
brief_fields = ['description', 'display', 'id', 'name', 'url']
user_permissions = ('circuits.view_provider', )
@classmethod
def setUpTestData(cls):

View File

@@ -451,6 +451,136 @@ class CircuitTerminationTestCase(TestCase, ChangeLoggedFilterSetTests):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 7)
class CircuitGroupTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = CircuitGroup.objects.all()
filterset = CircuitGroupFilterSet
@classmethod
def setUpTestData(cls):
tenant_groups = (
TenantGroup(name='Tenant group 1', slug='tenant-group-1'),
TenantGroup(name='Tenant group 2', slug='tenant-group-2'),
TenantGroup(name='Tenant group 3', slug='tenant-group-3'),
)
for tenantgroup in tenant_groups:
tenantgroup.save()
tenants = (
Tenant(name='Tenant 1', slug='tenant-1', group=tenant_groups[0]),
Tenant(name='Tenant 2', slug='tenant-2', group=tenant_groups[1]),
Tenant(name='Tenant 3', slug='tenant-3', group=tenant_groups[2]),
)
Tenant.objects.bulk_create(tenants)
CircuitGroup.objects.bulk_create((
CircuitGroup(name='Circuit Group 1', slug='circuit-group-1', description='foobar1', tenant=tenants[0]),
CircuitGroup(name='Circuit Group 2', slug='circuit-group-2', description='foobar2', tenant=tenants[1]),
CircuitGroup(name='Circuit Group 3', slug='circuit-group-3', tenant=tenants[1]),
))
def test_q(self):
params = {'q': 'foobar1'}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
def test_name(self):
params = {'name': ['Circuit Group 1']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
def test_slug(self):
params = {'slug': ['circuit-group-1']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
def test_description(self):
params = {'description': ['foobar1', 'foobar2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_tenant(self):
tenants = Tenant.objects.all()[:2]
params = {'tenant_id': [tenants[0].pk, tenants[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
params = {'tenant': [tenants[0].slug, tenants[1].slug]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
def test_tenant_group(self):
tenant_groups = TenantGroup.objects.all()[:2]
params = {'tenant_group_id': [tenant_groups[0].pk, tenant_groups[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
params = {'tenant_group': [tenant_groups[0].slug, tenant_groups[1].slug]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
class CircuitGroupAssignmentTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = CircuitGroupAssignment.objects.all()
filterset = CircuitGroupAssignmentFilterSet
@classmethod
def setUpTestData(cls):
circuit_groups = (
CircuitGroup(name='Circuit Group 1', slug='circuit-group-1'),
CircuitGroup(name='Circuit Group 2', slug='circuit-group-2'),
CircuitGroup(name='Circuit Group 3', slug='circuit-group-3'),
CircuitGroup(name='Circuit Group 4', slug='circuit-group-4'),
)
CircuitGroup.objects.bulk_create(circuit_groups)
providers = Provider.objects.bulk_create((
Provider(name='Provider 1', slug='provider-1'),
Provider(name='Provider 2', slug='provider-2'),
Provider(name='Provider 3', slug='provider-3'),
Provider(name='Provider 4', slug='provider-4'),
))
circuittype = CircuitType.objects.create(name='Circuit Type 1', slug='circuit-type-1')
circuits = (
Circuit(cid='Circuit 1', provider=providers[0], type=circuittype),
Circuit(cid='Circuit 2', provider=providers[1], type=circuittype),
Circuit(cid='Circuit 3', provider=providers[2], type=circuittype),
Circuit(cid='Circuit 4', provider=providers[3], type=circuittype),
)
Circuit.objects.bulk_create(circuits)
assignments = (
CircuitGroupAssignment(
group=circuit_groups[0],
circuit=circuits[0],
priority=CircuitPriorityChoices.PRIORITY_PRIMARY
),
CircuitGroupAssignment(
group=circuit_groups[1],
circuit=circuits[1],
priority=CircuitPriorityChoices.PRIORITY_SECONDARY
),
CircuitGroupAssignment(
group=circuit_groups[2],
circuit=circuits[2],
priority=CircuitPriorityChoices.PRIORITY_TERTIARY
),
)
CircuitGroupAssignment.objects.bulk_create(assignments)
def test_group_id(self):
groups = CircuitGroup.objects.filter(name__in=['Circuit Group 1', 'Circuit Group 2'])
params = {'group_id': [groups[0].pk, groups[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
params = {'group': [groups[0].slug, groups[1].slug]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_circuit(self):
circuits = Circuit.objects.all()[:2]
params = {'circuit_id': [circuits[0].pk, circuits[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
params = {'circuit': [circuits[0].cid, circuits[1].cid]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_provider(self):
providers = Provider.objects.all()[:2]
params = {'provider_id': [providers[0].pk, providers[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
params = {'provider': [providers[0].slug, providers[1].slug]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class ProviderNetworkTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = ProviderNetwork.objects.all()
filterset = ProviderNetworkFilterSet

View File

@@ -404,3 +404,109 @@ class CircuitTerminationTestCase(ViewTestCases.PrimaryObjectViewTestCase):
response = self.client.get(reverse('circuits:circuittermination_trace', kwargs={'pk': circuittermination.pk}))
self.assertHttpStatus(response, 200)
class CircuitGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
model = CircuitGroup
@classmethod
def setUpTestData(cls):
circuit_groups = (
CircuitGroup(name='Circuit Group 1', slug='circuit-group-1'),
CircuitGroup(name='Circuit Group 2', slug='circuit-group-2'),
CircuitGroup(name='Circuit Group 3', slug='circuit-group-3'),
)
CircuitGroup.objects.bulk_create(circuit_groups)
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'name': 'Circuit Group X',
'slug': 'circuit-group-x',
'description': 'A new Circuit Group',
'tags': [t.pk for t in tags],
}
cls.csv_data = (
"name,slug",
"Circuit Group 4,circuit-group-4",
"Circuit Group 5,circuit-group-5",
"Circuit Group 6,circuit-group-6",
)
cls.csv_update_data = (
"id,name,description",
f"{circuit_groups[0].pk},Circuit Group 7,New description7",
f"{circuit_groups[1].pk},Circuit Group 8,New description8",
f"{circuit_groups[2].pk},Circuit Group 9,New description9",
)
cls.bulk_edit_data = {
'description': 'Foo',
}
class CircuitGroupAssignmentTestCase(
ViewTestCases.CreateObjectViewTestCase,
ViewTestCases.EditObjectViewTestCase,
ViewTestCases.DeleteObjectViewTestCase,
ViewTestCases.ListObjectsViewTestCase,
ViewTestCases.BulkEditObjectsViewTestCase,
ViewTestCases.BulkDeleteObjectsViewTestCase
):
model = CircuitGroupAssignment
@classmethod
def setUpTestData(cls):
circuit_groups = (
CircuitGroup(name='Circuit Group 1', slug='circuit-group-1'),
CircuitGroup(name='Circuit Group 2', slug='circuit-group-2'),
CircuitGroup(name='Circuit Group 3', slug='circuit-group-3'),
CircuitGroup(name='Circuit Group 4', slug='circuit-group-4'),
)
CircuitGroup.objects.bulk_create(circuit_groups)
provider = Provider.objects.create(name='Provider 1', slug='provider-1')
circuittype = CircuitType.objects.create(name='Circuit Type 1', slug='circuit-type-1')
circuits = (
Circuit(cid='Circuit 1', provider=provider, type=circuittype),
Circuit(cid='Circuit 2', provider=provider, type=circuittype),
Circuit(cid='Circuit 3', provider=provider, type=circuittype),
Circuit(cid='Circuit 4', provider=provider, type=circuittype),
)
Circuit.objects.bulk_create(circuits)
assignments = (
CircuitGroupAssignment(
group=circuit_groups[0],
circuit=circuits[0],
priority=CircuitPriorityChoices.PRIORITY_PRIMARY
),
CircuitGroupAssignment(
group=circuit_groups[1],
circuit=circuits[1],
priority=CircuitPriorityChoices.PRIORITY_SECONDARY
),
CircuitGroupAssignment(
group=circuit_groups[2],
circuit=circuits[2],
priority=CircuitPriorityChoices.PRIORITY_TERTIARY
),
)
CircuitGroupAssignment.objects.bulk_create(assignments)
tags = create_tags('Alpha', 'Bravo', 'Charlie')
cls.form_data = {
'group': circuit_groups[3].pk,
'circuit': circuits[3].pk,
'priority': CircuitPriorityChoices.PRIORITY_INACTIVE,
'tags': [t.pk for t in tags],
}
cls.bulk_edit_data = {
'priority': CircuitPriorityChoices.PRIORITY_INACTIVE,
}

View File

@@ -55,4 +55,19 @@ urlpatterns = [
path('circuit-terminations/delete/', views.CircuitTerminationBulkDeleteView.as_view(), name='circuittermination_bulk_delete'),
path('circuit-terminations/<int:pk>/', include(get_model_urls('circuits', 'circuittermination'))),
# Circuit Groups
path('circuit-groups/', views.CircuitGroupListView.as_view(), name='circuitgroup_list'),
path('circuit-groups/add/', views.CircuitGroupEditView.as_view(), name='circuitgroup_add'),
path('circuit-groups/import/', views.CircuitGroupBulkImportView.as_view(), name='circuitgroup_import'),
path('circuit-groups/edit/', views.CircuitGroupBulkEditView.as_view(), name='circuitgroup_bulk_edit'),
path('circuit-groups/delete/', views.CircuitGroupBulkDeleteView.as_view(), name='circuitgroup_bulk_delete'),
path('circuit-groups/<int:pk>/', include(get_model_urls('circuits', 'circuitgroup'))),
# Circuit Group Assignments
path('circuit-group-assignments/', views.CircuitGroupAssignmentListView.as_view(), name='circuitgroupassignment_list'),
path('circuit-group-assignments/add/', views.CircuitGroupAssignmentEditView.as_view(), name='circuitgroupassignment_add'),
path('circuit-group-assignments/import/', views.CircuitGroupAssignmentBulkImportView.as_view(), name='circuitgroupassignment_import'),
path('circuit-group-assignments/edit/', views.CircuitGroupAssignmentBulkEditView.as_view(), name='circuitgroupassignment_bulk_edit'),
path('circuit-group-assignments/delete/', views.CircuitGroupAssignmentBulkDeleteView.as_view(), name='circuitgroupassignment_bulk_delete'),
path('circuit-group-assignments/<int:pk>/', include(get_model_urls('circuits', 'circuitgroupassignment'))),
]

View File

@@ -1,6 +1,7 @@
from django.contrib import messages
from django.db import transaction
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.translation import gettext_lazy as _
from dcim.views import PathTraceView
from netbox.views import generic
@@ -326,7 +327,9 @@ class CircuitSwapTerminations(generic.ObjectEditView):
# Circuit must have at least one termination to swap
if not circuit.termination_a and not circuit.termination_z:
messages.error(request, "No terminations have been defined for circuit {}.".format(circuit))
messages.error(request, _(
"No terminations have been defined for circuit {circuit}."
).format(circuit=circuit))
return redirect('circuits:circuit', pk=circuit.pk)
return render(request, 'circuits/circuit_terminations_swap.html', {
@@ -374,7 +377,7 @@ class CircuitSwapTerminations(generic.ObjectEditView):
circuit.termination_z = None
circuit.save()
messages.success(request, f"Swapped terminations for circuit {circuit}.")
messages.success(request, _("Swapped terminations for circuit {circuit}.").format(circuit=circuit))
return redirect('circuits:circuit', pk=circuit.pk)
return render(request, 'circuits/circuit_terminations_swap.html', {
@@ -440,3 +443,100 @@ class CircuitTerminationBulkDeleteView(generic.BulkDeleteView):
# Trace view
register_model_view(CircuitTermination, 'trace', kwargs={'model': CircuitTermination})(PathTraceView)
#
# Circuit Groups
#
class CircuitGroupListView(generic.ObjectListView):
queryset = CircuitGroup.objects.annotate(
circuit_group_assignment_count=count_related(CircuitGroupAssignment, 'group')
)
filterset = filtersets.CircuitGroupFilterSet
filterset_form = forms.CircuitGroupFilterForm
table = tables.CircuitGroupTable
@register_model_view(CircuitGroup)
class CircuitGroupView(GetRelatedModelsMixin, generic.ObjectView):
queryset = CircuitGroup.objects.all()
def get_extra_context(self, request, instance):
return {
'related_models': self.get_related_models(request, instance),
}
@register_model_view(CircuitGroup, 'edit')
class CircuitGroupEditView(generic.ObjectEditView):
queryset = CircuitGroup.objects.all()
form = forms.CircuitGroupForm
@register_model_view(CircuitGroup, 'delete')
class CircuitGroupDeleteView(generic.ObjectDeleteView):
queryset = CircuitGroup.objects.all()
class CircuitGroupBulkImportView(generic.BulkImportView):
queryset = CircuitGroup.objects.all()
model_form = forms.CircuitGroupImportForm
class CircuitGroupBulkEditView(generic.BulkEditView):
queryset = CircuitGroup.objects.all()
filterset = filtersets.CircuitGroupFilterSet
table = tables.CircuitGroupTable
form = forms.CircuitGroupBulkEditForm
class CircuitGroupBulkDeleteView(generic.BulkDeleteView):
queryset = CircuitGroup.objects.all()
filterset = filtersets.CircuitGroupFilterSet
table = tables.CircuitGroupTable
#
# Circuit Groups
#
class CircuitGroupAssignmentListView(generic.ObjectListView):
queryset = CircuitGroupAssignment.objects.all()
filterset = filtersets.CircuitGroupAssignmentFilterSet
filterset_form = forms.CircuitGroupAssignmentFilterForm
table = tables.CircuitGroupAssignmentTable
@register_model_view(CircuitGroupAssignment)
class CircuitGroupAssignmentView(generic.ObjectView):
queryset = CircuitGroupAssignment.objects.all()
@register_model_view(CircuitGroupAssignment, 'edit')
class CircuitGroupAssignmentEditView(generic.ObjectEditView):
queryset = CircuitGroupAssignment.objects.all()
form = forms.CircuitGroupAssignmentForm
@register_model_view(CircuitGroupAssignment, 'delete')
class CircuitGroupAssignmentDeleteView(generic.ObjectDeleteView):
queryset = CircuitGroupAssignment.objects.all()
class CircuitGroupAssignmentBulkImportView(generic.BulkImportView):
queryset = CircuitGroupAssignment.objects.all()
model_form = forms.CircuitGroupAssignmentImportForm
class CircuitGroupAssignmentBulkEditView(generic.BulkEditView):
queryset = CircuitGroupAssignment.objects.all()
filterset = filtersets.CircuitGroupAssignmentFilterSet
table = tables.CircuitGroupAssignmentTable
form = forms.CircuitGroupAssignmentBulkEditForm
class CircuitGroupAssignmentBulkDeleteView(generic.BulkDeleteView):
queryset = CircuitGroupAssignment.objects.all()
filterset = filtersets.CircuitGroupAssignmentFilterSet
table = tables.CircuitGroupAssignmentTable

View File

@@ -1,3 +1,5 @@
import warnings
from rest_framework import serializers
from core.choices import JobStatusChoices
@@ -12,25 +14,28 @@ __all__ = (
'NestedJobSerializer',
)
# TODO: Remove in v4.2
warnings.warn(
f"Dedicated nested serializers will be removed in NetBox v4.2. Use Serializer(nested=True) instead.",
DeprecationWarning
)
class NestedDataSourceSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='core-api:datasource-detail')
class Meta:
model = DataSource
fields = ['id', 'url', 'display', 'name']
fields = ['id', 'url', 'display_url', 'display', 'name']
class NestedDataFileSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='core-api:datafile-detail')
class Meta:
model = DataFile
fields = ['id', 'url', 'display', 'path']
fields = ['id', 'url', 'display_url', 'display', 'path']
class NestedJobSerializer(serializers.ModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='core-api:job-detail')
status = ChoiceField(choices=JobStatusChoices)
user = UserSerializer(
nested=True,
@@ -39,4 +44,4 @@ class NestedJobSerializer(serializers.ModelSerializer):
class Meta:
model = Job
fields = ['url', 'created', 'completed', 'user', 'status']
fields = ['url', 'display_url', 'created', 'completed', 'user', 'status']

View File

@@ -126,9 +126,18 @@ class NetBoxAutoSchema(AutoSchema):
return response_serializers
def _get_serializer_name(self, serializer, direction, bypass_extensions=False) -> str:
name = super()._get_serializer_name(serializer, direction, bypass_extensions)
# If this serializer is nested, prepend its name with "Brief"
if getattr(serializer, 'nested', False):
name = f'Brief{name}'
return name
def get_serializer_ref_name(self, serializer):
# from drf-yasg.utils
"""Get serializer's ref_name (or None for ModelSerializer if it is named 'NestedSerializer')
"""Get serializer's ref_name
:param serializer: Serializer instance
:return: Serializer's ``ref_name`` or ``None`` for inline serializer
:rtype: str or None
@@ -137,8 +146,6 @@ class NetBoxAutoSchema(AutoSchema):
serializer_name = type(serializer).__name__
if hasattr(serializer_meta, 'ref_name'):
ref_name = serializer_meta.ref_name
elif serializer_name == 'NestedSerializer' and isinstance(serializer, serializers.ModelSerializer):
ref_name = None
else:
ref_name = serializer_name
if ref_name.endswith('Serializer'):

View File

@@ -1,3 +1,3 @@
from .serializers_.change_logging import *
from .serializers_.data import *
from .serializers_.jobs import *
from .nested_serializers import *

View File

@@ -1,8 +1,8 @@
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from extras.choices import *
from extras.models import ObjectChange
from core.choices import *
from core.models import ObjectChange
from netbox.api.exceptions import SerializerNotFound
from netbox.api.fields import ChoiceField, ContentTypeField
from netbox.api.serializers import BaseModelSerializer
@@ -15,7 +15,6 @@ __all__ = (
class ObjectChangeSerializer(BaseModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:objectchange-detail')
user = UserSerializer(
nested=True,
read_only=True
@@ -44,8 +43,8 @@ class ObjectChangeSerializer(BaseModelSerializer):
class Meta:
model = ObjectChange
fields = [
'id', 'url', 'display', 'time', 'user', 'user_name', 'request_id', 'action', 'changed_object_type',
'changed_object_id', 'changed_object', 'prechange_data', 'postchange_data',
'id', 'url', 'display_url', 'display', 'time', 'user', 'user_name', 'request_id', 'action',
'changed_object_type', 'changed_object_id', 'changed_object', 'prechange_data', 'postchange_data',
]
@extend_schema_field(serializers.JSONField(allow_null=True))

View File

@@ -1,5 +1,3 @@
from rest_framework import serializers
from core.choices import *
from core.models import DataFile, DataSource
from netbox.api.fields import ChoiceField, RelatedObjectCountField
@@ -13,9 +11,6 @@ __all__ = (
class DataSourceSerializer(NetBoxModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='core-api:datasource-detail'
)
type = ChoiceField(
choices=get_data_backend_choices()
)
@@ -30,16 +25,14 @@ class DataSourceSerializer(NetBoxModelSerializer):
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',
'id', 'url', 'display_url', 'display', 'name', 'type', 'source_url', 'enabled', 'status', 'description',
'parameters', 'ignore_rules', 'comments', 'custom_fields', 'created', 'last_updated', 'last_synced',
'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
@@ -48,6 +41,6 @@ class DataFileSerializer(NetBoxModelSerializer):
class Meta:
model = DataFile
fields = [
'id', 'url', 'display', 'source', 'path', 'last_updated', 'size', 'hash',
'id', 'url', 'display_url', 'display', 'source', 'path', 'last_updated', 'size', 'hash',
]
brief_fields = ('id', 'url', 'display', 'path')

View File

@@ -12,7 +12,6 @@ __all__ = (
class JobSerializer(BaseModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='core-api:job-detail')
user = UserSerializer(
nested=True,
read_only=True
@@ -25,7 +24,7 @@ class JobSerializer(BaseModelSerializer):
class Meta:
model = Job
fields = [
'id', 'url', 'display', 'object_type', 'object_id', 'name', 'status', 'created', 'scheduled', 'interval',
'id', 'url', 'display_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

@@ -5,12 +5,10 @@ from . import views
router = NetBoxRouter()
router.APIRootView = views.CoreRootView
# Data sources
router.register('data-sources', views.DataSourceViewSet)
router.register('data-files', views.DataFileViewSet)
# Jobs
router.register('jobs', views.JobViewSet)
router.register('object-changes', views.ObjectChangeViewSet)
app_name = 'core-api'
urlpatterns = router.urls

View File

@@ -7,7 +7,10 @@ from rest_framework.routers import APIRootView
from rest_framework.viewsets import ReadOnlyModelViewSet
from core import filtersets
from core.choices import DataSourceStatusChoices
from core.jobs import SyncDataSourceJob
from core.models import *
from netbox.api.metadata import ContentTypeMetadata
from netbox.api.viewsets import NetBoxModelViewSet, NetBoxReadOnlyModelViewSet
from . import serializers
@@ -35,7 +38,11 @@ class DataSourceViewSet(NetBoxModelViewSet):
if not request.user.has_perm('core.sync_datasource', obj=datasource):
raise PermissionDenied(_("This user does not have permission to synchronize this data source."))
datasource.enqueue_sync_job(request)
# Enqueue the sync job & update the DataSource's status
SyncDataSourceJob.enqueue(instance=datasource, user=request.user)
datasource.status = DataSourceStatusChoices.QUEUED
DataSource.objects.filter(pk=datasource.pk).update(status=datasource.status)
serializer = serializers.DataSourceSerializer(datasource, context={'request': request})
return Response(serializer.data)
@@ -54,3 +61,13 @@ class JobViewSet(ReadOnlyModelViewSet):
queryset = Job.objects.all()
serializer_class = serializers.JobSerializer
filterset_class = filtersets.JobFilterSet
class ObjectChangeViewSet(ReadOnlyModelViewSet):
"""
Retrieve a list of recent changes.
"""
metadata_class = ContentTypeMetadata
queryset = ObjectChange.objects.valid_models()
serializer_class = serializers.ObjectChangeSerializer
filterset_class = filtersets.ObjectChangeFilterSet

View File

@@ -18,7 +18,7 @@ class CoreConfig(AppConfig):
def ready(self):
from core.api import schema # noqa
from netbox.models.features import register_models
from . import data_backends, search
from . import data_backends, events, search
# Register models
register_models(*self.get_models())

View File

@@ -59,8 +59,31 @@ class JobStatusChoices(ChoiceSet):
(STATUS_FAILED, _('Failed'), 'red'),
)
ENQUEUED_STATE_CHOICES = (
STATUS_PENDING,
STATUS_SCHEDULED,
STATUS_RUNNING,
)
TERMINAL_STATE_CHOICES = (
STATUS_COMPLETED,
STATUS_ERRORED,
STATUS_FAILED,
)
#
# ObjectChanges
#
class ObjectChangeActionChoices(ChoiceSet):
ACTION_CREATE = 'create'
ACTION_UPDATE = 'update'
ACTION_DELETE = 'delete'
CHOICES = (
(ACTION_CREATE, _('Created'), 'green'),
(ACTION_UPDATE, _('Updated'), 'blue'),
(ACTION_DELETE, _('Deleted'), 'red'),
)

33
netbox/core/events.py Normal file
View File

@@ -0,0 +1,33 @@
from django.utils.translation import gettext as _
from netbox.events import EventType, EVENT_TYPE_KIND_DANGER, EVENT_TYPE_KIND_SUCCESS, EVENT_TYPE_KIND_WARNING
__all__ = (
'JOB_COMPLETED',
'JOB_ERRORED',
'JOB_FAILED',
'JOB_STARTED',
'OBJECT_CREATED',
'OBJECT_DELETED',
'OBJECT_UPDATED',
)
# Object events
OBJECT_CREATED = 'object_created'
OBJECT_UPDATED = 'object_updated'
OBJECT_DELETED = 'object_deleted'
# Job events
JOB_STARTED = 'job_started'
JOB_COMPLETED = 'job_completed'
JOB_FAILED = 'job_failed'
JOB_ERRORED = 'job_errored'
# Register core events
EventType(OBJECT_CREATED, _('Object created')).register()
EventType(OBJECT_UPDATED, _('Object updated')).register()
EventType(OBJECT_DELETED, _('Object deleted'), destructive=True).register()
EventType(JOB_STARTED, _('Job started')).register()
EventType(JOB_COMPLETED, _('Job completed'), kind=EVENT_TYPE_KIND_SUCCESS).register()
EventType(JOB_FAILED, _('Job failed'), kind=EVENT_TYPE_KIND_WARNING).register()
EventType(JOB_ERRORED, _('Job errored'), kind=EVENT_TYPE_KIND_DANGER).register()

View File

@@ -1,3 +1,4 @@
from django.contrib.contenttypes.models import ContentType
from django.db.models import Q
from django.utils.translation import gettext as _
@@ -5,6 +6,8 @@ import django_filters
from netbox.filtersets import BaseFilterSet, ChangeLoggedModelFilterSet, NetBoxModelFilterSet
from netbox.utils import get_data_backend_choices
from users.models import User
from utilities.filters import ContentTypeFilter
from .choices import *
from .models import *
@@ -13,6 +16,7 @@ __all__ = (
'DataFileFilterSet',
'DataSourceFilterSet',
'JobFilterSet',
'ObjectChangeFilterSet',
)
@@ -126,6 +130,43 @@ class JobFilterSet(BaseFilterSet):
)
class ObjectChangeFilterSet(BaseFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
)
time = django_filters.DateTimeFromToRangeFilter()
changed_object_type = ContentTypeFilter()
changed_object_type_id = django_filters.ModelMultipleChoiceFilter(
queryset=ContentType.objects.all()
)
user_id = django_filters.ModelMultipleChoiceFilter(
queryset=User.objects.all(),
label=_('User (ID)'),
)
user = django_filters.ModelMultipleChoiceFilter(
field_name='user__username',
queryset=User.objects.all(),
to_field_name='username',
label=_('User name'),
)
class Meta:
model = ObjectChange
fields = (
'id', 'user', 'user_name', 'request_id', 'action', 'changed_object_type_id', 'changed_object_id',
'related_object_type', 'related_object_id', 'object_repr',
)
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(user_name__icontains=value) |
Q(object_repr__icontains=value)
)
class ConfigRevisionFilterSet(BaseFilterSet):
q = django_filters.CharFilter(
method='search',

View File

@@ -1,5 +1,4 @@
from django import forms
from django.contrib.auth import get_user_model
from django.utils.translation import gettext_lazy as _
from core.choices import *
@@ -7,8 +6,11 @@ from core.models import *
from netbox.forms import NetBoxModelFilterSetForm
from netbox.forms.mixins import SavedFiltersMixin
from netbox.utils import get_data_backend_choices
from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, FilterForm
from utilities.forms.fields import ContentTypeChoiceField, DynamicModelMultipleChoiceField
from users.models import User
from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, FilterForm, add_blank_choice
from utilities.forms.fields import (
ContentTypeChoiceField, ContentTypeMultipleChoiceField, DynamicModelMultipleChoiceField,
)
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import DateTimePicker
@@ -17,6 +19,7 @@ __all__ = (
'DataFileFilterForm',
'DataSourceFilterForm',
'JobFilterForm',
'ObjectChangeFilterForm',
)
@@ -118,12 +121,46 @@ class JobFilterForm(SavedFiltersMixin, FilterForm):
widget=DateTimePicker()
)
user = DynamicModelMultipleChoiceField(
queryset=get_user_model().objects.all(),
queryset=User.objects.all(),
required=False,
label=_('User')
)
class ObjectChangeFilterForm(SavedFiltersMixin, FilterForm):
model = ObjectChange
fieldsets = (
FieldSet('q', 'filter_id'),
FieldSet('time_before', 'time_after', name=_('Time')),
FieldSet('action', 'user_id', 'changed_object_type_id', name=_('Attributes')),
)
time_after = forms.DateTimeField(
required=False,
label=_('After'),
widget=DateTimePicker()
)
time_before = forms.DateTimeField(
required=False,
label=_('Before'),
widget=DateTimePicker()
)
action = forms.ChoiceField(
label=_('Action'),
choices=add_blank_choice(ObjectChangeActionChoices),
required=False
)
user_id = DynamicModelMultipleChoiceField(
queryset=User.objects.all(),
required=False,
label=_('User')
)
changed_object_type_id = ContentTypeMultipleChoiceField(
queryset=ObjectType.objects.with_feature('change_logging'),
required=False,
label=_('Object Type'),
)
class ConfigRevisionFilterForm(SavedFiltersMixin, FilterForm):
fieldsets = (
FieldSet('q', 'filter_id'),

View File

@@ -6,6 +6,7 @@ from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin
__all__ = (
'DataFileFilter',
'DataSourceFilter',
'ObjectChangeFilter',
)
@@ -19,3 +20,9 @@ class DataFileFilter(BaseFilterMixin):
@autotype_decorator(filtersets.DataSourceFilterSet)
class DataSourceFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.ObjectChange, lookups=True)
@autotype_decorator(filtersets.ObjectChangeFilterSet)
class ObjectChangeFilter(BaseFilterMixin):
pass

View File

@@ -0,0 +1,24 @@
from typing import Annotated, List
import strawberry
import strawberry_django
from django.contrib.contenttypes.models import ContentType
from core.models import ObjectChange
__all__ = (
'ChangelogMixin',
)
@strawberry.type
class ChangelogMixin:
@strawberry_django.field
def changelog(self, info) -> List[Annotated["ObjectChangeType", strawberry.lazy('.types')]]:
content_type = ContentType.objects.get_for_model(self)
object_changes = ObjectChange.objects.filter(
changed_object_type=content_type,
changed_object_id=self.pk
)
return object_changes.restrict(info.context.request.user, 'view')

View File

@@ -3,18 +3,13 @@ from typing import List
import strawberry
import strawberry_django
from core import models
from .types import *
@strawberry.type
@strawberry.type(name="Query")
class CoreQuery:
@strawberry.field
def data_file(self, id: int) -> DataFileType:
return models.DataFile.objects.get(pk=id)
data_file: DataFileType = strawberry_django.field()
data_file_list: List[DataFileType] = strawberry_django.field()
@strawberry.field
def data_source(self, id: int) -> DataSourceType:
return models.DataSource.objects.get(pk=id)
data_source: DataSourceType = strawberry_django.field()
data_source_list: List[DataSourceType] = strawberry_django.field()

View File

@@ -10,6 +10,7 @@ from .filters import *
__all__ = (
'DataFileType',
'DataSourceType',
'ObjectChangeType',
)
@@ -30,3 +31,12 @@ class DataFileType(BaseObjectType):
class DataSourceType(NetBoxObjectType):
datafiles: List[Annotated["DataFileType", strawberry.lazy('core.graphql.types')]]
@strawberry_django.type(
models.ObjectChange,
fields='__all__',
filters=ObjectChangeFilter
)
class ObjectChangeType(BaseObjectType):
pass

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