Compare commits

...

630 Commits

Author SHA1 Message Date
Jeremy Stretch
9f7313e492 Merge pull request #3661 from netbox-community/develop
Release v2.6.7
2019-11-01 15:43:38 -04:00
Jeremy Stretch
f6fbf66c8c Merge branch 'master' into develop 2019-11-01 15:39:24 -04:00
Jeremy Stretch
3deedfd177 Release v2.6.7 2019-11-01 15:37:05 -04:00
Jeremy Stretch
e9d5ff095c Suppress migration messages during tests; fix typo 2019-11-01 15:08:59 -04:00
Jeremy Stretch
43d6bfa15f Corrected test 2019-11-01 15:08:20 -04:00
Jeremy Stretch
875e09013c Move TreeNodeMultipleChoiceFilter tests to utilities (follow-up to #3616) 2019-11-01 15:01:24 -04:00
Jeremy Stretch
ee854eff2c Changelog for #3357 2019-11-01 14:31:03 -04:00
Jeremy Stretch
b176e0fafd Merge pull request #3616 from kobayashi/3357
allow null region filtering
2019-11-01 14:29:32 -04:00
Jeremy Stretch
fb24a4d420 Merge pull request #3660 from tyler-8/gunicorn_options
Add max_requests details to example config
2019-11-01 12:32:39 -04:00
Jeremy Stretch
391c42300e Closes #3659: Add filtering for objects in admin UI 2019-11-01 12:22:39 -04:00
Tyler Bigler
2a8fa01a57 Increase request counts for max_requests 2019-11-01 12:09:44 -04:00
Jeremy Stretch
bd4b496d14 Tweak PR template to indicate placement of issue number 2019-11-01 11:52:43 -04:00
Jeremy Stretch
66f4aac0e8 Changelog for #3619 2019-11-01 11:45:22 -04:00
Jeremy Stretch
267caa348e Merge pull request #3657 from Netnod/3619-new-400G-osfp-interface-type
3619 new 400 g osfp interface type
2019-11-01 11:43:43 -04:00
Tyler Bigler
7bf09a3085 Remove redundant word 2019-11-01 10:26:53 -04:00
Tyler Bigler
a0de0696c5 Add max_requests details to example config 2019-11-01 10:23:25 -04:00
Emil Palm
aa6b2b8407 Merge remote-tracking branch 'upstream/develop' into 3619-new-400G-osfp-interface-type 2019-10-30 14:31:17 -05:00
Emil Palm
3e6726ff97 Add IFACE_TYPE_400GE_OSPF 2019-10-30 14:30:23 -05:00
Jeremy Stretch
60ec3fde9d Closes #3652: Limit next/previous rack by assigned rack group 2019-10-29 15:17:00 -04:00
Jeremy Stretch
846acf7e9d Changelog for #3629 2019-10-28 13:19:01 -04:00
kobayashi
fb9279cc74 Merge pull request #3645 from BegBlev/llpd-port-id
Retrieve port-id in LLDP tab
2019-10-28 12:19:10 -04:00
kobayashi
d2aa9b8e79 filtering multiple regions with null 2019-10-28 02:24:44 -04:00
Tyler Bigler
eaeb52de20 Add CONN_MAX_AGE to documentation (#3642)
* Add CONN_MAX_AGE to sample configurations

* Correct alignment

* Restore ghost space

* Correct alignment.

* Use stable docs url
2019-10-25 13:11:48 -04:00
Jeremy Stretch
fdbf41e9fd Fixes #3643: Update all Django documentation links to 'stable' version 2019-10-25 11:09:30 -04:00
Jeremy Stretch
092346c819 Changelog for #3309 2019-10-25 09:57:17 -04:00
Jeremy Stretch
5a5f51fe48 Merge pull request #3632 from netbox-community/3309-changelog-middleware
Rewrite change logging middleware
2019-10-25 09:47:32 -04:00
Jeremy Stretch
800df1ebbe Fixes #3636: Add missing rack_group field to PowerFeed CSV export 2019-10-25 09:27:33 -04:00
John Anderson
8eec67e73a #3635 changelog 2019-10-24 23:31:06 -04:00
John Anderson
33b420652d Merge pull request #3639 from sdktr/patch-1
Fix #3635 - cache circuits.*
2019-10-24 21:16:28 -04:00
Stefan de Kooter
42b679d9a3 Fix #3635 - cache circuits.*
Enable caching for items under the circuits app
2019-10-25 00:46:05 +02:00
Jeremy Stretch
ae4f17264f Fixes #3460: Extend upgrade script to validate Python dependencies 2019-10-23 17:12:32 -04:00
Jeremy Stretch
c7d8083ac6 Closes #3594: Add ChoiceVar for custom scripts 2019-10-23 15:59:27 -04:00
Jeremy Stretch
95fec1a87c Merge pull request #3627 from bdlamprecht/pg_dump-documentation
Adding note on invalidating cache
2019-10-23 12:24:27 -04:00
Jeremy Stretch
7467347af1 Fixes #3596: Prevent server error when reassigning a device to a new device bay 2019-10-23 09:28:00 -04:00
Jeremy Stretch
0ebc2e4ac0 Fix reporting of custom fields in webhook data on object deletion 2019-10-22 16:12:25 -04:00
Jeremy Stretch
ccb9f7bfe2 Rewrote ObjectChangeMiddleware to remove the curried handle_deleted_object() function 2019-10-22 15:10:49 -04:00
kobayashi
766b5dff24 allow null region filtering 2019-10-22 00:41:49 -04:00
Vincent Catros
68e0329c1b Retrieve port-id in LLDP tab 2019-10-21 11:57:23 +02:00
Brady Lamprecht
fd99994fdb Adding note on invalidating cache 2019-10-18 14:59:55 -06:00
Jeremy Stretch
f21a63382c Changelog for #3340 2019-10-18 09:08:56 -04:00
Jeremy Stretch
6f0581598b Merge pull request #3613 from kobayashi/3340
modified front port connection type list
2019-10-18 09:07:22 -04:00
kobayashi
244e85e836 modify patch panel port connection type list 2019-10-18 00:01:21 -04:00
Jeremy Stretch
1df6713ad5 Minor improvements pertaining to CII best practices 2019-10-17 20:56:37 -04:00
Jeremy Stretch
af73ce75ce Merge pull request #3607 from netbox-community/3606-stale-bot
implemented #3606 - added stale bot config
2019-10-17 14:32:40 -04:00
Jeremy Stretch
f08968da49 Exempt issues tagged with "status: blocked" 2019-10-17 14:28:27 -04:00
John Anderson
24344ccfaf Merge pull request #3615 from markkuleinio/develop
Update examples in webhooks.md
2019-10-15 14:27:20 -04:00
Markku Leiniö
91f045a2e4 Update examples in webhooks.md 2019-10-15 20:51:57 +03:00
Jeremy Stretch
050dfb279d Merge pull request #3597 from dgarros/patch-1
Update pillow version to 6.2.0
2019-10-15 11:54:44 -04:00
Jeremy Stretch
f9b7f18be6 Merge pull request #3609 from ScanPlusGmbH/missing-variable
Add SCRIPTS_ROOT to configuration.example.py
2019-10-14 11:58:28 -04:00
Tobias Genannt
a7380ba353 Add SCRIPTS_ROOT to configuration.example.py
Fixes #3608 by adding the new variable to the example configuration.
2019-10-14 09:29:04 +02:00
John Anderson
b8feba1070 implemented #3606 - added stale bot config 2019-10-13 04:12:58 -04:00
John Anderson
64575fec42 Merge pull request #3605 from netbox-community/3445-webhooks-additional-headers
implemented #3445 - Add support for additional user defined headers t…
2019-10-13 03:15:43 -04:00
John Anderson
c7d9bf839e implemented #3445 - Add support for additional user defined headers to be added to webhook requests 2019-10-13 03:09:58 -04:00
John Anderson
8480c0da53 Merge pull request #3603 from netbox-community/3499-webhooks-ca-filepath
implemented #3499 - Add  to Webhook model to support user supplied CA certificate verification of webhook requests
2019-10-13 01:50:10 -04:00
John Anderson
5e88313276 typo in change log 2019-10-13 01:45:20 -04:00
John Anderson
09d7d38b04 implemented #3499 - Add to Webhook model to support user supplied CA certificate verrification of webhook requests 2019-10-13 01:43:08 -04:00
Damien Garros
4cc29729f9 Update pillow version to 6.2.0
A new CVE just got reporter regarding Pillow 
http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-16865
it's affecting all version prior to 6.2.0
2019-10-11 13:45:37 -04:00
Jeremy Stretch
b25faec159 Post-release version bump 2019-10-10 12:42:57 -04:00
Jeremy Stretch
c2b1556d68 Correct version number 2019-10-10 12:41:02 -04:00
Jeremy Stretch
81acf57518 Merge pull request #3591 from netbox-community/develop
Release v2.6.6
2019-10-10 09:34:17 -07:00
Jeremy Stretch
abbc44a7d1 #3259: Add site and rack fields to cables filter form 2019-10-10 12:28:17 -04:00
Jeremy Stretch
74c259af2e Tweak release notes for v2.6.6 2019-10-10 12:21:40 -04:00
Jeremy Stretch
465304588e Release v2.6.6 2019-10-10 12:18:18 -04:00
Jeremy Stretch
70ad705380 Provide index link to most recent version for release note 2019-10-10 12:10:35 -04:00
Jeremy Stretch
4e6b60b0f4 Rearranged release notes 2019-10-10 12:02:53 -04:00
Jeremy Stretch
d849a49842 Fixes #3588: Enforce object-form JSON for local context data on devices and VMs 2019-10-10 10:41:08 -04:00
Jeremy Stretch
4c7c40a09d Merge pull request #3587 from netbox-community/reorganized-changelog
Reorganized CHANGELOG into release notes based on minor version number
2019-10-10 06:17:42 -07:00
Jeremy Stretch
521d6941fa Merge branch 'develop' into reorganized-changelog 2019-10-10 09:15:57 -04:00
kobayashi
e223c88548 Merge pull request #3553 from chambersh1129/develop
Replace all instances of .extra() in QuerySets with annotations #3117
2019-10-09 21:54:16 -04:00
chambersh1129
52e9369af4 PEP 8 E121 continuation line under-indented for hanging indent fix 2019-10-09 20:11:02 -04:00
chambersh1129
bdb3838d71 Replace all instances of .extra() in QuerySets with annotations, including references in docstrings 2019-10-09 20:11:02 -04:00
Jeremy Stretch
0a921d37f8 Fixes #3582: Enforce view permissions on global search results 2019-10-09 16:45:33 -04:00
Jeremy Stretch
896d58fc3f Fixes #3458: Prevent primary IP address for a device/VM from being reassigned 2019-10-09 16:22:06 -04:00
Jeremy Stretch
c770b13903 Fixes #3474: Fix device status page loading when NAPALM call fails 2019-10-09 15:44:32 -04:00
Jeremy Stretch
6cdeb0ed95 Fixes #3463: Correct CSV headers for exported power feeds 2019-10-09 15:25:31 -04:00
Jeremy Stretch
99f7cfcbd3 Closes #3581: Introduce commit_default custom script attribute to not commit changes by default 2019-10-09 15:16:50 -04:00
Jeremy Stretch
d402b6c3e5 Closes #3545: Add MultiObjectVar for custom scripts 2019-10-09 15:06:00 -04:00
Jeremy Stretch
738368a6a1 Closes #3471: Disallow raw HTML in Markdown-rendered fields 2019-10-09 14:47:40 -04:00
Jeremy Stretch
3dbf94146a Reorganized CHANGELOG into release notes based on minor version number 2019-10-09 13:54:05 -04:00
Jeremy Stretch
7a65930361 Merge pull request #3561 from netbox-community/3560-drf-bootstrap-css
Override DRF's builtin Bootstrap with NetBox's own more recent copy
2019-10-09 08:53:13 -07:00
Jeremy Stretch
6068b1a275 Closes #3580: Render text and URL fields as textareas in the custom link form 2019-10-09 09:40:24 -04:00
Jeremy Stretch
4c677e7fe6 Force checkbox table columns to narrow width 2019-10-07 17:22:35 -04:00
Jeremy Stretch
c668b990e1 Closes #3563: Enable editing of individual DeviceType components 2019-10-07 17:08:51 -04:00
Jeremy Stretch
a9d3d38a41 Changelog for #1941 2019-10-07 15:03:10 -04:00
Jeremy Stretch
f04c321aab Merge pull request #3565 from ananace/add-infiniband
Added InfiniBand interface form factor
2019-10-07 12:02:06 -07:00
Jeremy Stretch
ed785098a6 Fixes #3571: Prevent erroneous redirects when editing tags 2019-10-07 14:27:02 -04:00
Jeremy Stretch
9198d6924e Fixes #3573: Ensure consistent display of changelog retention period 2019-10-07 14:12:16 -04:00
Jeremy Stretch
4ab6aca2ec Fixes #3575: Restore label for comments field when bulk editing circuits 2019-10-07 14:06:41 -04:00
dansheps
04d9889127 Fixes #3574 - Change device to parent in interface edit form vlan filtering logic 2019-10-07 08:54:39 -05:00
Jeremy Stretch
a3b9bdaff1 Miscellaneous cleanup 2019-10-04 12:59:10 -04:00
Jeremy Stretch
6f83fca216 Delete obsolete IFACE_ORDERING constants 2019-10-03 19:53:32 -04:00
Jeremy Stretch
9f52cdeee9 Closes #3568: Update jQuery library to v3.4.1 2019-10-03 18:53:00 -04:00
Jeremy Stretch
26071903c1 Add stdout progress logging to cable migration 2019-10-03 18:49:56 -04:00
Jeremy Stretch
8a5ee6f8ea Merge pull request #3552 from kobayashi/develop
fixed broken links
2019-10-03 14:33:15 -04:00
Saria Hajjar
0ec8476b4d Added InfiniBand interface form factor 2019-10-03 09:22:19 +02:00
Jeremy Stretch
63ad93afd0 Merge pull request #3550 from netbox-community/3259-cable-filters
3259 cable filters
2019-10-01 14:25:20 -04:00
Jeremy Stretch
60e03eb841 Override DRF's builtin Bootstrap with NetBox's own more recent copy 2019-10-01 10:11:23 -04:00
kobayashi
b2ac81a78f fixed broken links 2019-09-28 00:41:09 -04:00
Jeremy Stretch
aa9d034b5d Filter by site slug rather than by name 2019-09-27 15:12:16 -04:00
Jeremy Stretch
4889c8ff9b Closes #3259: Add rack and site filters for cables 2019-09-27 12:18:53 -04:00
Jeremy Stretch
d2ab41abfb Cache A/B termination devices on the Cable model 2019-09-26 17:17:12 -04:00
Jeremy Stretch
56ddf79ae7 Update Travis-CI badges in README 2019-09-26 15:03:20 -04:00
Jeremy Stretch
ef1fa55902 Post-release version bump 2019-09-25 13:29:22 -04:00
Jeremy Stretch
963991e246 Merge pull request #3544 from netbox-community/develop
Release v2.6.5
2019-09-25 13:28:17 -04:00
Jeremy Stretch
3e4cd59fc0 Releae v2.6.5 2019-09-25 13:24:28 -04:00
Jeremy Stretch
874247c7ab Merge pull request #3508 from mohahmed13/develop
Adding a reference to deploying NetBox via Kubernetes manifests
2019-09-25 12:11:08 -04:00
Jeremy Stretch
b7b04045de Closes #3515: Enable export templates for inventory items 2019-09-25 12:07:41 -04:00
Daniel Sheppard
a84dbbf482 Fixes: #3543 - Adds inline vlan editing to virtual machine interfaces 2019-09-25 10:49:08 -05:00
Jeremy Stretch
f54774f6a1 Closes #3297: Include reserved units when calculating rack utilization 2019-09-25 10:54:08 -04:00
Daniel Sheppard
a359d88897 Update changelog 2019-09-25 09:29:54 -05:00
Daniel Sheppard
458251683c Fixes: #3540 - Changed interface edit to use new inline vlan edit fields 2019-09-25 09:28:23 -05:00
Jeremy Stretch
eeb3434349 Closes #3347: Extend upgrade script to automatically remove stale content types 2019-09-25 10:22:02 -04:00
Jeremy Stretch
d183a9e7b5 Closes #3352: Enable filtering changelog API by changed_object_id 2019-09-25 10:11:41 -04:00
Jeremy Stretch
86cef1c502 Fixes #3435: Change IP/prefix CSV export to reference VRF name instead of RD 2019-09-25 09:39:03 -04:00
Jeremy Stretch
b8d8cb33ff Closes #3529: Enable filtering circuits list by region 2019-09-25 09:21:21 -04:00
Daniel Sheppard
a4690ec5ce Fix ordering for rack positioning 2019-09-24 15:08:57 -05:00
Daniel Sheppard
d0597cd289 Fix ordering for Rack Positioning 2019-09-24 14:36:41 -05:00
Jeremy Stretch
fe85dc1186 Closes #3524: Enable bulk editing of power outlet/power port associations 2019-09-24 15:27:47 -04:00
Jeremy Stretch
51baaab74b Changelog for #3464 2019-09-24 15:14:22 -04:00
Jeremy Stretch
35fc0d6847 Merge pull request #3536 from DanSheps/develop
Fixes: #3464 - Change color picker to dynamic coloring from static CSS
2019-09-24 15:13:11 -04:00
Jeremy Stretch
ffc6eec483 Fixes #3519: Prevent cables from being terminated to virtual/wireless interfaces 2019-09-24 15:07:54 -04:00
Daniel Sheppard
17ec9aa170 Fixes: #3464 - Change color picker to dynamic coloring from static CSS 2019-09-24 09:39:06 -05:00
Daniel Sheppard
6fdd35785e Fixes: #3531 - Fix FG Color for Rack Role 2019-09-24 08:50:23 -05:00
Daniel Sheppard
fb34507def Fixes: #3532 - add device to graph type for documentation 2019-09-24 08:45:31 -05:00
Daniel Sheppard
8996c530c9 Fixes: #3534 - Add none option for untagged vlan field 2019-09-24 08:39:41 -05:00
Jeremy Stretch
b0724dfd14 Add developer guidance 2019-09-23 14:57:01 -04:00
Daniel Sheppard
a17060c060 Update CHANGELOG.md 2019-09-23 13:04:13 -05:00
Jeremy Stretch
f8e10f66e1 Remove extraneous demo scripts 2019-09-23 09:21:38 -04:00
Jeremy Stretch
0798533201 Add docs link for custom scripts; arrange links alphabetically 2019-09-20 09:10:32 -04:00
Daniel Sheppard
a1216fc1b7 Fixed thrown error in parseURL 2019-09-19 10:32:47 -05:00
Jeremy Stretch
353195850f Post-release version bump 2019-09-19 09:36:23 -04:00
Jeremy Stretch
b8dd379cef Merge pull request #3518 from netbox-community/develop
Release v2.6.4
2019-09-19 09:34:49 -04:00
Jeremy Stretch
be2417d808 Release v2.6.4 2019-09-19 09:30:16 -04:00
Jeremy Stretch
d4b263dd0c Fixes #3439: Add a note about the Swagger API interface 2019-09-18 16:41:08 -04:00
Jeremy Stretch
37aaf6f4ab Fixes #3429: Update installation docs for Redis 2019-09-18 16:35:45 -04:00
Jeremy Stretch
51fb0b59ec Closes #3485: Enable embedded graphs for devices 2019-09-18 15:59:52 -04:00
Jeremy Stretch
a0545568cd Fixes #3514: Label TextVar fields when rendering custom script forms 2019-09-18 15:39:26 -04:00
Jeremy Stretch
84208d5429 Fixes #3511: Correct API URL for nested device bays 2019-09-18 14:40:47 -04:00
Jeremy Stretch
7264a4ffb6 Fixes #3513: Fix assignment of tags when creating front/rear ports 2019-09-18 14:33:47 -04:00
Daniel Sheppard
e8ee6f1bc5 Clean up extra line that snuck in 2019-09-17 15:45:55 -05:00
Jeremy Stretch
a742d897d7 Closes #3510: Add minimum/maximum prefix length enforcement for IPNetworkVar 2019-09-17 16:36:36 -04:00
Moh Ahmed
0903362334 Adding a reference to deploying NetBox via Kubernetes manifests 2019-09-17 15:51:29 -04:00
Daniel Sheppard
0601619f50 Merge branch 'develop' of https://github.com/netbox-community/netbox into develop 2019-09-13 12:09:02 -05:00
Daniel Sheppard
1a1f6aff7b Closes: #3495 2019-09-13 12:08:48 -05:00
Jeremy Stretch
5962e7c942 Fixes #3501: Fix rendering of checkboxes on custom script forms 2019-09-13 11:45:35 -04:00
Daniel Sheppard
57d35181f0 Fix performance issues when creating/editing interfaces due to unfiltered vlan queryset 2019-09-12 11:13:40 -05:00
Daniel Sheppard
73065fa6e7 Using static element to determine brief parameter, corrected to $(element) 2019-09-11 10:10:43 -05:00
Jeremy Stretch
a8ca536d44 Bump platform name/slug max length to 100 chars (#3318) 2019-09-10 15:50:41 -04:00
Jeremy Stretch
062c65fd67 Changelog cleanup 2019-09-10 15:45:54 -04:00
Jeremy Stretch
f533530693 Moved related projects list to the wiki 2019-09-10 15:30:19 -04:00
Jeremy Stretch
355910e182 Fixes #3489: Prevent exception triggered by webhook upon object deletion 2019-09-09 15:50:10 -04:00
Daniel Sheppard
e67d4fb2e5 Update Changelog with Future Changes 2019-09-06 13:32:21 -05:00
Daniel Sheppard
050f2478d3 Fixes: #3318 - Increases length of platform name and slug to 64 characters (#3353) 2019-09-06 13:01:27 -05:00
Daniel Sheppard
9c6dbd7337 Add in in-line vlan editing and Bulk vlan editing (#3350)
* Fixes #3341 - Added in-line vlan editing
* Fixes #2160 - Added bulk vlan editing

Inconsequential behaviour changes:

* APISelect can now take "full=True" to return a non-brief set
* Select2 will no group by "group & site, group, site, global" if full=True is set in APISelect
2019-09-06 12:45:37 -05:00
Daniel Sheppard
8f5e73a598 Add filter for has local context data (#3159)
* Add filter for has local context data
* Broke out filter and form for re-use
* Fix missing StaticSelect2 import
* Fix missing BOOLEAN_WITH_BLANK_CHOICES import
* Fix class resolution
* Fix field ordering
* Fix PEP8 errors
2019-09-06 11:42:56 -05:00
Jeremy Stretch
2ce0ff505a Post-release version bump 2019-09-04 16:28:58 -04:00
Jeremy Stretch
a4d8b92cb1 Merge pull request #3478 from netbox-community/develop
Release v2.6.3
2019-09-04 16:27:32 -04:00
Jeremy Stretch
143a158e4a Release v2.6.3 2019-09-04 16:23:06 -04:00
Jeremy Stretch
4213454234 Tweak docs for custom scripts 2019-09-04 16:21:21 -04:00
Jeremy Stretch
559beffd24 Add documentation for custom links 2019-08-28 12:39:11 -04:00
Jeremy Stretch
5f4bac6076 Closes #3454: Enable filtering circuits by region 2019-08-28 12:12:27 -04:00
Jeremy Stretch
8ff3d2cbf6 Closes #3456: Enable bulk editing of tag color 2019-08-28 11:56:00 -04:00
Jeremy Stretch
273a9793db Fix typo 2019-08-28 10:51:56 -04:00
Jeremy Stretch
5a911aa5a1 Fixes #3392: Add database index for ObjectChange time 2019-08-28 10:48:19 -04:00
Jeremy Stretch
3078e366e2 Simplify changelog cleanup logic 2019-08-28 10:44:05 -04:00
Jeremy Stretch
22b8a45a71 Add tests for changelog 2019-08-28 10:18:37 -04:00
Jeremy Stretch
4756353fbd Merge pull request #3453 from netbox-community/3452-change-logging
Fixes #3452: Queue deletion ObjectChanges until after response is sent
2019-08-28 09:51:54 -04:00
Jeremy Stretch
3e8799b5c7 Fix script form rendering 2019-08-28 09:20:19 -04:00
Jeremy Stretch
a3d9e633c1 Always include 'commit' option 2019-08-26 17:04:04 -04:00
Jeremy Stretch
6e66f8d68a Fixes #3452: Queue deletion ObjectChanges until after response is sent 2019-08-26 16:52:05 -04:00
Jeremy Stretch
03ac2721bc Merge pull request #3423 from netbox-community/3415-custom-scripts
Add custom scripting
2019-08-26 14:06:22 -04:00
Jeremy Stretch
456621695a Wrap script form inside a panel 2019-08-26 13:53:30 -04:00
Jeremy Stretch
9a9660a765 Fix errant changelog entries when executing a script without committing 2019-08-26 11:59:38 -04:00
Jeremy Stretch
6568653d13 Fix typo in link 2019-08-22 10:51:29 -04:00
Jeremy Stretch
5248fc7c7c Merge pull request #3434 from netbox-community/3428-cache-invalidation
Cache invalidation
2019-08-22 10:49:51 -04:00
Jeremy Stretch
6a8f256a56 Fix typo 2019-08-21 15:46:06 -04:00
John Anderson
46bedc6156 missed a merge conflict resolution 2019-08-21 14:30:07 -04:00
John Anderson
63c3f423c2 Merge branch 'develop' into 3428-cache-invalidation 2019-08-20 17:35:54 -04:00
John Anderson
3d2a738f44 #3428 changelog 2019-08-20 17:27:40 -04:00
John Anderson
f0f1ef2ef2 fix signals update call 2019-08-20 17:20:46 -04:00
John Anderson
c359ac5737 convert update() calls to save() calls 2019-08-20 17:16:00 -04:00
Jeremy Stretch
a4936ad0dd Introduce BaseScript for extending Script without creating a new executable script 2019-08-19 14:40:08 -04:00
Jeremy Stretch
a02ded6b01 Import Django User model automatically when running nbshell 2019-08-19 11:47:50 -04:00
Jeremy Stretch
2d2bb3ec0c Fixes #3421: Fix exception when ordering power connections list by PDU 2019-08-19 11:27:36 -04:00
Jeremy Stretch
eb6e95ae9b Add tests for Script Variables 2019-08-19 10:41:44 -04:00
Jeremy Stretch
f56a0aebdb Closes #3430: Linkify platform field on device view 2019-08-19 09:50:41 -04:00
John Anderson
c54f2e3e40 remove blank line after update call 2019-08-19 02:11:54 -04:00
John Anderson
ade844f7a7 fixes #3428 - caching invalidation issues
Mitgate invalidation issues by using prefetch_related instead of select_related.
Also use invalidated_update instead of just update.
2019-08-19 01:53:39 -04:00
Jeremy Stretch
2929621651 Updated docs for IPNetworkVar and FileVar 2019-08-16 15:31:29 -04:00
Jeremy Stretch
de770faf6a Add FileVar for file uploads 2019-08-16 15:27:58 -04:00
Jeremy Stretch
99394de14e Change fields to field_order 2019-08-15 16:19:25 -04:00
Jeremy Stretch
305d330391 Docs updates 2019-08-15 16:07:15 -04:00
Jeremy Stretch
9c41984f6d Changelog for #3426 2019-08-15 15:31:05 -04:00
Jeremy Stretch
aa858fea03 Merge pull request #3427 from candlerb/candlerb/3426
Improve API error handling when a list is given as a choice value
2019-08-15 15:29:47 -04:00
Brian Candler
6e5d527fec Improve API error handling when a list is given as a choice value
Fixes #3426
2019-08-15 17:16:24 +01:00
Jeremy Stretch
b11d3dde46 Closes #3391: Update Bootstrap CSS to v3.4.1 2019-08-15 11:47:57 -04:00
Jeremy Stretch
3df8ccb92f Fixes #3424: Fix tag coloring for non-linked tags 2019-08-15 11:12:52 -04:00
Jeremy Stretch
0b95cab47b Closes #3386: Add mac_address filter for virtual machines 2019-08-15 11:02:40 -04:00
Jeremy Stretch
cb0dbc0769 Add TextVar for large text entry 2019-08-14 16:20:52 -04:00
Jeremy Stretch
47d60dbb20 Fix table column widths 2019-08-14 15:46:08 -04:00
Jeremy Stretch
f8326ef6df Add markdown rendering for log mesages 2019-08-14 14:38:11 -04:00
Jeremy Stretch
434e656e27 Include stack trace when catching an exception 2019-08-14 14:26:13 -04:00
Jeremy Stretch
8bd1fad7d0 Use TreeNodeChoiceField for MPTT objects 2019-08-14 14:03:11 -04:00
Jeremy Stretch
7f65e009a8 Add convenience functions for loading YAML/JSON data from file 2019-08-14 13:08:21 -04:00
Jeremy Stretch
11e5e1c490 Show script log when an exception occurs 2019-08-14 12:19:36 -04:00
Jeremy Stretch
9c079ead4c Fix notice when form does not require user input 2019-08-14 10:18:25 -04:00
Jeremy Stretch
c562af3a13 Record script execution time 2019-08-14 10:12:30 -04:00
Jeremy Stretch
30e14db881 Tweak form display (cosmetic) 2019-08-14 09:40:23 -04:00
Jeremy Stretch
dab30f50d3 Add IPNetworkVar 2019-08-13 09:48:51 -04:00
Jeremy Stretch
3d6a583ce4 Allow user to override module name 2019-08-13 09:09:12 -04:00
Jeremy Stretch
44fd0ebb2d Meta.fields should be optional 2019-08-12 16:59:09 -04:00
Jeremy Stretch
0d289d660d Add option to commit database changes 2019-08-12 14:28:06 -04:00
Jeremy Stretch
3e75da4307 Implemented run_script() wrapper 2019-08-12 13:51:25 -04:00
Jeremy Stretch
19eb4c510c Move script attributes under a Meta class 2019-08-12 13:16:18 -04:00
Jeremy Stretch
dd4dafa7be Closes #3420: Serial number filter for racks, devices, and inventory items is now case-insensitive 2019-08-12 12:10:36 -04:00
Jeremy Stretch
116c395948 Fixes #3422: Prevent navigation menu from overlapping page content 2019-08-12 11:57:48 -04:00
Jeremy Stretch
ab504439fb Implemented permissions for scripts 2019-08-12 11:39:36 -04:00
Jeremy Stretch
463c636301 Extend example custom script to generate output 2019-08-12 11:13:16 -04:00
Jeremy Stretch
950a09895b BooleanVar cannot be required 2019-08-12 11:13:16 -04:00
Jeremy Stretch
c62a9f1b3f Example script tweak 2019-08-12 11:13:16 -04:00
Jeremy Stretch
3f7f3f88f3 Fix form field ordering 2019-08-12 11:13:16 -04:00
Jeremy Stretch
4fc19742ec Added documentation for custom scripts 2019-08-12 11:13:16 -04:00
Jeremy Stretch
9d054fb345 Add options for script vars; include script output 2019-08-12 11:13:16 -04:00
Jeremy Stretch
a25a27f31f Initial work on custom scripts (#3415) 2019-08-12 11:13:16 -04:00
Jeremy Stretch
f18c3be745 Merge pull request #3412 from netbox-community/3405-bugfix
Move device component creation logic out of Device model
2019-08-08 21:16:11 -04:00
Jeremy Stretch
0516aecb03 Changelog for #3405 2019-08-07 17:49:54 -04:00
Jeremy Stretch
605be30fb2 Add test for device component creation 2019-08-07 17:48:12 -04:00
Jeremy Stretch
86cd044a68 Fixes #3405: Move device component creation logic into template models 2019-08-07 17:47:44 -04:00
Jeremy Stretch
068a0e2257 Removed invalid contact email 2019-08-02 15:02:29 -04:00
Jeremy Stretch
3a2fc43542 Post-release version bump 2019-08-02 10:31:56 -04:00
Jeremy Stretch
7fc60cd667 Merge pull request #3387 from netbox-community/develop
Release v2.6.2
2019-08-02 10:31:34 -04:00
Jeremy Stretch
c90baaa807 Release v2.6.2 2019-08-02 10:29:10 -04:00
Jeremy Stretch
ea9492d4bd Fixes #3384: Maximum and allocated draw fields should be included on power port template creation form 2019-08-02 09:56:02 -04:00
Jeremy Stretch
025f77dcdc Fixes #3385: Fix power panels list when bulk editing power feeds 2019-08-02 09:43:46 -04:00
Jeremy Stretch
eb19b1a39e Changelog for #3367 2019-08-02 09:13:48 -04:00
Jeremy Stretch
25748efd54 Merge pull request #3383 from ragzilla/develop
Closes #3367: Add BNC Front/Rear port types and Coaxial cable type.
2019-08-02 09:10:40 -04:00
Matt Addison
2215a095c8 Closes #3367: Add BNC Front/Rear port types and Coaxial cable type. 2019-08-01 10:33:29 -04:00
Jeremy Stretch
a32d185ff0 Fixes #3018: Components connected via a cable must have an equal number of positions 2019-07-31 10:12:51 -04:00
Jeremy Stretch
ea32853ab3 Fixes #3289: Prevent position from being nullified when moving a device to a new rack 2019-07-30 17:07:58 -04:00
Jeremy Stretch
a6c41e0be5 Changelog for #3368 2019-07-30 16:26:52 -04:00
Jeremy Stretch
f223f9b9c1 Merge pull request #3369 from jlrgraham23/fix-changelog-wording
Indicate when changelog retention configured to be forever.
2019-07-30 16:20:39 -04:00
Daniel Sheppard
bcc7daeac7 Fixes #3370 - Add filter class to VirtualChassis API 2019-07-24 12:22:15 -05:00
Justin L R Graham
890ba3ea94 Indicate when changelog retention configured to be forever. 2019-07-23 13:46:55 -05:00
Jeremy Stretch
cab3c50ae6 Closes #3314: Paginate object changelog entries 2019-07-18 21:40:36 -04:00
Jeremy Stretch
86b6b9bf8b Fixes #3315: Enable filtering devices/interfaces by multiple MAC addresses 2019-07-18 21:21:56 -04:00
Jeremy Stretch
71551893b1 Fixes #3293: Enable filtering device components by multiple device IDs 2019-07-18 20:42:15 -04:00
Jeremy Stretch
0431b296c4 Merge pull request #3325 from lassebm/fix-3324
Fixes #3324: Doc incorrectly states child devices shown as non-racked
2019-07-17 16:27:55 -04:00
Jeremy Stretch
376eae748c Changelog for #3323 2019-07-17 16:25:19 -04:00
Jeremy Stretch
f2a45c5892 Merge pull request #3326 from lassebm/fix-3323
Fixes #3323: Interface Connections view inaccessible with "dcim.view_interface" permission
2019-07-17 16:24:14 -04:00
Jeremy Stretch
88b176ae15 Changelog for #3307 2019-07-17 16:22:30 -04:00
Jeremy Stretch
f1744ef4db Merge pull request #3308 from mmahacek/powerpanel-count
Add Powerpanel count to home page
2019-07-17 16:20:27 -04:00
Jeremy Stretch
892ff0c1ca Changelog for #3342 2019-07-16 11:31:36 -04:00
Jeremy Stretch
647163e2b2 Merge pull request #3337 from robellegate/fix/docs_digitalocean_repo_links
Replacing references to digitalocean org
2019-07-16 10:18:37 -04:00
Jeremy Stretch
8e043b00b8 Closes #3330: Remove .pyc file cleanup step from upgrade script 2019-07-16 10:11:39 -04:00
Jeremy Stretch
154b9e1faf Fixes #3342: Fix cluster delete button permissions reference 2019-07-16 10:07:38 -04:00
Robert Ellegate
30ef4b208c Replacing references to digitalocean org
s/(?<=:\/\/github.com\/)digitalocean(?=\/netbox)/netbox-community/g
2019-07-10 09:23:43 -04:00
Lasse Bang Mikkelsen
6276f5f7b9 Fixes #3323: Interface Connections view inaccessible with "dcim.view_interface" permission 2019-07-04 17:37:28 +02:00
Lasse Bang Mikkelsen
118ec358c0 Fixes #3324: Doc incorrectly states child devices shown as non-racked 2019-07-04 17:28:25 +02:00
Jeremy Stretch
3da9af5a9f Fixes #3317: Fix permissions for ConfigContextBulkDeleteView 2019-07-02 09:39:26 -04:00
mmahacek
ddced4fc2b Add stats.powerpanel_count 2019-06-28 17:04:42 -07:00
mmahacek
7a41b02fdd Add line for PowerPanel count 2019-06-28 17:03:06 -07:00
Jeremy Stretch
6c3c6fba62 Closes #984: Allow ordering circuits by A/Z side 2019-06-27 12:30:17 -04:00
Jeremy Stretch
2bb9464905 Merge pull request #3296 from michaelxniu/patch-1
Create NOTICE file
2019-06-25 14:10:14 -04:00
Michael Niu
821924f57f Create NOTICE file 2019-06-25 13:59:00 -04:00
Jeremy Stretch
74f14c5535 Post-release version bump 2019-06-25 09:44:00 -04:00
Jeremy Stretch
80c8c4c4b2 Merge pull request #3295 from digitalocean/develop
Release v2.6.1
2019-06-25 09:41:29 -04:00
Jeremy Stretch
d219c3ea88 Release v2.6.1 2019-06-25 09:39:30 -04:00
Jeremy Stretch
954ba91c86 Closes #3154: Add virtual_chassis_member device filter 2019-06-24 16:31:21 -04:00
Jeremy Stretch
7effb7e8d4 Fix for #3229 2019-06-24 15:48:49 -04:00
Jeremy Stretch
3fdb655a92 Fixes #3269: Raise validation error when specifying non-existent cable terminationss 2019-06-24 15:42:15 -04:00
Jeremy Stretch
cf770bf40c Closes #3277: Add cable trace buttons for console and power ports 2019-06-24 14:27:34 -04:00
Jeremy Stretch
9f50ced6fc Changelog for #3229 2019-06-24 14:22:03 -04:00
Jeremy Stretch
4dd97eab0c Merge pull request #3232 from hellerve/fix-3229
Filter group by site in rack filter
2019-06-24 14:19:50 -04:00
Jeremy Stretch
5de242fe53 Closes #3281: Hide custom links which render as empty text 2019-06-24 12:20:09 -04:00
Jeremy Stretch
251ba08e09 Fixes #3283: Fix rack group assignment on PowerFeed CSV import 2019-06-24 11:10:35 -04:00
Jeremy Stretch
653770ede9 Fixes #3292: Ignore empty URL query parameters 2019-06-24 11:00:18 -04:00
Jeremy Stretch
70ef6a69ee Fixes #3290: Fix server error when viewing cascaded PDUs 2019-06-24 10:05:21 -04:00
Jeremy Stretch
5a6c928a7c Fixes #3279: Reset the PostgreSQL sequence for Tag and TaggedItem IDs 2019-06-21 17:34:06 -04:00
John Anderson
bd9ef9951b Merge pull request #3284 from cimnine/fix_pwd_protected_redis_cache
Fixes Cacheops with a password protected redis
2019-06-21 16:35:50 -04:00
Jeremy Stretch
c067549f21 Fixes #3275: Fix error when adding power outlets to a device type 2019-06-21 16:24:12 -04:00
Christian Mäder
94ca3abefc Fixes Cacheops with a password protected redis
As per the [`README.rst`][1] of `django-cacheops`, if a password is
added to the connection string, it must be in the form
`redis://:password@host:port/db`. Notice the colon, which was missing
from the implementation in [`settings.py`][2].

[1]: 8ad970d55a/README.rst
[2]: 86d5b48007/netbox/netbox/settings.py (L349)
2019-06-21 22:23:10 +02:00
Jeremy Stretch
86d5b48007 Post-release version bump 2019-06-20 17:01:21 -04:00
Jeremy Stretch
2b7e8c4b9f Merge pull request #3271 from digitalocean/develop
Release v2.6.0
2019-06-20 16:59:31 -04:00
Jeremy Stretch
f888d50a15 Release v2.6.0 2019-06-20 16:55:43 -04:00
Jeremy Stretch
dfcd2c247d Closes #3264: Annotate changelog retention time on UI 2019-06-20 14:05:53 -04:00
Jeremy Stretch
1c54d7ed55 Hide color block if cable color is not defined 2019-06-20 14:00:45 -04:00
Jeremy Stretch
7cb618df5e Fixes #3176: Add cable trace button for console server ports and power outlets 2019-06-20 13:58:32 -04:00
Jeremy Stretch
6778d1398f Added changelog noting #3239 has been fixed 2019-06-20 13:26:15 -04:00
Jeremy Stretch
1dd3e9dab1 Merge pull request #3270 from digitalocean/develop-2.6
Merge develop-2.6
2019-06-20 13:20:42 -04:00
Jeremy Stretch
99f6b3a3bd Merged develop 2019-06-20 13:10:45 -04:00
Jeremy Stretch
b626387b38 Bumped dependencies to latest releases 2019-06-20 10:06:39 -04:00
Jeremy Stretch
dd554ee7b5 Updated django-cors-headers to v3.0.2 2019-06-20 09:48:34 -04:00
Jeremy Stretch
fe28e5befe Cleaned up changelog 2019-06-20 09:31:01 -04:00
Jeremy Stretch
21856c6f0c Merge pull request #3268 from digitalocean/power-utilization-tweaks
Cleaned up logic for calculating power draw
2019-06-20 09:09:06 -04:00
John Anderson
b745401817 prometheus docs updates 2019-06-20 00:04:44 -04:00
John Anderson
625a09785a minor tweaks to error handling to allow for defaulted values from pre v2.6 data 2019-06-19 23:47:48 -04:00
Jeremy Stretch
1a5aaf54dd Cleaned up logic for calculating power draw 2019-06-19 10:51:53 -04:00
John Anderson
ce9f7a8ddc closes #3161 - prometheus env var docs and set prometheus to not be enabled by default 2019-06-17 16:38:05 -04:00
Jeremy Stretch
9747cae773 Fix power outlet connection link 2019-06-17 16:32:21 -04:00
Jeremy Stretch
948286aa32 Fixes #3258: Exception raised when creating/viewing a circuit with a non-connected termination 2019-06-17 16:27:26 -04:00
Jeremy Stretch
8da660f5da Fixes #3231: Fixed cosmetic error indicating a missing schema migration 2019-06-17 15:30:23 -04:00
Jeremy Stretch
b884341d73 Update migration for tweak to available_power 2019-06-17 15:23:37 -04:00
Jeremy Stretch
c9afe96324 Fix power utilization calculation for three-phase feeds 2019-06-17 15:16:52 -04:00
Jeremy Stretch
d529ebc172 Rename power_factor to max_utilization 2019-06-17 14:52:11 -04:00
Jeremy Stretch
d25dd52ec9 Compressed migrations 2019-06-17 14:41:40 -04:00
Jeremy Stretch
97a5f3ea0e Merge pull request #3262 from digitalocean/feature/524-power-utilization-graphs
#524 - Added power utilization graphs to power feeds, devices, and racks
2019-06-17 14:38:12 -04:00
John Anderson
2eeffce924 #524 - Added power utilization graphs to power feeds, devices, and racks 2019-06-16 06:11:32 -04:00
Jeremy Stretch
438b01815a Fix test for tags view 2019-06-05 15:05:35 -04:00
Jeremy Stretch
8b3ec625f6 Add missing PermissionRequiredMixin to TagListView 2019-06-05 14:47:43 -04:00
Jeremy Stretch
50cbfb9360 Fixed merge conflict in requirements.txt 2019-06-05 14:47:20 -04:00
Jeremy Stretch
90f7122fde Closes #3241: Correct terminology in inventory items table header 2019-06-05 12:10:33 -04:00
hellerve
e89343e100 dcim: filter group by site in rack filter (fixes #3229) 2019-06-02 14:26:28 +02:00
Jeremy Stretch
3bb3b85fa2 Merged v2.5.13 2019-05-31 21:37:41 -04:00
Jeremy Stretch
f9dba8a75c Post-release version bump 2019-05-31 09:56:50 -04:00
Jeremy Stretch
1a97a1c9d2 Merge pull request #3230 from digitalocean/develop
Release v2.5.13
2019-05-31 09:54:46 -04:00
Jeremy Stretch
893e327ac6 Release v2.5.13 2019-05-31 09:49:53 -04:00
dansheps
814c50f461 Fix #3228 - UrlEncode full path for next if not on logon page
Include the full path for the ?next= variable in login links if we are not on the logon page.
Additionally include next for post requests that have the next variable set (will only come from the login page itself generally)
2019-05-30 12:01:41 -05:00
dansheps
1958c0b118 Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	netbox/utilities/middleware.py
2019-05-30 10:59:25 -05:00
dansheps
a11b33d214 Fix #3228 - Send full path info instead of just path info and urlencode said path info 2019-05-30 10:58:39 -05:00
dansheps
7d053f8ba4 Fix #3228 - Send full path info instead of just path info and urlencode said path info 2019-05-30 10:54:29 -05:00
Jeremy Stretch
1e1aba73ef Remove request.user assertion from ObjectChangeMiddleware 2019-05-30 10:32:09 -04:00
Jeremy Stretch
b9b009c0b5 Fixes #3227: Fix exception when deleting a circuit with a termination(s) 2019-05-29 17:17:06 -04:00
Jeremy Stretch
a6ff6505c6 Closes #3151: Add inventory item count to manufacturers list 2019-05-29 15:20:36 -04:00
Jeremy Stretch
823257ca72 Closes #3185: Improve performance for custom field access within templates 2019-05-29 15:04:57 -04:00
Jeremy Stretch
0804c1acbd Fixed test from #3211 follow-up work 2019-05-29 10:51:49 -04:00
Jeremy Stretch
28facca291 Changelog & grammar tweak for #3211 2019-05-29 10:33:29 -04:00
Jeremy Stretch
a7ca49c44d Merge pull request #3222 from hellerve/tmp
Fix error message on trying to delete protected models
2019-05-29 10:24:28 -04:00
Jeremy Stretch
5639fc9791 Merge pull request #3195 from TakeMeNL/feature/3156
Closes #3156: Add site link to rack reservations overview
2019-05-29 10:19:10 -04:00
Jeremy Stretch
8b00513175 Changelog for #3031 2019-05-29 10:10:07 -04:00
Jeremy Stretch
00ffa358b2 Merge pull request #3197 from KhaledTo/bug/3031
Fixes #3031: Select2 creates multiple tags for tags with spaces
2019-05-29 10:08:59 -04:00
TakeMeNL
1ff7e1149c Closes #3156: Add site link to rack reservations overview 2019-05-29 16:08:24 +02:00
hellerve
2c7bad9fff utilities: move protectederror handling to modelviewset 2019-05-28 21:11:23 +02:00
Jeremy Stretch
c4f481d705 Bump DRF to 3.9.1 to address WS-2019-0037 2019-05-28 14:21:36 -04:00
Jeremy Stretch
99a3a216c3 Merge pull request #3216 from hellerve/fix-3168
Fix 3168: Update to new path syntax
2019-05-28 13:16:08 -04:00
Jeremy Stretch
87f5dd05f5 Fixes #3223: Fix filtering devices by "has power outlets" 2019-05-28 13:10:54 -04:00
hellerve
cc87d99017 all: fix error message on trying to delete protected models (references #3211) 2019-05-28 17:31:02 +02:00
hellerve
1366730a3f netbox urls: move to re_path as suggested by @jeremystretch 2019-05-27 22:41:10 +02:00
Jeremy Stretch
ab07f721cf Changelog for #3204 & #3207 2019-05-27 15:40:17 -04:00
Jeremy Stretch
3c83a18291 Merge pull request #3205 from ananace/fix-interface-connection
Fixes #3204: Fix connecting cables to interfaces
2019-05-27 15:38:36 -04:00
Jeremy Stretch
8bbb3031e5 Merge pull request #3207 from ananace/fix-connecting-rear
Fixes #3206: Unable to connect to rear ports
2019-05-27 15:31:49 -04:00
Jeremy Stretch
473dafc2c8 Changelog for #3184 2019-05-27 15:03:00 -04:00
Jeremy Stretch
38d5a8fdc0 Merge pull request #3199 from candlerb/candlerb/3184
Add grey border around color-block
2019-05-27 15:00:48 -04:00
hellerve
b114b9d396 utilities: add converters module and use for json/yaml url 2019-05-26 14:56:01 +02:00
hellerve
f9cd89a4a4 urls: fix 3168 by changing url to path 2019-05-26 14:56:00 +02:00
Alexander Olofsson
7ba9675f02 Fixes #3206: Unable to connect to rear ports 2019-05-22 19:58:26 +02:00
Alexander Olofsson
d0e7b052a8 Fixes #3204: Fix connecting cables to interfaces 2019-05-22 19:00:13 +02:00
Brian Candler
4313a717c4 Add grey border around color-block
Fixes #3184
2019-05-20 21:06:53 +01:00
Khaled BEN ABDALLAH
cbace6f831 Fixes #3031: Select2 creates multiple tags for tags with spaces 2019-05-18 22:43:47 +02:00
Jeremy Stretch
edabc8eee9 Closes #3138: Add 2.5GE and 5GE interface form factors 2019-05-16 20:49:00 -04:00
Jeremy Stretch
9b47e57e8e Closes #3183: Enable bulk deletion of sites 2019-05-16 20:24:55 -04:00
Jeremy Stretch
2f32488c25 Fixes #3190: Fix custom field rendering for Jinja2 export templates 2019-05-16 19:45:36 -04:00
Jeremy Stretch
62d497dd0b Closes #3186: Add interface name filter for IP addresses 2019-05-14 19:03:03 -04:00
Jeremy Stretch
e19feb92ea Move TenancyFilterForm to tenancy.forms 2019-05-09 14:36:18 -04:00
Jeremy Stretch
fbde6282b2 Cleanup from #2931 2019-05-09 14:32:49 -04:00
Jeremy Stretch
7895ccfae1 Merge pull request #2931 from DanSheps/2813-addtenantgroupfilter
Closes #2813: Add Filter and View on Lists for TenantGroup
2019-05-09 13:48:46 -04:00
Jeremy Stretch
c24fb8df84 Merge pull request #3165 from digitalocean/3038-filtering-improvements
Closes #3038: Filtering improvements
2019-05-08 21:13:42 -04:00
Jeremy Stretch
53b5fed8ae Tweak docs to indicate deprecation of id__in filter 2019-05-08 21:10:49 -04:00
Jeremy Stretch
dfffd1ea94 Restore id__in filters to retain backward compatability until v2.7 2019-05-08 21:08:35 -04:00
Jeremy Stretch
ffa34c6133 Updated documentation 2019-05-07 14:23:02 -04:00
Jeremy Stretch
8e8c9822ea Correct Device.position filter 2019-05-07 14:12:56 -04:00
Jeremy Stretch
205adeb2e9 Remove NullableCharFieldFilter; add missing filter fields 2019-05-07 13:59:21 -04:00
Jeremy Stretch
3d616baf75 Remove NumericInFilter and id__in filters 2019-05-07 13:07:18 -04:00
Jeremy Stretch
6cb5173e27 Update query filters to OR multiple values 2019-05-07 12:25:33 -04:00
Daniel Sheppard
dfd4a712c9 Merge pull request #3158 from tb-killa/3150
Fixes: #3150- Formatting of cable length in cable trace
2019-05-06 11:54:28 -05:00
Daniel Sheppard
b97339017b Update CHANGELOG.md 2019-05-06 11:54:16 -05:00
Oli
1b862045e3 Formatting of cable length in cable trace 2019-05-06 15:36:44 +02:00
Jeremy Stretch
244c07e5f7 Closes #3085: Catch all exceptions during export template rendering 2019-05-02 15:36:51 -04:00
Jeremy Stretch
eb41bc66a4 Merge pull request #3142 from austin987/upgrade-cwd
upgrade.sh: make sure we are in the right directory
2019-05-02 14:51:37 -04:00
Austin English
01c5d9e909 upgrade.sh: make sure we are in the right directory 2019-05-02 13:02:53 -05:00
Jeremy Stretch
a8c57313d3 Fixes #3140: Add bulk edit capability for power outlets and console server ports 2019-05-02 13:56:30 -04:00
Jeremy Stretch
5f5e4ce1a1 Changelog for #3132 2019-05-02 11:41:37 -04:00
Jeremy Stretch
d50acb39dd Merge pull request #3133 from shanemadden/cable_circuit_endpoint_choice
Fixes: #3132: Add circuittermination to the choices API for cable endpoints
2019-05-02 11:39:18 -04:00
Jeremy Stretch
25c8007b66 Fixes #3137: Add power_port and feed_leg fields to power outlet creation form 2019-05-02 10:12:27 -04:00
Jeremy Stretch
d4db04c649 Fixes #3136: Add power draw fields to power port creation form 2019-05-02 10:02:02 -04:00
John Anderson
250f310a98 fixes #3134 - remove component buttons from power utilization table 2019-05-01 21:48:35 -04:00
Shane Madden
ee4a3bcb02 Add circuittermination as a choice for cable endpoint types, which is not in the choices API for cable termination types but is accepted by the application as a valid endpoint for cables 2019-05-01 13:47:52 -06:00
Jeremy Stretch
d4d355dce6 Fixes #3130: Fix exception when creating a new power outlet 2019-05-01 12:02:18 -04:00
Jeremy Stretch
346b00e215 Merge branch 'develop' into develop-2.6 2019-05-01 11:53:44 -04:00
Jeremy Stretch
ceeac9bae3 Merge pull request #3131 from digitalocean/develop
Release v2.5.12
2019-05-01 11:10:43 -04:00
Jeremy Stretch
49446ffb74 Post-release version bump 2019-05-01 11:09:11 -04:00
Jeremy Stretch
5487ab40af Release v2.5.12 2019-05-01 11:08:32 -04:00
Jeremy Stretch
5a8ba159f2 Fixes #3127: Fix natural ordering of device components 2019-04-30 13:25:37 -04:00
Jeremy Stretch
cb93303f56 Fixes #3126: Incorrect calculation of PowerFeed available power 2019-04-30 12:38:06 -04:00
Jeremy Stretch
088903218d Fixes #3125: Fix exception when viewing PDUs 2019-04-30 12:24:53 -04:00
Jeremy Stretch
73927d2d56 Changelog for #3123 2019-04-30 12:10:00 -04:00
John Anderson
f9a74b68c1 Merge pull request #3124 from larsweiler/develop-2.6-metrics
Exclude /metrics from LOGIN_REQUIRED
2019-04-30 11:08:55 -04:00
dansheps
22e5834d8b Remove tenant group from ipam table 2019-04-30 10:06:27 -05:00
Lars Weiler
2a2026a2cc Forgot the additional brackets for a tuple 2019-04-30 17:04:21 +02:00
dansheps
63b71d43da Merge branch 'develop' of https://github.com/digitalocean/netbox into 2813-addtenantgroupfilter 2019-04-30 10:01:29 -05:00
Lars Weiler
560c8d6f01 More elegant path checking 2019-04-30 16:54:23 +02:00
Lars Weiler
4c5603e6ff Fix PEP 8 errors 2019-04-30 16:47:38 +02:00
Lars Weiler
99f0e7b939 Exclude /metrics from LOGIN_REQUIRED 2019-04-30 16:09:10 +02:00
Jeremy Stretch
7b5c1964b9 Fix broken link 2019-04-29 16:55:17 -04:00
Jeremy Stretch
b7a5afa797 Revert previous change 2019-04-29 16:44:13 -04:00
Jeremy Stretch
66f90f46de Fix mkdocs 2019-04-29 16:37:32 -04:00
Jeremy Stretch
4d2ac1ca53 Release v2.6-beta1 2019-04-29 15:36:21 -04:00
Jeremy Stretch
05a21369ae Bump django-cors-headers version 2019-04-29 15:27:14 -04:00
Jeremy Stretch
9b404facab Cleaned up changelog 2019-04-29 15:24:52 -04:00
Jeremy Stretch
f31d6c55be Fix erroneous merge conflict resolution from v2.5.11 2019-04-29 14:57:28 -04:00
Jeremy Stretch
37c2c4b4a2 Merge v2.5.11 2019-04-29 14:40:18 -04:00
Jeremy Stretch
d5dcb77d99 Post-release version bump 2019-04-29 14:27:22 -04:00
Jeremy Stretch
2b93510c45 Merge pull request #3121 from digitalocean/develop
Release v2.5.11
2019-04-29 14:25:37 -04:00
Jeremy Stretch
9717c6f825 Add a note about Django 2.2 2019-04-29 14:22:47 -04:00
Jeremy Stretch
92c227d103 Release v2.5.11 2019-04-29 14:21:10 -04:00
Jeremy Stretch
1491222642 Fixes #3072: Preserve multiselect filter values when updating per-page count for list views 2019-04-29 12:54:03 -04:00
Jeremy Stretch
39fceeb455 Add device field on cable search form (#3023) 2019-04-29 11:29:07 -04:00
Jeremy Stretch
3562b5552b Fixes #3118: Disable last_login update on login when maintenance mode is enabled 2019-04-29 11:04:32 -04:00
Jeremy Stretch
6d778f686d Closes #3023: Add support for filtering cables by connected device 2019-04-29 10:07:08 -04:00
Jeremy Stretch
245a97176a Closes #2986: Replace DeviceComponentManager with NaturalOrderingManager 2019-04-26 22:23:28 -04:00
Jeremy Stretch
ca56871aaa Changelog & CSS fix for #3070 2019-04-26 17:06:54 -04:00
Jeremy Stretch
bd4086cb50 Merge pull request #3103 from clercrobin/add_decommissioning
Fixes : #3070 Add the decommissioning status for devices
2019-04-26 17:04:36 -04:00
Jeremy Stretch
d8c9b1af27 Fixes #3116: Fix tagged_items count in tags API endpoint 2019-04-26 16:54:13 -04:00
Jeremy Stretch
19d2850b29 Fixes #3112: Fix ordering of interface connections list by termination B name/device 2019-04-26 16:41:01 -04:00
Jeremy Stretch
2c730b08e4 Fix PowerPort connected_endpoint filtering, ordering 2019-04-25 15:06:48 -04:00
Jeremy Stretch
e1bca52d57 Fix regex for IPAddress.dns_name (but see #3106) 2019-04-25 14:49:52 -04:00
Jeremy Stretch
06245f6422 Promote django-rq to a required dependency 2019-04-25 14:25:16 -04:00
John Anderson
f057a2c016 closes #3104 - add support for exposing prometheus metrics 2019-04-25 01:09:19 -04:00
rclerc
f4636537ad Add the decommissioning status for devices 2019-04-24 09:14:29 +02:00
Jeremy Stretch
a026ec45b8 Closes #166: Add dns_name to IPAddress 2019-04-22 18:10:28 -04:00
Jeremy Stretch
695a07daf4 Clean up settings.py and restrict import of LDAP parameters 2019-04-22 16:33:28 -04:00
Jeremy Stretch
c2d0e8fd95 Cleanup from earlier work on caching 2019-04-22 14:49:31 -04:00
Jeremy Stretch
1be5cf8184 Fix pagination logic for detecting QuerySets 2019-04-22 11:09:12 -04:00
Jeremy Stretch
f4aec1e7d0 Change VLAN view columns to a 4/8 split 2019-04-22 07:47:16 -04:00
Jeremy Stretch
e4c06700bb Closes #3094: Remove NullsFirstQuerySet 2019-04-19 20:59:07 -04:00
Jeremy Stretch
46b3512c45 Remove extraneous imports 2019-04-19 16:58:39 -04:00
Jeremy Stretch
017a5011ec Added '*_count' fields for child objects 2019-04-19 16:50:42 -04:00
Jeremy Stretch
f4bbdf30e8 Implement get_subquery() for annotation of child object counts; Rename dcim.Site 'count_*' fields 2019-04-19 16:09:22 -04:00
Jeremy Stretch
7d41a9ccdb Changelog cleanup 2019-04-19 15:28:08 -04:00
Jeremy Stretch
074d0349a1 Increase length of CustomLink text and url fields 2019-04-19 14:58:55 -04:00
Jeremy Stretch
6ab56c3978 Misc cleanup 2019-04-19 14:56:40 -04:00
Jeremy Stretch
5303d2c16d Add device types and power feeds to home page 2019-04-19 14:27:19 -04:00
Jeremy Stretch
30da4594cd Improved enforcement of view permissions for home page 2019-04-19 14:16:55 -04:00
Jeremy Stretch
92567137a5 PowerFeed list improvements 2019-04-19 13:43:09 -04:00
Jeremy Stretch
7b6c104768 Add power_panel_id field to PowerFeedFilterForm 2019-04-19 13:17:43 -04:00
John Anderson
d27d347d21 changelog and docs updates for 2.6 2019-04-19 02:32:50 -04:00
John Anderson
16b4ffa3fa Merge pull request #3080 from digitalocean/2647-cacheops
change cacheing to use cacheops
2019-04-19 01:41:59 -04:00
John Anderson
8b75969d1d fix typo in requirements 2019-04-18 14:39:12 -04:00
John Anderson
a5e1088f1f fixes #2621 - deletion issue in the changelog middleware 2019-04-18 14:37:58 -04:00
Jeremy Stretch
92a450e59c Improve the logic for gathering models from all apps 2019-04-17 14:33:26 -04:00
Jeremy Stretch
2f3c39295c Clean up, update Webhook models 2019-04-17 14:19:57 -04:00
Jeremy Stretch
000fde25c6 Add PowerFeeds to global search 2019-04-17 14:06:45 -04:00
Jeremy Stretch
cd3924520d Clean up limit_to for ForeignKeys referencing ContentType 2019-04-17 13:36:05 -04:00
John Anderson
c7778db7f2 fix timeout expression 2019-04-17 12:38:54 -04:00
John Anderson
2580b026fe change cacheing to use cacheops 2019-04-17 12:29:21 -04:00
Jeremy Stretch
eb86053a53 Merge pull request #3078 from digitalocean/3077-nested-api-writes
Enable dictionary specification of related objects in API
2019-04-17 11:25:40 -04:00
Jeremy Stretch
fd4c5031c7 Add test for dict_to_filter_params 2019-04-17 11:19:59 -04:00
Jeremy Stretch
655e48823a Merge branch 'develop-2.6' into 3077-nested-api-writes 2019-04-17 10:57:01 -04:00
Jeremy Stretch
a108807534 Add tests for WritableNestedSerializer 2019-04-17 10:54:50 -04:00
Jeremy Stretch
dbe0e9506d Cleaned up CHANGELOG 2019-04-16 21:12:16 -04:00
Jeremy Stretch
75b4ba2c3a Removed tags from the admin UI 2019-04-16 21:00:29 -04:00
Jeremy Stretch
de7207de55 Enable dictionary specification of related objects in API 2019-04-16 18:02:52 -04:00
Jeremy Stretch
bf2f314cd4 Remove run_validators() override (fixed upstream) 2019-04-16 15:35:15 -04:00
Jeremy Stretch
6034265dfd Add limit_choices_to to CustomLink.content_type field 2019-04-15 21:53:22 -04:00
Jeremy Stretch
48fe5470d2 Changelog for #969 2019-04-15 21:49:16 -04:00
Jeremy Stretch
2b2de8f8a5 Merge pull request #3074 from digitalocean/969-custom-links
969 custom links
2019-04-15 21:43:17 -04:00
Jeremy Stretch
dd58e78fde Add custom links to templates 2019-04-15 21:38:04 -04:00
Jeremy Stretch
2ec7ac1ea3 Custom link cleanup 2019-04-15 21:29:02 -04:00
Jeremy Stretch
f342a37362 Fix RelatedObjectDoesNotExist when adding a new PowerFeed 2019-04-15 17:55:50 -04:00
Jeremy Stretch
a411e32a9f Changelog for #2647 2019-04-15 17:49:50 -04:00
Jeremy Stretch
1877afc760 Merge pull request #3069 from digitalocean/2647-caching
intial work on #2647 - caching
2019-04-15 17:16:10 -04:00
Jeremy Stretch
6f055792df Corrected typo 2019-04-15 17:14:24 -04:00
Jeremy Stretch
4536754b20 Initial work on #969: Custom links 2019-04-15 17:12:41 -04:00
John Anderson
351736cc6d Merge branch 'develop-2.6' into 2647-caching 2019-04-15 14:46:04 -04:00
John Anderson
4723ddb5ce move caching to views 2019-04-15 14:41:04 -04:00
Jeremy Stretch
20670c546d Updated dependencies for v2.6 2019-04-15 13:14:48 -04:00
John Anderson
cdff29c7d5 add redis to travis builds 2019-04-15 04:16:22 -04:00
John Anderson
c11abbe8ca pep8 and postgres backend 2019-04-15 04:07:52 -04:00
John Anderson
f0505477b8 intial work on #2647 - caching 2019-04-15 03:55:33 -04:00
Jeremy Stretch
c484b27a35 Merge pull request #3068 from digitalocean/1863-api-child-counts
Add child objects counts to API serializers
2019-04-12 17:25:27 -04:00
Jeremy Stretch
04c1945abc Changelog for #1863 2019-04-12 17:21:04 -04:00
Jeremy Stretch
ad4d23fa20 Replace distinct annotations with subqueries for much better performance 2019-04-12 17:18:04 -04:00
Jeremy Stretch
a46b43bff6 Added child counts to API serializers (WIP) 2019-04-12 17:07:56 -04:00
Jeremy Stretch
b1c160f9d4 Merge pull request #3067 from digitalocean/2920-interface-type
Rename Interface.form_factor to Interface.type
2019-04-12 15:36:51 -04:00
Jeremy Stretch
533520ec1f Rename Interface type (classification) filter to kind 2019-04-12 14:09:03 -04:00
Jeremy Stretch
4acd842237 Maintain backward-compatibile support for Interface.form_factor until v2.7 2019-04-12 13:57:33 -04:00
Jeremy Stretch
778d56ac12 Update 'form_factor' in docs 2019-04-12 13:46:37 -04:00
Jeremy Stretch
1a2c9e3bba Rename form_factor to type on dcim.Interface 2019-04-12 13:42:56 -04:00
Jeremy Stretch
067c788df7 Add changelog tabs for power panel, power feed & clean up nav links 2019-04-12 10:16:34 -04:00
Jeremy Stretch
78c90ba24b Changelog for #54 2019-04-12 10:00:43 -04:00
Jeremy Stretch
573af6a236 Merge pull request #3066 from digitalocean/323-view-permissions
Closes #323
2019-04-12 09:33:48 -04:00
Jeremy Stretch
b29944d5d7 Check view permissions for object navigation tabs 2019-04-12 09:29:36 -04:00
Jeremy Stretch
dfa26cc5e2 Hide non-viewable object types on home page 2019-04-12 09:18:05 -04:00
Jeremy Stretch
8c5d544734 Changelog for #323 2019-04-12 09:07:22 -04:00
Jeremy Stretch
df0686a1bd Implement custom auth backend and EXEMPT_VIEW_PERMISSIONS setting 2019-04-11 22:01:26 -04:00
Jeremy Stretch
3e818cde69 Disable navigation links for objects user does not have permission to view 2019-04-11 21:08:40 -04:00
Jeremy Stretch
43a569d18a Enforce view permissions for API views 2019-04-11 17:40:46 -04:00
Jeremy Stretch
e710ccb0e6 Enforce view permissions for UI views 2019-04-11 17:27:38 -04:00
Jeremy Stretch
ea6815b9bb Merge pull request #3061 from digitalocean/54-power-modeling
#54: Power modeling
2019-04-11 15:02:07 -04:00
Jeremy Stretch
3b06251720 Added tests for power panels, power feeds 2019-04-11 12:29:43 -04:00
Jeremy Stretch
bfa07aec39 Fix tests 2019-04-11 11:37:44 -04:00
Jeremy Stretch
05c19af2a3 Misc cleanup 2019-04-11 10:49:43 -04:00
Jeremy Stretch
b22fd2bc44 Rename max_utilization to power_factor 2019-04-10 16:36:38 -04:00
Jeremy Stretch
ac3e899f5e First stab at power utilization tracking 2019-04-10 16:32:13 -04:00
Jeremy Stretch
3d5f85c0ca Add associatiton from power outlet to power port/phase 2019-04-10 14:16:16 -04:00
Jeremy Stretch
0ddd71fc36 Include draw numbers of connected power ports 2019-04-10 13:05:51 -04:00
dansheps
6fa54bed73 Fix PEP8 Errors 2019-04-10 08:42:27 -05:00
dansheps
6e8e6809f3 Move Filter and Form to new file, update all files 2019-04-10 08:37:12 -05:00
Jeremy Stretch
8230ea1c83 Add max/allocated current draw fields to PowerPort 2019-04-09 17:32:04 -04:00
dansheps
a91a79681f Merge branch 'develop' of https://github.com/digitalocean/netbox into 2813-addtenantgroupfilter 2019-04-09 15:57:22 -05:00
Jeremy Stretch
c1127148e2 Provide individual views for each type of cable connection 2019-04-09 16:49:04 -04:00
Jeremy Stretch
ef867e789d Finish power outlet/feed connection forms 2019-04-09 15:37:31 -04:00
Jeremy Stretch
cd655d289b Fix CSV import forms 2019-04-09 14:55:17 -04:00
Jeremy Stretch
f1d1e8b537 Update migrations 2019-04-09 14:22:45 -04:00
Jeremy Stretch
71b674d11a Merge branch 'develop-2.6' into 54-power-modeling 2019-04-09 14:12:45 -04:00
Jeremy Stretch
4f9b666eee Merge branch 'develop' into develop-2.6 2019-04-09 14:11:26 -04:00
Jeremy Stretch
8d79353d9b Post-release version bump 2019-04-08 14:30:06 -04:00
Jeremy Stretch
f3fffc6161 Merge pull request #3053 from digitalocean/develop
Release v2.5.10
2019-04-08 14:26:27 -04:00
Jeremy Stretch
a8d20e13a8 Release v2.5.10 2019-04-08 14:19:37 -04:00
Jeremy Stretch
9a91bdbdb2 Fixes #2937: Redirect to list view after editing an object from list view 2019-04-08 14:10:55 -04:00
Jeremy Stretch
6f8591f769 Closes #3052: Add Jinja2 support for export templates 2019-04-08 12:20:24 -04:00
dansheps
7f6d79362e Fix virtualization test and add to changelog 2019-04-05 09:47:38 -05:00
Daniel Sheppard
c032413201 Remove unneeded import from testing. 2019-04-05 09:39:04 -05:00
dansheps
e556c78599 Fixes #3047: Fix exception string for invalid MAC Address format 2019-04-05 09:36:56 -05:00
Jeremy Stretch
1b389d662b Fixes #3046: Fix exception at reports API endpoint 2019-04-04 17:34:36 -04:00
Jeremy Stretch
090efde21a Fixes #3044: Ignore site/rack fields when connecting a new cable via device search 2019-04-04 16:19:20 -04:00
Jeremy Stretch
74c03e3295 Fixes #3036: DCIM interfaces API endpoint should not include VM interfaces 2019-04-04 15:07:41 -04:00
Jeremy Stretch
858be6d216 Fixes #3039: Fix exception when retrieving change object for a component template via API 2019-04-04 15:00:59 -04:00
Jeremy Stretch
1e160fd9e9 Fixes #3041: Fix form widget for bulk cable label update 2019-04-04 12:43:14 -04:00
Jeremy Stretch
4884f3ac77 Fix column links 2019-04-02 14:49:40 -04:00
Jeremy Stretch
0e5dff212e Fix rack elevation display 2019-04-02 13:50:57 -04:00
Jeremy Stretch
e0085ec819 Fixed table columns 2019-04-02 13:37:53 -04:00
Jeremy Stretch
07bfd7c8e5 Merge branch 'develop-2.6' into 54-power-modeling 2019-04-02 13:15:40 -04:00
Jeremy Stretch
c8cccc30d1 Merge branch 'develop' into develop-2.6 2019-04-02 13:12:34 -04:00
Jeremy Stretch
7c6d2a6281 Post-release version bump 2019-04-02 12:37:39 -04:00
Jeremy Stretch
fdf168934e Merge pull request #3034 from digitalocean/develop
Release v2.5.9
2019-04-02 12:34:34 -04:00
Jeremy Stretch
738a20ad34 Release v2.5.9 2019-04-02 11:54:00 -04:00
Jeremy Stretch
3602d5a84c Fixes #3032: Save assigned tags when creating a new secret 2019-04-02 11:42:49 -04:00
Jeremy Stretch
d23ca041cf Ensure fallback to default serializer when attempting to load nested serializer 2019-04-02 11:17:14 -04:00
Jeremy Stretch
110387e81b Fixes #3019: Fix tag population when running NetBox within a path 2019-04-02 11:05:16 -04:00
Jeremy Stretch
498f132cad Fixes #3027: Ignore empty local context data when rendering config contexts 2019-03-28 10:16:28 -04:00
Jeremy Stretch
dff3165402 Fixes #3026: Tweak prefix/IP filter forms to filter using VRF ID rather than route distinguisher 2019-03-28 10:06:25 -04:00
Jeremy Stretch
3f5f75c71f Fixes #3001: Fix API representation of ObjectChange action and add changed_object_type 2019-03-28 09:57:26 -04:00
John Anderson
2e1887eb0e implements #3025 - Add request ID to outbound webhook requests 2019-03-24 15:35:42 -04:00
John Anderson
2170eedf08 implements #2933 - username in webhooks 2019-03-24 15:31:12 -04:00
Jeremy Stretch
a208cd156d Template and table polish 2019-03-22 21:58:45 -04:00
Jeremy Stretch
3acc8ca3ab Fixes #3022: Add missing cable termination types to DCIM _choices endpoint 2019-03-22 16:26:56 -04:00
Jeremy Stretch
fc76c8eb0f FieldChoicesViewSet should infer field choices from serializer, not model 2019-03-22 16:24:53 -04:00
Jeremy Stretch
681e20133a Further work on power feed modeling 2019-03-21 17:47:43 -04:00
Jeremy Stretch
7d1ee2e94e Changelog for #3011 2019-03-19 10:34:07 -04:00
Jeremy Stretch
3cea92384c Merge pull request #3012 from ajknv/develop
Add support for configuring use of an SSL connection to Redis.
2019-03-19 10:28:01 -04:00
Jeremy Stretch
6ba3d611af Merge pull request #3015 from DanSheps/3014-fix-vm-roles
Changes vm_role from "true" to "True" in virtualization form
2019-03-19 10:24:59 -04:00
Jeremy Stretch
f6345b9a5d Fixes #2998: Limit device query to non-racked devices if no rack selected when creating a cable 2019-03-19 10:22:52 -04:00
Jeremy Stretch
044f7395bb Closes #2995: Added powerbox to community SDK list 2019-03-19 09:48:16 -04:00
Alexander Kinneer
8bda6be65a Merge branch 'develop' of https://github.com/ajknv/netbox into develop 2019-03-18 11:28:48 -05:00
Alexander Kinneer
e544705256 Add support for configuring use of an SSL connection to Redis.
Requires a build or release of django-rq containing
44f3fdd7cb
2019-03-18 11:26:37 -05:00
Alexander Kinneer
f88099eb3b Add documentation for new Redis SSL configuration parameter. 2019-03-18 11:15:40 -05:00
dansheps
5f40be4bd5 Changes vm_role from "true" to "True" in virtualization form 2019-03-18 09:22:36 -05:00
John Anderson
1bf04f2e30 fixes #2936 - device role selection showing duplicate first entry 2019-03-17 01:24:54 -04:00
Alexander Kinneer
7edad4eba9 Add support for configuring use of an SSL connection to Redis.
Requires a build or release of django-rq containing
44f3fdd7cb
2019-03-15 10:35:03 -05:00
Jeremy Stretch
dd7249d2ef Merge pull request #2996 from DanSheps/2207-fix_deterministic_ordering_of_interfaces
Fixes #2207
2019-03-13 16:15:34 -04:00
dansheps
aab84ba6f3 Change ID to PK 2019-03-13 14:02:23 -05:00
Jeremy Stretch
520af82f05 Closes #2924: Add interface type for QSFP28 50GE 2019-03-13 10:00:40 -04:00
dansheps
9292534324 Changelog Updates 2019-03-12 15:54:38 -05:00
dansheps
61efe6102e Fixes #2207
* Added 'id' field sort to InterfaceManager
2019-03-12 15:52:44 -05:00
Jeremy Stretch
705f82e416 Added filters for power panels & feeds 2019-03-12 12:05:58 -04:00
Jeremy Stretch
e06dece00c More power work 2019-03-12 11:36:29 -04:00
Jeremy Stretch
3b9c0e4c67 Fixed up models & forms 2019-03-12 10:17:00 -04:00
Jeremy Stretch
5b753923b6 Initial work on power modeling (WIP) 2019-03-11 22:40:52 -04:00
Jeremy Stretch
0b95016e00 Merge branch 'develop' into develop-2.6 2019-03-11 21:01:18 -04:00
Daniel Sheppard
0022a5a6e2 Merge pull request #2992 from DanSheps/api-documentation-filtering
* Fixes #2991: Fixed typo
* Fixes #2577: Clarified documentation regarding filtering multiple times (some filters can, some cannot)
2019-03-11 15:26:59 -05:00
dansheps
332487efcd API Filtering Documentation Changes
* Fixed typo
* Clarified documentation regarding filtering multiple times (some filters can, some cannot)
2019-03-11 15:11:16 -05:00
Jeremy Stretch
28331dfd53 Post-release version bump 2019-03-11 13:39:30 -04:00
Jeremy Stretch
d112b6027a Merge pull request #2990 from digitalocean/develop
Release v2.5.8
2019-03-11 13:37:07 -04:00
Jeremy Stretch
88933a3120 Release v2.5.8 2019-03-11 13:34:56 -04:00
Jeremy Stretch
e05871b467 Fixes #2982: Correct CSS class assignment on color picker 2019-03-11 13:31:48 -04:00
Jeremy Stretch
7e70bfaacc Fixes #2985: Fix pagination page length for rack elevations 2019-03-11 12:51:03 -04:00
Jeremy Stretch
f4b85751bb Fixes #2940: Allow CSV import of prefixes/IPs to VRF without an RD assigned 2019-03-11 12:34:19 -04:00
Jeremy Stretch
ea11e70e3f Fixes #2984: Fix logging of unlabeled cable ID on cable deletion 2019-03-11 12:26:40 -04:00
Jeremy Stretch
bd8b239e15 Fixes #2980: Improve rendering time for API docs 2019-03-08 10:28:11 -05:00
Jeremy Stretch
d9c8c0cbc1 Fixes #2968: Correct API documentation for SerializerMethodFields 2019-03-08 10:25:09 -05:00
Jeremy Stretch
7a8fc8dfd5 Changelog for #2944 2019-03-07 14:08:36 -05:00
Jeremy Stretch
4334f1bc65 Merge pull request #2979 from TakeMeNL/fix/2944
Fixes: #2944 Adding/removing an IP address is not recorded on the associated interface's changelog
2019-03-07 14:06:00 -05:00
Jeremy Stretch
1995091169 Changelog & renaming for #2065 2019-03-07 13:58:34 -05:00
Jeremy Stretch
ef089d3722 Merge pull request #2957 from axnsan12/ref-name
Resolve drf-yasg `ref_name` conflicts
2019-03-07 13:52:29 -05:00
Marc
1d904b1722 Fix IP address is not recorded in the associated interface's changelog 2019-03-07 10:57:26 +01:00
Jeremy Stretch
ab02f26a0e Fixes #2972: Improve ContentTypeField serializer to elegantly handle invalid data 2019-03-06 12:42:47 -05:00
Jeremy Stretch
f35b4bf768 Fixes #2976: Add delete button to tag view 2019-03-06 12:11:01 -05:00
Jeremy Stretch
f2382dd255 Merge pull request #2969 from DanSheps/2435-PrintingCSS
Closes #2435: Printer friendly CSS
2019-03-06 09:44:04 -05:00
Daniel Sheppard
8df169b170 Remove .noprint from outside of the @media tag 2019-03-05 21:52:54 -06:00
Daniel Sheppard
88aeaaffb0 Remove .noprint from outside of the @media tag 2019-03-05 21:52:37 -06:00
dansheps
e9546b810c * Updated changelog 2019-03-05 15:46:41 -06:00
dansheps
c208d8fc2e * Added CSS to:
* Hide URLs
  * Hide elements with "noprint" class
* Added noprint to:
  * Header Panel
  * Search Panel, Tags Panel
  * Buttons
  * Various list elements
  * Related elements
2019-03-05 15:42:47 -06:00
Jeremy Stretch
8a1d7fdb37 Updated migrations to account for extras/0017_exporttemplate_mime_type_length 2019-03-05 13:46:37 -05:00
Jeremy Stretch
c52d077f92 Merge branch 'develop' into develop-2.6 2019-03-05 13:39:00 -05:00
Jeremy Stretch
7294f43fa3 Changelog for #2966 2019-03-05 13:18:56 -05:00
Jeremy Stretch
0572c66c17 Merge pull request #2967 from Anthony25/fix_cable_length_unit_api
Allow nullable length unit in cable API
2019-03-05 13:17:37 -05:00
Jeremy Stretch
6406e213bd Fixes #2961: Prevent exception when exporting inventory items belonging to unnamed devices 2019-03-05 13:15:09 -05:00
Jeremy Stretch
cfb56f7cfe Fixes #2962: Increase ExportTemplate mime_type field length 2019-03-05 13:08:40 -05:00
Anthony Ruhier
bd65e782bb Allow nullable length unit in cable API
Cables models define it as None by default, but the API rejects a
request containing a null length_unit. Allows it in the API
serializer.
2019-03-05 18:44:44 +01:00
dansheps
37811d3f7e * Resolve conflict with virtualization filters. 2019-03-05 08:19:21 -06:00
dansheps
3bb1cbcdb0 * Resolve conflict with virtualization filters. 2019-03-05 08:18:04 -06:00
dansheps
5fcd673f9f Merge remote-tracking branch 'dansheps/2813-addtenantgroupfilter' into 2813-addtenantgroupfilter
# Conflicts:
#	netbox/ipam/tables.py
2019-03-05 08:11:44 -06:00
dansheps
b4d7f9ea43 Fixes #2781: Fixes filter by regions on site and device list
* Add Device filter
2019-03-05 08:10:10 -06:00
Jeremy Stretch
66d9d9d9cb Rearranged changelog items 2019-03-04 16:01:43 -05:00
Grokzen
b9f4a9e57b Closes #1792 - Add CustomFieldChoices API endpoint (#2941)
* Add new api endpoint for CustomFieldChoices

* Add changelog item for #1792

* Add tests for CustomFieldchoiceAPI endpoint
2019-03-04 15:58:40 -05:00
Jeremy Stretch
80b3a5ffc4 Fixed erroneous addition of 'description' field to unique_together 2019-03-04 15:57:21 -05:00
Jeremy Stretch
0c142f2078 Changelog for #2954 2019-03-04 15:16:35 -05:00
Jeremy Stretch
7295c3554e Merge pull request #2955 from axnsan12/trailing-slashes
Remove trailing slashes from filesystem paths
2019-03-04 15:14:45 -05:00
Jeremy Stretch
78725b8483 Follow-up from #2781 2019-03-04 14:57:35 -05:00
Jeremy Stretch
e97ad3f066 Merge pull request #2935 from DanSheps/2781-fixsitelistfilterregion
Fixes: #2781 - Enable Filtering by Multiple Regions on Site and Device Lists
2019-03-04 14:42:04 -05:00
Cristi Vîjdea
3a62e9a322 Resolve drf-yasg ref_name conflicts
This solves the problem of distinct serializers being confused because they have the same class name (e.g. `InterfaceSerializer`)

Fixes #2065.
2019-03-04 03:49:26 +02:00
Cristi Vîjdea
4d18d9661b Remove trailing slashes from filesystem paths
Paths with trailing slashes do not work on windows, they cause errors such as `django.core.exceptions.SuspiciousFileOperation: The joined path (C:\Projects\netbox\netbox\static\clipboard-2.0.4.min.js) is located outside of the base path component (C:\Projects\netbox\netbox\static\)`.
2019-03-04 03:38:15 +02:00
John Anderson
11976f8968 Merge branch 'develop' into develop-2.6 2019-03-03 19:06:15 -05:00
John Anderson
5991bd368c Merge branch 'develop-2.6' of github.com:digitalocean/netbox into develop-2.6 2019-03-03 19:06:05 -05:00
John Anderson
b381bdec27 fixes #2952 - slug field absent from TenantFilter 2019-03-03 18:52:57 -05:00
John Anderson
231a5aa9fd v2.6.0-dev version update 2019-03-01 23:16:08 -05:00
Jeremy Stretch
6f5c35c278 Force resolution of request User object when logging an object deletion (resolves intermittent test failures) 2019-02-28 11:40:32 -05:00
Jeremy Stretch
3e6033e9ff Fixes #2938: Exclude circuit terminations from API interface connections endpoint 2019-02-28 09:59:17 -05:00
Jeremy Stretch
32f63a18ff Fixes #2938: Enforce deterministic ordering of device components returned by API 2019-02-28 09:49:23 -05:00
dansheps
00aaf500de Fixes #2781: Fixes filter by regions on site and device list
* Add Device filter
2019-02-27 14:46:11 -06:00
dansheps
f2471aedb2 Fixes #2781: Fixes filter by regions on site and device list 2019-02-27 11:41:12 -06:00
Jeremy Stretch
beff774295 Fixes #2705: Fix endpoint grouping in API docs 2019-02-26 16:38:49 -05:00
Daniel Sheppard
679aa0f764 Update tables.py
Fix whitespace
2019-02-26 07:53:59 -06:00
dansheps
8683efe54a Fixes #2813: Add Filter and List View for TenantGroup
Added Filter for TenantGroup to the following Forms and Filter classes

* circuit.Circuit
* dcim.Site
* dcim.Rack
* dcim.RackElevation
* dcim.RackReservation
* dcim.Device
* ipam.IPAddress
* ipam.Prefix
* ipam.VRF
* ipam.VLAN
* virtualization.VirtualMachine

Added List View to the following classes:

* circuit.Circuit
* dcim.Site
* dcim.Rack
* dcim.RackReservation
* dcim.Device
* ipam.IPAddress
* ipam.Prefix
* ipam.VRF
* ipam.VLAN
* virtualization.VirtualMachine
2019-02-23 11:09:02 -06:00
dansheps
f78c228c75 Fixes #2813: Add Filter for TenantGroup to the following Forms and Filter classes:
* circuit.Circuit
* dcim.Site
* dcim.Rack
* dcim.RackElevation
* dcim.RackReservation
* dcim.Device
* ipam.IPAddress
* ipam.Prefix
* ipam.VRF
* ipam.VLAN
* virtualization.VirtualMachine
2019-02-23 10:37:30 -06:00
Jeremy Stretch
8dfef83f1a Changelog for #2923 2019-02-22 12:16:15 -05:00
Jeremy Stretch
d707844c30 Merge pull request #2928 from DanSheps/2923-changecircuitsitefiltermulti
Fixes #2923: Changes Site on ProviderFilterForm to APISelectMultiple
2019-02-22 12:13:56 -05:00
Jeremy Stretch
6a8c935380 Merge branch 'develop' into develop-2.6 2019-02-22 12:10:44 -05:00
Jeremy Stretch
00c4d3dd92 Merge pull request #2918 from digitalocean/2643-description-fields
Closes #2643: Add description field to console/power components and device bays
2019-02-22 12:08:24 -05:00
Daniel Sheppard
3ffea43253 Fixes #2923: Changes Site Filter on Provider List to APISelectMultiple 2019-02-22 07:13:39 -06:00
John Anderson
8e548605c8 added changelog views for Tag 2019-02-22 02:29:00 -05:00
John Anderson
de52f21905 fix circular import for ObjectChange, for now... 2019-02-22 01:42:17 -05:00
John Anderson
b9d11aa4ca refactor tag migrations and add changelog fields to tag 2019-02-22 01:32:31 -05:00
Jeremy Stretch
77c387a559 Post-release version bump 2019-02-21 14:45:23 -05:00
John Anderson
fba6d28603 removed migration to delete taggit models 2019-02-21 00:15:15 -05:00
Jeremy Stretch
bb6fb81fc0 Closes #2643: Add description field to console/power components and device bays 2019-02-20 14:34:05 -05:00
John Anderson
0a06d92c2e added default values for comments and color fields on tag 2019-02-20 03:56:32 -05:00
John Anderson
fc2bb724fa initial pass on migrating to custom tag model with color and comments fields 2019-02-20 03:52:47 -05:00
John Anderson
e521508de9 #2350 - added virtual machines and test cases 2019-02-18 22:10:05 -05:00
John Anderson
bd573fd5cf implemented #2350 - config context included by default in API 2019-02-18 21:37:00 -05:00
361 changed files with 15002 additions and 6526 deletions

View File

@@ -9,8 +9,7 @@
IF YOUR PULL REQUEST DOES NOT REFERENCE AN ACCEPTED BUG REPORT OR
FEATURE REQUEST, IT WILL BE MARKED AS INVALID AND CLOSED.
-->
### Fixes:
### Fixes: <ISSUE NUMBER GOES HERE>
<!--
Please include a summary of the proposed changes below.
-->

23
.github/stale.yaml vendored Normal file
View File

@@ -0,0 +1,23 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 14
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- "status: accepted"
- "status: gathering feedback"
- "status: blocked"
# Label to use when marking an issue as stale
staleLabel: wontfix
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. NetBox
is governed by a small group of core maintainers which means not all opened
issues may receive direct feedback. Please see our [contributing guide](https://github.com/netbox-community/netbox/blob/develop/CONTRIBUTING.md).
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: >
This issue has been automatically closed due to lack of activity. In an
effort to reduce noise, please do not comment any further. Note that the
core maintainers may elect to reopen this issue at a later date if deemed
necessary.

2
.gitignore vendored
View File

@@ -3,6 +3,8 @@
/netbox/netbox/ldap_config.py
/netbox/reports/*
!/netbox/reports/__init__.py
/netbox/scripts/*
!/netbox/scripts/__init__.py
/netbox/static
.idea
/*.sh

View File

@@ -1,6 +1,7 @@
sudo: required
services:
- postgresql
- redis-server
addons:
postgresql: "9.4"
language: python

File diff suppressed because it is too large Load Diff

View File

@@ -16,15 +16,15 @@ For real-time discussion, you can join the #netbox Slack channel on [NetworkToCo
## Reporting Bugs
* First, ensure that you've installed the [latest stable version](https://github.com/digitalocean/netbox/releases)
* First, ensure that you've installed the [latest stable version](https://github.com/netbox-community/netbox/releases)
of NetBox. If you're running an older version, it's possible that the bug has
already been fixed.
* Next, check the GitHub [issues list](https://github.com/digitalocean/netbox/issues)
* Next, check the GitHub [issues list](https://github.com/netbox-community/netbox/issues)
to see if the bug you've found has already been reported. If you think you may
be experiencing a reported issue that hasn't already been resolved, please
click "add a reaction" in the top right corner of the issue and add a thumbs
up (+1). You mightalso want to add a comment describing how it's affecting your
up (+1). You might also want to add a comment describing how it's affecting your
installation. This will allow us to prioritize bugs based on how many users are
affected.
@@ -51,7 +51,7 @@ your issue.
## Feature Requests
* First, check the GitHub [issues list](https://github.com/digitalocean/netbox/issues)
* First, check the GitHub [issues list](https://github.com/netbox-community/netbox/issues)
to see if the feature you're requesting is already listed. (Be sure to search
closed issues as well, since some feature requests have been rejected.) If the
feature you'd like to see has already been requested and is open, click "add a
@@ -99,6 +99,8 @@ any work that's already in progress.
* Any pull request which does _not_ relate to an accepted issue will be closed.
* All major new functionality must include relevant tests where applicable.
* When submitting a pull request, please be sure to work off of the `develop`
branch, rather than `master`. The `develop` branch is used for ongoing
development, while `master` is used for tagging new stable releases.
@@ -117,3 +119,49 @@ Only comment on an issue if you are sharing a relevant idea or constructive
feedback. **Do not** comment on an issue just to show your support (give the
top post a :+1: instead) or ask for an ETA. These comments will be deleted to
reduce noise in the discussion.
## Issue Lifecycle
When a correctly formatted issue is submitted it is evaluated by a moderator
who may elect to immediately label the issue as accepted in addition to another
issue type label. In other cases, the issue may be labeled as "status: gathering feedback"
which will often be accompanied by a comment from a moderator asking for further dialog from the community.
If an issue is labeled as "status: revisions needed" a moderator has identified a problem with
the issue itself and is asking for the submitter himself to update the original post with
the requested information. If the original post is not updated in a reasonable amount of time,
the issue will be closed as invalid.
The core maintainers group has chosen to make use of the GitHub Stale bot to aid in issue management.
* Issues will be marked as stale after 14 days of no activity.
* Then after 7 more days of inactivity, the issue will be closed.
* Any issue bearing one of the following labels will be exempt from all Stale bot actions:
* `status: accepted`
* `status: gathering feedback`
* `status: blocked`
It is natural that some new issues get more attention than others. Often this is a metric of an issues's
overall usefulness to the project. In other cases in which issues merely get lost in the shuffle,
notifications from Stale bot can bring renewed attention to potentially meaningful issues.
## Maintainer Guidance
* Maintainers are expected to contribute at least four hours per week to the
project on average. This can be employer-sponsored or individual time, with
the understanding that all contributions are submitted under the Apache 2.0
license and that your employer may not make claim to any contributions.
Contributions include code work, issue management, and community support. All
development must be in accordance with our [development guidance](https://netbox.readthedocs.io/en/stable/development/).
* Maintainers are expected to attend (where feasible) our biweekly ~30-minute
sync to review agenda items. This meeting provides opportunity to present and
discuss pressing topics. Meetings are held as virtual audio/video conferences.
* Official channels for communication include:
* GitHub issues/pull requests
* The [netbox-discuss](https://groups.google.com/forum/#!forum/netbox-discuss) mailing list
* The **#netbox** channel on [NetworkToCode Slack](https://networktocode.slack.com/)
* Maintainers with no substantial recorded activity in a 60-day period will be
removed from the project.

1
NOTICE Normal file
View File

@@ -0,0 +1 @@
Copyrighted and licensed under Apache License 2.0 by DigitalOcean, LLC.

View File

@@ -3,11 +3,12 @@
NetBox is an IP address management (IPAM) and data center infrastructure
management (DCIM) tool. Initially conceived by the network engineering team at
[DigitalOcean](https://www.digitalocean.com/), NetBox was developed specifically
to address the needs of network and infrastructure engineers.
to address the needs of network and infrastructure engineers. It is intended to
function as a domain-specific source of truth for network operations.
NetBox runs as a web application atop the [Django](https://www.djangoproject.com/)
Python framework with a [PostgreSQL](http://www.postgresql.org/) database. For a
complete list of requirements, see `requirements.txt`. The code is available [on GitHub](https://github.com/digitalocean/netbox).
complete list of requirements, see `requirements.txt`. The code is available [on GitHub](https://github.com/netbox-community/netbox).
The complete documentation for NetBox can be found at [Read the Docs](http://netbox.readthedocs.io/en/stable/).
@@ -18,8 +19,8 @@ or join us in the #netbox Slack channel on [NetworkToCode](https://networktocode
| | status |
|-------------|------------|
| **master** | [![Build Status](https://travis-ci.org/digitalocean/netbox.svg?branch=master)](https://travis-ci.org/digitalocean/netbox) |
| **develop** | [![Build Status](https://travis-ci.org/digitalocean/netbox.svg?branch=develop)](https://travis-ci.org/digitalocean/netbox) |
| **master** | [![Build Status](https://travis-ci.org/netbox-community/netbox.svg?branch=master)](https://travis-ci.com/netbox-community/netbox/) |
| **develop** | [![Build Status](https://travis-ci.org/netbox-community/netbox.svg?branch=develop)](https://travis-ci.com/netbox-community/netbox/) |
## Screenshots
@@ -32,7 +33,7 @@ or join us in the #netbox Slack channel on [NetworkToCode](https://networktocode
# Installation
Please see [the documentation](http://netbox.readthedocs.io/en/stable/) for
instructions on installing NetBox. To upgrade NetBox, please download the [latest release](https://github.com/digitalocean/netbox/releases)
instructions on installing NetBox. To upgrade NetBox, please download the [latest release](https://github.com/netbox-community/netbox/releases)
and run `upgrade.sh`.
## Alternative Installations
@@ -40,18 +41,17 @@ and run `upgrade.sh`.
* [Docker container](https://github.com/netbox-community/netbox-docker) (via [@cimnine](https://github.com/cimnine))
* [Vagrant deployment](https://github.com/ryanmerolle/netbox-vagrant) (via [@ryanmerolle](https://github.com/ryanmerolle))
* [Ansible deployment](https://github.com/lae/ansible-role-netbox) (via [@lae](https://github.com/lae))
* [Kubernetes deployment](https://github.com/CENGN/netbox-kubernetes) (via [@CENGN](https://github.com/CENGN))
# Providing Feedback
Feature requests and bug reports must be submitted as GiHub issues. (Please be
sure to use the [appropriate template](https://github.com/netbox-community/netbox/issues/new/choose).)
For general discussion, please consider joining our [mailing list](https://groups.google.com/forum/#!forum/netbox-discuss).
If you are interested in contributing to the development of NetBox, please read
our [contributing guide](CONTRIBUTING.md) prior to beginning any work.
# Related projects
## Supported SDK
- [pynetbox](https://github.com/digitalocean/pynetbox) Python API client library for Netbox.
## Community SDK
- [netbox-client-ruby](https://github.com/ninech/netbox-client-ruby) A ruby client library for Netbox v2.
## Ansible Inventory
- [netbox-as-ansible-inventory](https://github.com/AAbouZaid/netbox-as-ansible-inventory) Ansible dynamic inventory script for Netbox.
Please see [our wiki](https://github.com/netbox-community/netbox/wiki/Community-Contributions) for a list of relevant community projects.

View File

@@ -2,6 +2,10 @@
# https://github.com/django/django
Django
# Django caching using Redis
# https://github.com/Suor/django-cacheops
django-cacheops
# Django middleware which permits cross-domain API requests
# https://github.com/OttoYiu/django-cors-headers
django-cors-headers
@@ -18,6 +22,14 @@ django-filter
# https://github.com/django-mptt/django-mptt
django-mptt
# Django integration for RQ (Reqis queuing)
# https://github.com/rq/django-rq
django-rq
# Prometheus metrics library for Django
# https://github.com/korfuri/django-prometheus
django-prometheus
# Abstraction models for rendering and paginating HTML tables
# https://github.com/jieter/django-tables2
django-tables2

View File

@@ -0,0 +1,21 @@
# Caching
To improve performance, NetBox supports caching for most object and list views. Caching is implemented using Redis,
and [django-cacheops](https://github.com/Suor/django-cacheops)
Several management commands are avaliable for administrators to manaully invalidate cache entries in extenuating circumstances.
To invalidate a specifc model instance (for example a Device with ID 34):
```
python netbox/manage.py invalidate dcim.Device.34
```
To invalidate all instance of a model:
```
python netbox/manage.py invalidate dcim.Device
```
To flush the entire cache database:
```
python netbox/manage.py invalidate all
```

View File

@@ -0,0 +1,43 @@
# Custom Links
Custom links allow users to place arbitrary hyperlinks within NetBox views. These are helpful for cross-referencing related records in external systems. For example, you might create a custom link on the device view which links to the current device in a network monitoring system.
Custom links are created under the admin UI. Each link is associated with a particular NetBox object type (site, device, prefix, etc.) and will be displayed on relevant views. Each link is assigned text and a URL, both of which support Jinja2 templating. The text and URL are rendered with the context variable `obj` representing the current object.
For example, you might define a link like this:
* Text: `View NMS`
* URL: `https://nms.example.com/nodes/?name={{ obj.name }}`
When viewing a device named Router4, this link would render as:
```
<a href="https://nms.example.com/nodes/?name=Router4">View NMS</a>
```
Custom links appear as buttons at the top right corner of the page. Numeric weighting can be used to influence the ordering of links.
## Conditional Rendering
Only links which render with non-empty text are included on the page. You can employ conditional Jinja2 logic to control the conditions under which a link gets rendered.
For example, if you only want to display a link for active devices, you could set the link text to
```
{% if obj.status == 1 %}View NMS{% endif %}
```
The link will not appear when viewing a device with any status other than "active."
Another example, if you want to only show an object of a certain manufacturer, you could set the link text to:
```
{% if obj.device_type.manufacturer.name == 'Cisco' %}View NMS {% endif %}
```
The link will only appear when viewing a device with a manufacturer name of "Cisco."
## Link Groups
You can specify a group name to organize links into related sets. Grouped links will render as a dropdown menu beneath a
single button bearing the name of the group.

View File

@@ -0,0 +1,238 @@
# Custom Scripts
Custom scripting was introduced to provide a way for users to execute custom logic from within the NetBox UI. Custom scripts enable the user to directly and conveniently manipulate NetBox data in a prescribed fashion. They can be used to accomplish myriad tasks, such as:
* Automatically populate new devices and cables in preparation for a new site deployment
* Create a range of new reserved prefixes or IP addresses
* Fetch data from an external source and import it to NetBox
Custom scripts are Python code and exist outside of the official NetBox code base, so they can be updated and changed without interfering with the core NetBox installation. And because they're written from scratch, a custom script can be used to accomplish just about anything.
## Writing Custom Scripts
All custom scripts must inherit from the `extras.scripts.Script` base class. This class provides the functionality necessary to generate forms and log activity.
```
from extras.scripts import Script
class MyScript(Script):
..
```
Scripts comprise two core components: variables and a `run()` method. Variables allow your script to accept user input via the NetBox UI. The `run()` method is where your script's execution logic lives. (Note that your script can have as many methods as needed: this is merely the point of invocation for NetBox.)
```
class MyScript(Script):
var1 = StringVar(...)
var2 = IntegerVar(...)
var3 = ObjectVar(...)
def run(self, data):
...
```
The `run()` method is passed a single argument: a dictionary containing all of the variable data passed via the web form. Your script can reference this data during execution.
Defining variables is optional: You may create a script with only a `run()` method if no user input is needed.
Returning output from your script is optional. Any raw output generated by the script will be displayed under the "output" tab in the UI.
## Module Attributes
### `name`
You can define `name` within a script module (the Python file which contains one or more scripts) to set the module name. If `name` is not defined, the filename will be used.
## Script Attributes
Script attributes are defined under a class named `Meta` within the script. These are optional, but encouraged.
### `name`
This is the human-friendly names of your script. If omitted, the class name will be used.
### `description`
A human-friendly description of what your script does.
### `field_order`
A list of field names indicating the order in which the form fields should appear. This is optional, however on Python 3.5 and earlier the fields will appear in random order. (Declarative ordering is preserved on Python 3.6 and above.) For example:
```
field_order = ['var1', 'var2', 'var3']
```
### `commit_default`
The checkbox to commit database changes when executing a script is checked by default. Set `commit_default` to False under the script's Meta class to leave this option unchecked by default.
```
commit_default = False
```
## Reading Data from Files
The Script class provides two convenience methods for reading data from files:
* `load_yaml`
* `load_json`
These two methods will load data in YAML or JSON format, respectively, from files within the local path (i.e. `SCRIPTS_ROOT`).
## Logging
The Script object provides a set of convenient functions for recording messages at different severity levels:
* `log_debug`
* `log_success`
* `log_info`
* `log_warning`
* `log_failure`
Log messages are returned to the user upon execution of the script. Markdown rendering is supported for log messages.
## Variable Reference
### StringVar
Stores a string of characters (i.e. a line of text). Options include:
* `min_length` - Minimum number of characters
* `max_length` - Maximum number of characters
* `regex` - A regular expression against which the provided value must match
Note: `min_length` and `max_length` can be set to the same number to effect a fixed-length field.
### TextVar
Arbitrary text of any length. Renders as multi-line text input field.
### IntegerVar
Stored a numeric integer. Options include:
* `min_value:` - Minimum value
* `max_value` - Maximum value
### BooleanVar
A true/false flag. This field has no options beyond the defaults.
### ChoiceVar
A set of choices from which the user can select one.
* `choices` - A list of `(value, label)` tuples representing the available choices. For example:
```python
CHOICES = (
('n', 'North'),
('s', 'South'),
('e', 'East'),
('w', 'West')
)
direction = ChoiceVar(choices=CHOICES)
```
### ObjectVar
A NetBox object. The list of available objects is defined by the queryset parameter. Each instance of this variable is limited to a single object type.
* `queryset` - A [Django queryset](https://docs.djangoproject.com/en/stable/topics/db/queries/)
### FileVar
An uploaded file. Note that uploaded files are present in memory only for the duration of the script's execution: They will not be save for future use.
### IPNetworkVar
An IPv4 or IPv6 network with a mask.
### Default Options
All variables support the following default options:
* `label` - The name of the form field
* `description` - A brief description of the field
* `default` - The field's default value
* `required` - Indicates whether the field is mandatory (default: true)
## Example
Below is an example script that creates new objects for a planned site. The user is prompted for three variables:
* The name of the new site
* The device model (a filtered list of defined device types)
* The number of access switches to create
These variables are presented as a web form to be completed by the user. Once submitted, the script's `run()` method is called to create the appropriate objects.
```
from django.utils.text import slugify
from dcim.constants import *
from dcim.models import Device, DeviceRole, DeviceType, Site
from extras.scripts import *
class NewBranchScript(Script):
class Meta:
name = "New Branch"
description = "Provision a new branch site"
fields = ['site_name', 'switch_count', 'switch_model']
site_name = StringVar(
description="Name of the new site"
)
switch_count = IntegerVar(
description="Number of access switches to create"
)
switch_model = ObjectVar(
description="Access switch model",
queryset = DeviceType.objects.filter(
manufacturer__name='Cisco',
model__in=['Catalyst 3560X-48T', 'Catalyst 3750X-48T']
)
)
def run(self, data):
# Create the new site
site = Site(
name=data['site_name'],
slug=slugify(data['site_name']),
status=SITE_STATUS_PLANNED
)
site.save()
self.log_success("Created new site: {}".format(site))
# Create access switches
switch_role = DeviceRole.objects.get(name='Access Switch')
for i in range(1, data['switch_count'] + 1):
switch = Device(
device_type=data['switch_model'],
name='{}-switch{}'.format(site.slug, i),
site=site,
status=DEVICE_STATUS_PLANNED,
device_role=switch_role
)
switch.save()
self.log_success("Created new switch: {}".format(switch))
# Generate a CSV table of new devices
output = [
'name,make,model'
]
for switch in Device.objects.filter(site=site):
attrs = [
switch.name,
switch.device_type.manufacturer.name,
switch.device_type.model
]
output.append(','.join(attrs))
return '\n'.join(output)
```

View File

@@ -4,7 +4,7 @@ NetBox allows users to define custom templates that can be used when exporting o
Each export template is associated with a certain type of object. For instance, if you create an export template for VLANs, your custom template will appear under the "Export" button on the VLANs list.
Export templates are written in [Django's template language](https://docs.djangoproject.com/en/1.9/ref/templates/language/), which is very similar to Jinja2. The list of objects returned from the database is stored in the `queryset` variable, which you'll typically want to iterate through using a `for` loop. Object properties can be access by name. For example:
Export templates are written in [Django's template language](https://docs.djangoproject.com/en/stable/ref/templates/language/), which is very similar to Jinja2. The list of objects returned from the database is stored in the `queryset` variable, which you'll typically want to iterate through using a `for` loop. Object properties can be access by name. For example:
```
{% for rack in queryset %}

View File

@@ -2,7 +2,7 @@
NetBox does not have the ability to generate graphs natively, but this feature allows you to embed contextual graphs from an external resources (such as a monitoring system) inside the site, provider, and interface views. Each embedded graph must be defined with the following parameters:
* **Type:** Site, provider, or interface. This determines in which view the graph will be displayed.
* **Type:** Site, device, provider, or interface. This determines in which view the graph will be displayed.
* **Weight:** Determines the order in which graphs are displayed (lower weights are displayed first). Graphs with equal weights will be ordered alphabetically by name.
* **Name:** The title to display above the graph.
* **Source URL:** The source of the image to be embedded. The associated object will be available as a template variable named `obj`.

View File

@@ -0,0 +1,34 @@
# Prometheus Metrics
NetBox supports optionally exposing native Prometheus metrics from the application. [Prometheus](https://prometheus.io/) is a popular time series metric platform used for monitoring.
NetBox exposes metrics at the `/metrics` HTTP endpoint, e.g. `https://netbox.local/metrics`. Metric exposition can be toggled with the `METRICS_ENABLED` configuration setting. Metrics are not exposed by default.
## Metric Types
NetBox makes use of the [django-prometheus](https://github.com/korfuri/django-prometheus) library to export a number of different types of metrics, including:
- Per model insert, update, and delete counters
- Per view request counters
- Per view request latency histograms
- Request body size histograms
- Response body size histograms
- Response code counters
- Database connection, execution, and error counters
- Cache hit, miss, and invalidation counters
- Django middleware latency histograms
- Other Django related metadata metrics
For the exhaustive list of exposed metrics, visit the `/metrics` endpoint on your NetBox instance.
## Multi Processing Notes
When deploying NetBox in a multiprocess mannor--such as using Gunicorn as recomented in the installation docs--the Prometheus client library requires the use of a shared directory
to collect metrics from all the worker processes. This can be any arbitrary directory to which the processes have read/write access. This directory is then made available by use of the
`prometheus_multiproc_dir` environment variable.
This can be setup by first creating a shared directory and then adding this line (with the appropriate directory) to the `[program:netbox]` section of the supervisor config file.
```
environment=prometheus_multiproc_dir=/tmp/prometheus_metrics
```

View File

@@ -12,7 +12,7 @@ A NetBox report is a mechanism for validating the integrity of data within NetBo
## Writing Reports
Reports must be saved as files in the [`REPORTS_ROOT`](../configuration/optional-settings/#reports_root) path (which defaults to `netbox/reports/`). Each file created within this path is considered a separate module. Each module holds one or more reports (Python classes), each of which performs a certain function. The logic of each report is broken into discrete test methods, each of which applies a small portion of the logic comprising the overall test.
Reports must be saved as files in the [`REPORTS_ROOT`](../../configuration/optional-settings/#reports_root) path (which defaults to `netbox/reports/`). Each file created within this path is considered a separate module. Each module holds one or more reports (Python classes), each of which performs a certain function. The logic of each report is broken into discrete test methods, each of which applies a small portion of the logic comprising the overall test.
!!! warning
The reports path includes a file named `__init__.py`, which registers the path as a Python module. Do not delete this file.
@@ -43,7 +43,7 @@ class DeviceConnectionsReport(Report):
def test_console_connection(self):
# Check that every console port for every active device has a connection defined.
for console_port in ConsolePort.objects.select_related('device').filter(device__status=DEVICE_STATUS_ACTIVE):
for console_port in ConsolePort.objects.prefetch_related('device').filter(device__status=DEVICE_STATUS_ACTIVE):
if console_port.connected_endpoint is None:
self.log_failure(
console_port.device,

View File

@@ -4,14 +4,6 @@ A webhook defines an HTTP request that is sent to an external application when c
An optional secret key can be configured for each webhook. This will append a `X-Hook-Signature` header to the request, consisting of a HMAC (SHA-512) hex digest of the request body using the secret as the key. This digest can be used by the receiver to authenticate the request's content.
## Installation
If you are upgrading from a previous version of Netbox and want to enable the webhook feature, please follow the directions listed in the sections below.
* [Install Redis server and djano-rq package](../installation/2-netbox/#install-python-packages)
* [Modify configuration to enable webhooks](../installation/2-netbox/#webhooks-configuration)
* [Create supervisord program to run the rqworker process](../installation/3-http-daemon/#supervisord-installation)
## Requests
The webhook POST request is structured as so (assuming `application/json` as the Content-Type):
@@ -19,8 +11,10 @@ The webhook POST request is structured as so (assuming `application/json` as the
```no-highlight
{
"event": "created",
"signal_received_timestamp": 1508769597,
"model": "Site"
"timestamp": "2019-10-12 12:51:29.746944",
"username": "admin",
"model": "site",
"request_id": "43d8e212-94c7-4f67-b544-0dcde4fc0f43",
"data": {
...
}
@@ -32,8 +26,10 @@ The webhook POST request is structured as so (assuming `application/json` as the
```no-highlight
{
"event": "deleted",
"signal_received_timestamp": 1508781858.544069,
"model": "Site",
"timestamp": "2019-10-12 12:55:44.030750",
"username": "johnsmith",
"model": "site",
"request_id": "e9bb83b2-ebe4-4346-b13f-07144b1a00b4",
"data": {
"asn": None,
"comments": "",

View File

@@ -4,7 +4,7 @@ NetBox includes a Python shell within which objects can be directly queried, cre
./manage.py nbshell
```
This will launch a customized version of [the built-in Django shell](https://docs.djangoproject.com/en/dev/ref/django-admin/#shell) with all relevant NetBox models pre-loaded. (If desired, the stock Django shell is also available by executing `./manage.py shell`.)
This will launch a customized version of [the built-in Django shell](https://docs.djangoproject.com/en/stable/ref/django-admin/#shell) with all relevant NetBox models pre-loaded. (If desired, the stock Django shell is also available by executing `./manage.py shell`.)
```
$ ./manage.py nbshell
@@ -28,7 +28,7 @@ DCIM:
## Querying Objects
Objects are retrieved by forming a [Django queryset](https://docs.djangoproject.com/en/dev/topics/db/queries/#retrieving-objects). The base queryset for an object takes the form `<model>.objects.all()`, which will return a (truncated) list of all objects of that type.
Objects are retrieved by forming a [Django queryset](https://docs.djangoproject.com/en/stable/topics/db/queries/#retrieving-objects). The base queryset for an object takes the form `<model>.objects.all()`, which will return a (truncated) list of all objects of that type.
```
>>> Device.objects.all()
@@ -99,7 +99,7 @@ This approach can span multiple levels of relations. For example, the following
```
!!! note
While the above query is functional, it is very inefficient. There are ways to optimize such requests, however they are out of the scope of this document. For more information, see the [Django queryset method reference](https://docs.djangoproject.com/en/dev/ref/models/querysets/) documentation.
While the above query is functional, it is very inefficient. There are ways to optimize such requests, however they are out of the scope of this document. For more information, see the [Django queryset method reference](https://docs.djangoproject.com/en/stable/ref/models/querysets/) documentation.
Reverse relationships can be traversed as well. For example, the following will find all devices with an interface named "em0":
@@ -137,7 +137,7 @@ To return the inverse of a filtered queryset, use `exclude()` instead of `filter
```
!!! info
The examples above are intended only to provide a cursory introduction to queryset filtering. For an exhaustive list of the available filters, please consult the [Django queryset API docs](https://docs.djangoproject.com/en/dev/ref/models/querysets/).
The examples above are intended only to provide a cursory introduction to queryset filtering. For an exhaustive list of the available filters, please consult the [Django queryset API docs](https://docs.djangoproject.com/en/stable/ref/models/querysets/).
## Creating and Updating Objects

View File

@@ -30,7 +30,7 @@ psql -c 'create database netbox'
psql netbox < netbox.sql
```
Keep in mind that PostgreSQL user accounts and permissions are not included with the dump: You will need to create those manually if you want to fully replicate the original database (see the [installation docs](installation/1-postgresql.md)). When setting up a development instance of NetBox, it's strongly recommended to use different credentials anyway.
Keep in mind that PostgreSQL user accounts and permissions are not included with the dump: You will need to create those manually if you want to fully replicate the original database (see the [installation docs](../installation/1-postgresql.md)). When setting up a development instance of NetBox, it's strongly recommended to use different credentials anyway.
## Export the Database Schema
@@ -39,6 +39,11 @@ If you want to export only the database schema, and not the data itself (e.g. fo
```no-highlight
pg_dump -s netbox > netbox_schema.sql
```
If you are migrating your instance of NetBox to a different machine, please make sure you invalidate the cache by performing this command:
```no-highlight
python3 manage.py invalidate all
```
---

View File

@@ -27,7 +27,7 @@ $ curl -H "Accept: application/json; indent=4" http://localhost/api/dcim/sites/
}
```
However, if the [`LOGIN_REQUIRED`](../configuration/optional-settings/#login_required) configuration setting has been set to `True`, all requests must be authenticated.
However, if the [`LOGIN_REQUIRED`](../../configuration/optional-settings/#login_required) configuration setting has been set to `True`, all requests must be authenticated.
```
$ curl -H "Accept: application/json; indent=4" http://localhost/api/dcim/sites/

View File

@@ -1,5 +1,3 @@
NetBox v2.0 and later includes a full-featured REST API that allows its data model to be read and manipulated externally.
# What is a REST API?
REST stands for [representational state transfer](https://en.wikipedia.org/wiki/Representational_state_transfer). It's a particular type of API which employs HTTP to create, retrieve, update, and delete objects from a database. (This set of operations is commonly referred to as CRUD.) Each type of operation is associated with a particular HTTP verb:
@@ -34,6 +32,10 @@ $ curl -s http://localhost:8000/api/ipam/ip-addresses/2954/ | jq '.'
Each attribute of the NetBox object is expressed as a field in the dictionary. Fields may include their own nested objects, as in the case of the `status` field above. Every object includes a primary key named `id` which uniquely identifies it in the database.
# Interactive Documentation
Comprehensive, interactive documentation of all API endpoints is available on a running NetBox instance at `/api/docs/`. This interface provides a convenient sandbox for researching and experimenting with NetBox's various API endpoints and different request types.
# URL Hierarchy
NetBox's entire API is housed under the API root at `https://<hostname>/api/`. The URL structure is divided at the root level by application: circuits, DCIM, extras, IPAM, secrets, and tenancy. Within each application, each model has its own path. For example, the provider and circuit objects are located under the "circuits" application:
@@ -104,24 +106,37 @@ The base serializer is used to represent the default view of a model. This inclu
}
```
Related objects (e.g. `ForeignKey` fields) are represented using a nested serializer. A nested serializer provides a minimal representation of an object, including only its URL and enough information to construct its name. When performing write api actions (`POST`, `PUT`, and `PATCH`), any `ForeignKey` relationships do not use the nested serializer, instead you will pass just the integer ID of the related model.
## Related Objects
When a base serializer includes one or more nested serializers, the hierarchical structure precludes it from being used for write operations. Thus, a flat representation of an object may be provided using a writable serializer. This serializer includes only raw database values and is not typically used for retrieval, except as part of the response to the creation or updating of an object.
Related objects (e.g. `ForeignKey` fields) are represented using a nested serializer. A nested serializer provides a minimal representation of an object, including only its 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):
```
{
"id": 1201,
"site": 7,
"group": 4,
"vid": 102,
"name": "Users-Floor2",
"tenant": null,
"status": 1,
"role": 9,
"description": ""
"name": "MyNewDevice",
"rack": 123,
...
}
```
Or by a set of nested attributes used to identify the rack:
```
{
"name": "MyNewDevice",
"rack": {
"site": {
"name": "Equinix DC6"
},
"name": "R204"
},
...
}
```
Note that if the provided parameters do not return exactly one object, a validation error is raised.
## Brief Format
Most API endpoints support an optional "brief" format, which returns only a minimal representation of each object in the response. This is useful when you need only a list of the objects themselves without any related data, such as when populating a drop-down list in a form.
@@ -231,7 +246,7 @@ Vary: Accept
}
```
The default page size derives from the [`PAGINATE_COUNT`](../configuration/optional-settings/#paginate_count) configuration setting, which defaults to 50. However, this can be overridden per request by specifying the desired `offset` and `limit` query parameters. For example, if you wish to retrieve a hundred devices at a time, you would make a request for:
The default page size derives from the [`PAGINATE_COUNT`](../../configuration/optional-settings/#paginate_count) configuration setting, which defaults to 50. However, this can be overridden per request by specifying the desired `offset` and `limit` query parameters. For example, if you wish to retrieve a hundred devices at a time, you would make a request for:
```
http://localhost:8000/api/dcim/devices/?limit=100
@@ -248,7 +263,7 @@ The response will return devices 1 through 100. The URL provided in the `next` a
}
```
The maximum number of objects that can be returned is limited by the [`MAX_PAGE_SIZE`](../configuration/optional-settings/#max_page_size) setting, which is 1000 by default. Setting this to `0` or `None` will remove the maximum limit. An API consumer can then pass `?limit=0` to retrieve _all_ matching objects with a single request.
The maximum number of objects that can be returned is limited by the [`MAX_PAGE_SIZE`](../../configuration/optional-settings/#max_page_size) setting, which is 1000 by default. Setting this to `0` or `None` will remove the maximum limit. An API consumer can then pass `?limit=0` to retrieve _all_ matching objects with a single request.
!!! warning
Disabling the page size limit introduces a potential for very resource-intensive requests, since one API request can effectively retrieve an entire table from the database.
@@ -261,12 +276,31 @@ A list of objects retrieved via the API can be filtered by passing one or more q
GET /api/ipam/prefixes/?status=1
```
The same filter can be incldued multiple times. These will effect a logical OR and return objects matching any of the given values. For example, the following will return all active and reserved prefixes:
The choices available for fixed choice fields such as `status` are exposed in the API under a special `_choices` endpoint for each NetBox app. For example, the available choices for `Prefix.status` are listed at `/api/ipam/_choices/` under the key `prefix:status`:
```
GET /api/ipam/prefixes/?status=1&status=2
"prefix:status": [
{
"label": "Container",
"value": 0
},
{
"label": "Active",
"value": 1
},
{
"label": "Reserved",
"value": 2
},
{
"label": "Deprecated",
"value": 3
}
],
```
For most fields, when a filter is passed multiple times, objects matching _any_ of the provided values will be returned. For example, `GET /api/dcim/sites/?name=Foo&name=Bar` will return all sites named "Foo" _or_ "Bar". The exception to this rule is ManyToManyFields which may have multiple values assigned. Tags are the most common example of a ManyToManyField. For example, `GET /api/dcim/sites/?tag=foo&tag=bar` will return only sites tagged with both "foo" _and_ "bar".
## Custom Fields
To filter on a custom field, prepend `cf_` to the field name. For example, the following query will return only sites where a custom field named `foo` is equal to 123:

View File

@@ -2,7 +2,7 @@ As with most other objects, the NetBox API can be used to create, modify, and de
# Generating a Session Key
In order to encrypt or decrypt secret data, a session key must be attached to the API request. To generate a session key, send an authenticated request to the `/api/secrets/get-session-key/` endpoint with the private RSA key which matches your [UserKey](../data-model/secrets/#user-keys). The private key must be POSTed with the name `private_key`.
In order to encrypt or decrypt secret data, a session key must be attached to the API request. To generate a session key, send an authenticated request to the `/api/secrets/get-session-key/` endpoint with the private RSA key which matches your [UserKey](../../core-functionality/secrets/#user-keys). The private key must be POSTed with the name `private_key`.
```
$ curl -X POST http://localhost:8000/api/secrets/get-session-key/ \

View File

@@ -44,6 +44,14 @@ BASE_PATH = 'netbox/'
---
## CACHE_TIMEOUT
Default: 900
The number of seconds to retain cache entries before automatically invalidating them.
---
## CHANGELOG_RETENTION
Default: 90
@@ -64,7 +72,13 @@ If True, cross-origin resource sharing (CORS) requests will be accepted from all
## CORS_ORIGIN_REGEX_WHITELIST
These settings specify a list of origins that are authorized to make cross-site API requests. Use `CORS_ORIGIN_WHITELIST` to define a list of exact hostnames, or `CORS_ORIGIN_REGEX_WHITELIST` to define a set of regular expressions. (These settings have no effect if `CORS_ORIGIN_ALLOW_ALL` is True.)
These settings specify a list of origins that are authorized to make cross-site API requests. Use `CORS_ORIGIN_WHITELIST` to define a list of exact hostnames, or `CORS_ORIGIN_REGEX_WHITELIST` to define a set of regular expressions. (These settings have no effect if `CORS_ORIGIN_ALLOW_ALL` is True.) For example:
```
CORS_ORIGIN_WHITELIST = [
'https://example.com',
]
```
---
@@ -89,6 +103,30 @@ In order to send email, NetBox needs an email server configured. The following i
---
## EXEMPT_VIEW_PERMISSIONS
Default: Empty list
A list of models to exempt from the enforcement of view permissions. Models listed here will be viewable by all users and by anonymous users.
List models in the form `<app>.<model>`. For example:
```
EXEMPT_VIEW_PERMISSIONS = [
'dcim.site',
'dcim.region',
'ipam.prefix',
]
```
To exempt _all_ models from view permission enforcement, set the following. (Note that `EXEMPT_VIEW_PERMISSIONS` must be an iterable.)
```
EXEMPT_VIEW_PERMISSIONS = ['*']
```
---
# ENFORCE_GLOBAL_UNIQUE
Default: False
@@ -101,7 +139,7 @@ Enforcement of unique IP space can be toggled on a per-VRF basis. To enforce uni
By default, all messages of INFO severity or higher will be logged to the console. Additionally, if `DEBUG` is False and email access has been configured, ERROR and CRITICAL messages will be emailed to the users defined in `ADMINS`.
The Django framework on which NetBox runs allows for the customization of logging, e.g. to write logs to file. Please consult the [Django logging documentation](https://docs.djangoproject.com/en/1.11/topics/logging/) for more information on configuring this setting. Below is an example which will write all INFO and higher messages to a file:
The Django framework on which NetBox runs allows for the customization of logging, e.g. to write logs to file. 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 file:
```
LOGGING = {
@@ -165,6 +203,14 @@ The file path to the location where media files (such as image attachments) are
---
## METRICS_ENABLED
Default: False
Toggle exposing Prometheus metrics at `/metrics`. See the [Prometheus Metrics](../../additional-features/prometheus-metrics/) documentation for more details.
---
## NAPALM_USERNAME
## NAPALM_PASSWORD
@@ -231,6 +277,14 @@ The file path to the location where custom reports will be kept. By default, thi
---
## SCRIPTS_ROOT
Default: $BASE_DIR/netbox/scripts/
The file path to the location where custom scripts will be kept. By default, this is the `netbox/scripts/` directory within the base NetBox installation path.
---
## SESSION_FILE_PATH
Default: None
@@ -251,13 +305,13 @@ The time zone NetBox will use when dealing with dates and times. It is recommend
Default: False
Enable this option to run the webhook backend. See the docs section on the webhook backend [here](../additional-features/webhooks/) for more information on setup and use.
Enable this option to run the webhook backend. See the docs section on the webhook backend [here](../../additional-features/webhooks/) for more information on setup and use.
---
## Date and Time Formatting
You may define custom formatting for date and times. For detailed instructions on writing format strings, please see [the Django documentation](https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date).
You may define custom formatting for date and times. For detailed instructions on writing format strings, please see [the Django documentation](https://docs.djangoproject.com/en/stable/ref/templates/builtins/#date).
Defaults:
@@ -269,49 +323,3 @@ SHORT_TIME_FORMAT = 'H:i:s' # 13:23:00
DATETIME_FORMAT = 'N j, Y g:i a' # June 26, 2016 1:23 p.m.
SHORT_DATETIME_FORMAT = 'Y-m-d H:i' # 2016-06-27 13:23
```
---
## Redis Connection Settings
[Redis](https://redis.io/) is a key-value store which functions as a very lightweight database. It is required when enabling NetBox [webhooks](../additional-features/webhooks/). A Redis connection is configured using a dictionary similar to the following:
```
REDIS = {
'HOST': 'localhost',
'PORT': 6379,
'PASSWORD': '',
'DATABASE': 0,
'DEFAULT_TIMEOUT': 300,
}
```
### DATABASE
Default: 0
The Redis database ID.
### DEFAULT_TIMEOUT
Default: 300
The timeout value to use when connecting to the Redis server (in seconds).
### HOST
Default: localhost
The hostname or IP address of the Redis server.
### PORT
Default: 6379
The TCP port to use when connecting to the Redis server.
### PASSWORD
Default: None
The password to use when authenticating to the Redis server (optional).

View File

@@ -2,7 +2,7 @@
## ALLOWED_HOSTS
This is a list of valid fully-qualified domain names (FQDNs) that is used to reach the NetBox service. Usually this is the same as the hostname for the NetBox server, but can also be different (e.g. when using a reverse proxy serving the NetBox website under a different FQDN than the hostname of the NetBox server). NetBox will not permit access to the server via any other hostnames (or IPs). The value of this option is also used to set `CSRF_TRUSTED_ORIGINS`, which restricts `HTTP POST` to the same set of hosts (more about this [here](https://docs.djangoproject.com/en/1.9/ref/settings/#std:setting-CSRF_TRUSTED_ORIGINS)). Keep in mind that NetBox, by default, has `USE_X_FORWARDED_HOST = True` (in `netbox/netbox/settings.py`) which means that if you're using a reverse proxy, it's the FQDN used to reach that reverse proxy which needs to be in this list (more about this [here](https://docs.djangoproject.com/en/1.9/ref/settings/#allowed-hosts)).
This is a list of valid fully-qualified domain names (FQDNs) that is used to reach the NetBox service. Usually this is the same as the hostname for the NetBox server, but can also be different (e.g. when using a reverse proxy serving the NetBox website under a different FQDN than the hostname of the NetBox server). NetBox will not permit access to the server via any other hostnames (or IPs). The value of this option is also used to set `CSRF_TRUSTED_ORIGINS`, which restricts `HTTP POST` to the same set of hosts (more about this [here](https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-CSRF_TRUSTED_ORIGINS)). Keep in mind that NetBox, by default, has `USE_X_FORWARDED_HOST = True` (in `netbox/netbox/settings.py`) which means that if you're using a reverse proxy, it's the FQDN used to reach that reverse proxy which needs to be in this list (more about this [here](https://docs.djangoproject.com/en/stable/ref/settings/#allowed-hosts)).
Example:
@@ -16,11 +16,12 @@ ALLOWED_HOSTS = ['netbox.example.com', '192.0.2.123']
NetBox requires access to a PostgreSQL database service to store data. This service can run locally or on a remote system. The following parameters must be defined within the `DATABASE` dictionary:
* NAME - Database name
* USER - PostgreSQL username
* PASSWORD - PostgreSQL password
* HOST - Name or IP address of the database server (use `localhost` if running locally)
* PORT - TCP port of the PostgreSQL service; leave blank for default port (5432)
* `NAME` - Database name
* `USER` - PostgreSQL username
* `PASSWORD` - PostgreSQL password
* `HOST` - Name or IP address of the database server (use `localhost` if running locally)
* `PORT` - TCP port of the PostgreSQL service; leave blank for default port (5432)
* `CONN_MAX_AGE` - Number in seconds for Netbox to keep database connections open. 150-300 seconds is typically a good starting point ([more info](https://docs.djangoproject.com/en/stable/ref/databases/#persistent-connections)).
Example:
@@ -31,11 +32,53 @@ DATABASE = {
'PASSWORD': 'J5brHrAXFLQSif0K', # PostgreSQL password
'HOST': 'localhost', # Database server
'PORT': '', # Database port (leave blank for default)
'CONN_MAX_AGE': 300, # Max database connection age
}
```
---
## REDIS
[Redis](https://redis.io/) is an in-memory data store similar to memcached. While Redis has been an optional component of
NetBox since the introduction of webhooks in version 2.4, it is required starting in 2.6 to support NetBox's caching
functionality (as well as other planned features).
Redis is configured using a configuration setting similar to `DATABASE`:
* `HOST` - Name or IP address of the Redis server (use `localhost` if running locally)
* `PORT` - TCP port of the Redis service; leave blank for default port (6379)
* `PASSWORD` - Redis password (if set)
* `DATABASE` - Numeric database ID for webhooks
* `CACHE_DATABASE` - Numeric database ID for caching
* `DEFAULT_TIMEOUT` - Connection timeout in seconds
* `SSL` - Use SSL connection to Redis
Example:
```
REDIS = {
'HOST': 'localhost',
'PORT': 6379,
'PASSWORD': '',
'DATABASE': 0,
'CACHE_DATABASE': 1,
'DEFAULT_TIMEOUT': 300,
'SSL': False,
}
```
!!! note:
If you were using these settings in a prior release with webhooks, the `DATABASE` setting remains the same but
an additional `CACHE_DATABASE` setting has been added with a default value of 1 to support the caching backend. The
`DATABASE` setting will be renamed in a future release of NetBox to better relay the meaning of the setting.
!!! warning:
It is highly recommended to keep the webhook and cache databases seperate. Using the same database number for both may result in webhook
processing data being lost in cache flushing events.
---
## SECRET_KEY
This is a secret cryptographic key is used to improve the security of cookies and password resets. The key defined here should not be shared outside of the configuration file. `SECRET_KEY` can be changed at any time, however be aware that doing so will invalidate all existing sessions.

View File

@@ -81,7 +81,7 @@ Power ports connect only to power outlets. Power connections can be marked as ei
Interfaces connect to one another in a symmetric manner: If interface A connects to interface B, interface B therefore connects to interface A. Each type of connection can be classified as either *planned* or *connected*.
Each interface is a assigned a form factor denoting its physical properties. Two special form factors exist: the "virtual" form factor can be used to designate logical interfaces (such as SVIs), and the "LAG" form factor can be used to desinate link aggregation groups to which physical interfaces can be assigned.
Each interface is a assigned a type denoting its physical properties. Two special types exist: the "virtual" type can be used to designate logical interfaces (such as SVIs), and the "LAG" type can be used to desinate link aggregation groups to which physical interfaces can be assigned.
Each interface can also be enabled or disabled, and optionally designated as management-only (for out-of-band management). Fields are also provided to store an interface's MTU and MAC address.
@@ -95,7 +95,7 @@ Pass-through ports can also be used to model "bump in the wire" devices, such as
### Device Bays
Device bays represent the ability of a device to house child devices. For example, you might install four blade servers into a 2U chassis. The chassis would appear in the rack elevation as a 2U device with four device bays. Each server within it would be defined as a 0U device installed in one of the device bays. Child devices do not appear within rack elevations, but they are included in the "Non-Racked Devices" list within the rack view.
Device bays represent the ability of a device to house child devices. For example, you might install four blade servers into a 2U chassis. The chassis would appear in the rack elevation as a 2U device with four device bays. Each server within it would be defined as a 0U device installed in one of the device bays. Child devices do not appear within rack elevations or the "Non-Racked Devices" list within the rack view.
Child devices are first-class Devices in their own right: that is, fully independent managed entities which don't share any control plane with the parent. Just like normal devices, child devices have their own platform (OS), role, tags, and interfaces. You cannot create a LAG between interfaces in different child devices.

View File

@@ -38,7 +38,7 @@ Add the name of the new field to `csv_headers` and included a CSV-friendly repre
### 4. Update relevant querysets
If you're adding a relational field (e.g. `ForeignKey`) and intend to include the data when retreiving a list of objects, be sure to include the field using `select_related()` or `prefetch_related()` as appropriate. This will optimize the view and avoid excessive database lookups.
If you're adding a relational field (e.g. `ForeignKey`) and intend to include the data when retreiving a list of objects, be sure to include the field using `prefetch_related()` as appropriate. This will optimize the view and avoid excessive database lookups.
### 5. Update API serializer

View File

@@ -1,12 +1,12 @@
# NetBox Development
NetBox is maintained as a [GitHub project](https://github.com/digitalocean/netbox) under the Apache 2 license. Users are encouraged to submit GitHub issues for feature requests and bug reports, however we are very selective about pull requests. Please see the `CONTRIBUTING` guide for more direction on contributing to NetBox.
NetBox is maintained as a [GitHub project](https://github.com/netbox-community/netbox) under the Apache 2 license. Users are encouraged to submit GitHub issues for feature requests and bug reports, however we are very selective about pull requests. Please see the `CONTRIBUTING` guide for more direction on contributing to NetBox.
## Communication
Communication among developers should always occur via public channels:
* [GitHub issues](https://github.com/digitalocean/netbox/issues) - All feature requests, bug reports, and other substantial changes to the code base **must** be documented in an issue.
* [GitHub issues](https://github.com/netbox-community/netbox/issues) - All feature requests, bug reports, and other substantial changes to the code base **must** be documented in an issue.
* [The mailing list](https://groups.google.com/forum/#!forum/netbox-discuss) - The preferred forum for general discussion and support issues. Ideal for shaping a feature request prior to submitting an issue.
* [#netbox on NetworkToCode](http://slack.networktocode.com/) - Good for quick chats. Avoid any discussion that might need to be referenced later on, as the chat history is not retained long.

View File

@@ -29,9 +29,14 @@ Update the following static libraries to their most recent stable release:
* Bootstrap 3
* Font Awesome 4
* Select2
* jQuery
* jQuery UI
## Create a new Release Notes Page
Create a file at `/docs/release-notes/X.Y.md` to establish the release notes for the new release. Add the file to the table of contents within `mkdocs.yml`.
## Manually Perform a New Install
Create a new installation of NetBox by following [the current documentation](http://netbox.readthedocs.io/en/latest/). This should be a manual process, so that issues with the documentation can be identified and corrected.
@@ -50,7 +55,7 @@ Ensure that continuous integration testing on the `develop` branch is completing
## Update Version and Changelog
Update the `VERSION` constant in `settings.py` to the new release version and add the current date to the release notes in `CHANGELOG.md`.
Update the `VERSION` constant in `settings.py` to the new release version and annotate the current data in the release notes for the new version.
## Submit a Pull Request
@@ -60,7 +65,7 @@ Once CI has completed on the PR, merge it.
## Create a New Release
Draft a [new release](https://github.com/digitalocean/netbox/releases/new) with the following parameters.
Draft a [new release](https://github.com/netbox-community/netbox/releases/new) with the following parameters.
* **Tag:** Current version (e.g. `v2.3.4`)
* **Target:** `master`

View File

@@ -1,6 +1,6 @@
# Style Guide
NetBox generally follows the [Django style guide](https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/coding-style/), which is itself based on [PEP 8](https://www.python.org/dev/peps/pep-0008/). [Pycodestyle](https://github.com/pycqa/pycodestyle) is used to validate code formatting, ignoring certain violations. See `scripts/cibuild.sh`.
NetBox generally follows the [Django style guide](https://docs.djangoproject.com/en/stable/internals/contributing/writing-code/coding-style/), which is itself based on [PEP 8](https://www.python.org/dev/peps/pep-0008/). [Pycodestyle](https://github.com/pycqa/pycodestyle) is used to validate code formatting, ignoring certain violations. See `scripts/cibuild.sh`.
## PEP 8 Exceptions

View File

@@ -1,18 +1,18 @@
# Installation
This section of the documentation discusses installing and configuring the NetBox application.
This section of the documentation discusses installing and configuring the NetBox application. Begin by installing all system packages required by NetBox and its dependencies:
**Ubuntu**
```no-highlight
# apt-get install -y python3 python3-pip python3-dev build-essential libxml2-dev libxslt1-dev libffi-dev graphviz libpq-dev libssl-dev zlib1g-dev
# apt-get install -y python3 python3-pip python3-dev build-essential libxml2-dev libxslt1-dev libffi-dev graphviz libpq-dev libssl-dev redis-server zlib1g-dev
```
**CentOS**
```no-highlight
# yum install -y epel-release
# yum install -y gcc python36 python36-devel python36-setuptools libxml2-devel libxslt-devel libffi-devel graphviz openssl-devel redhat-rpm-config
# yum install -y gcc python36 python36-devel python36-setuptools libxml2-devel libxslt-devel libffi-devel graphviz openssl-devel redhat-rpm-config redis
# easy_install-3.6 pip
# ln -s /usr/bin/python36 /usr/bin/python3
```
@@ -21,10 +21,10 @@ You may opt to install NetBox either from a numbered release or by cloning the m
## Option A: Download a Release
Download the [latest stable release](https://github.com/digitalocean/netbox/releases) from GitHub as a tarball or ZIP archive and extract it to your desired path. In this example, we'll use `/opt/netbox`.
Download the [latest stable release](https://github.com/netbox-community/netbox/releases) from GitHub as a tarball or ZIP archive and extract it to your desired path. In this example, we'll use `/opt/netbox`.
```no-highlight
# wget https://github.com/digitalocean/netbox/archive/vX.Y.Z.tar.gz
# wget https://github.com/netbox-community/netbox/archive/vX.Y.Z.tar.gz
# tar -xzf vX.Y.Z.tar.gz -C /opt
# cd /opt/
# ln -s netbox-X.Y.Z/ netbox
@@ -56,7 +56,7 @@ If `git` is not already installed, install it:
Next, clone the **master** branch of the NetBox GitHub repository into the current directory:
```no-highlight
# git clone -b master https://github.com/digitalocean/netbox.git .
# git clone -b master https://github.com/netbox-community/netbox.git .
Cloning into '.'...
remote: Counting objects: 1994, done.
remote: Compressing objects: 100% (150/150), done.
@@ -90,28 +90,6 @@ NetBox supports integration with the [NAPALM automation](https://napalm-automati
# pip3 install napalm
```
## Webhooks (Optional)
[Webhooks](../data-model/extras/#webhooks) allow NetBox to integrate with external services by pushing out a notification each time a relevant object is created, updated, or deleted. Enabling the webhooks feature requires [Redis](https://redis.io/), a lightweight in-memory database. You may opt to install a Redis sevice locally (see below) or connect to an external one.
**Ubuntu**
```no-highlight
# apt-get install -y redis-server
```
**CentOS**
```no-highlight
# yum install -y redis
```
Enabling webhooks also requires installing the [`django-rq`](https://github.com/ui/django-rq) package. This allows NetBox to use the Redis database as a queue for outgoing webhooks.
```no-highlight
# pip3 install django-rq
```
# Configuration
Move into the NetBox configuration directory and make a copy of `configuration.example.py` named `configuration.py`.
@@ -123,9 +101,10 @@ Move into the NetBox configuration directory and make a copy of `configuration.e
Open `configuration.py` with your preferred editor and set the following variables:
* ALLOWED_HOSTS
* DATABASE
* SECRET_KEY
* `ALLOWED_HOSTS`
* `DATABASE`
* `REDIS`
* `SECRET_KEY`
## ALLOWED_HOSTS
@@ -139,7 +118,7 @@ ALLOWED_HOSTS = ['netbox.example.com', '192.0.2.123']
## DATABASE
This parameter holds the database configuration details. You must define the username and password used when you configured PostgreSQL. If the service is running on a remote host, replace `localhost` with its address.
This parameter holds the database configuration details. You must define the username and password used when you configured PostgreSQL. If the service is running on a remote host, replace `localhost` with its address. See the [configuration documentation](../../configuration/required-settings/#database) for more detail on individual parameters.
Example:
@@ -150,6 +129,23 @@ DATABASE = {
'PASSWORD': 'J5brHrAXFLQSif0K', # PostgreSQL password
'HOST': 'localhost', # Database server
'PORT': '', # Database port (leave blank for default)
'CONN_MAX_AGE': 300, # Max database connection age
}
```
## REDIS
Redis is a in-memory key-value store required as part of the NetBox installation. It is used for features such as webhooks and caching. Redis typically requires minimal configuration; the values below should suffice for most installations. See the [configuration documentation](../../configuration/required-settings/#redis) for more detail on individual parameters.
```python
REDIS = {
'HOST': 'localhost',
'PORT': 6379,
'PASSWORD': '',
'DATABASE': 0,
'CACHE_DATABASE': 1,
'DEFAULT_TIMEOUT': 300,
'SSL': False,
}
```
@@ -162,21 +158,6 @@ You may use the script located at `netbox/generate_secret_key.py` to generate a
!!! note
In the case of a highly available installation with multiple web servers, `SECRET_KEY` must be identical among all servers in order to maintain a persistent user session state.
## Webhooks Configuration
If you have opted to enable the webhooks, set `WEBHOOKS_ENABLED = True` and define the relevant `REDIS` database parameters. Below is an example:
```python
WEBHOOKS_ENABLED = True
REDIS = {
'HOST': 'localhost',
'PORT': 6379,
'PASSWORD': '',
'DATABASE': 0,
'DEFAULT_TIMEOUT': 300,
}
```
# Run Database Migrations
Before NetBox can run, we need to install the database schema. This is done by running `python3 manage.py migrate` from the `netbox` directory (`/opt/netbox/netbox/` in our example):

View File

@@ -108,7 +108,7 @@ Install gunicorn:
# pip3 install gunicorn
```
Save the following configuration in the root netbox installation path as `gunicorn_config.py` (e.g. `/opt/netbox/gunicorn_config.py` per our example installation). Be sure to verify the location of the gunicorn executable on your server (e.g. `which gunicorn`) and to update the `pythonpath` variable if needed. If using CentOS/RHEL, change the username from `www-data` to `nginx` or `apache`.
Save the following configuration in the root netbox installation path as `gunicorn_config.py` (e.g. `/opt/netbox/gunicorn_config.py` per our example installation). Be sure to verify the location of the gunicorn executable on your server (e.g. `which gunicorn`) and to update the `pythonpath` variable if needed. If using CentOS/RHEL, change the username from `www-data` to `nginx` or `apache`. More info on `max_requests` can be found in the [gunicorn docs](https://docs.gunicorn.org/en/stable/settings.html#max-requests).
```no-highlight
command = '/usr/bin/gunicorn'
@@ -116,6 +116,8 @@ pythonpath = '/opt/netbox/netbox'
bind = '127.0.0.1:8001'
workers = 3
user = 'www-data'
max_requests = 5000
max_requests_jitter = 500
```
# supervisord Installation

View File

@@ -1,7 +1,7 @@
# Migration
!!! warning
Beginning with v2.5, NetBox will no longer support Python 2. It is strongly recommended that you upgrade to Python 3 as soon as possible.
As of version 2.5, NetBox no longer supports Python 2. Python 3 is required to run any 2.5 release or later.
## Ubuntu
@@ -36,9 +36,3 @@ If using LDAP authentication, install the `django-auth-ldap` package:
```no-highlight
# pip3 install django-auth-ldap
```
If using Webhooks, install the `django-rq` package:
```no-highlight
# pip3 install django-rq
```

View File

@@ -1,15 +1,19 @@
# Review the Release Notes
Prior to upgrading your NetBox instance, be sure to carefully review all [release notes](../../release-notes/) that have been published since your current version was released. Although the upgrade process typically does not involve additional work, certain releases may introduce breaking or backward-incompatible changes. These are called out in the release notes under the version in which the change went into effect.
# Install the Latest Code
As with the initial installation, you can upgrade NetBox by either downloading the latest release package or by cloning the `master` branch of the git repository.
## Option A: Download a Release
Download the [latest stable release](https://github.com/digitalocean/netbox/releases) from GitHub as a tarball or ZIP archive. Extract it to your desired path. In this example, we'll use `/opt/netbox`.
Download the [latest stable release](https://github.com/netbox-community/netbox/releases) from GitHub as a tarball or ZIP archive. Extract it to your desired path. In this example, we'll use `/opt/netbox`.
Download and extract the latest version:
```no-highlight
# wget https://github.com/digitalocean/netbox/archive/vX.Y.Z.tar.gz
# wget https://github.com/netbox-community/netbox/archive/vX.Y.Z.tar.gz
# tar -xzf vX.Y.Z.tar.gz -C /opt
# cd /opt/
# ln -sfn netbox-X.Y.Z/ netbox

1
docs/release-notes/index.md Symbolic link
View File

@@ -0,0 +1 @@
version-2.6.md

View File

@@ -0,0 +1,22 @@
# v1.0.7-r1 (2016-07-05)
* [#199](https://github.com/netbox-community/netbox/issues/199) - Correct IP address validation
---
# v1.0.7 (2016-06-30)
**Note:** If upgrading from a previous release, be sure to run ./upgrade.sh after downloading the new code.
* [#135](https://github.com/netbox-community/netbox/issues/135) - Fixed display of navigation menu on mobile screens
* [#141](https://github.com/netbox-community/netbox/issues/141) - Fixed rendering of "getting started" guide
* Modified upgrade.sh to use sudo for pip installations
* [#109](https://github.com/netbox-community/netbox/issues/109) - Hide the navigation menu from anonymous users if login is required
* [#143](https://github.com/netbox-community/netbox/issues/143) - Add help_text to Device.position
* [#136](https://github.com/netbox-community/netbox/issues/136) - Prefixes which have host bits set will trigger an error instead of being silently corrected
* [#140](https://github.com/netbox-community/netbox/issues/140) - Improved support for Unicode in object names
---
# v1.0.0 (2016-06-27)
NetBox was originally developed internally at DigitalOcean by the network development team. This release marks the debut of NetBox as an open source project.

View File

@@ -0,0 +1,15 @@
# v1.1.0 (2016-07-07)
## New Features
* [#107](https://github.com/netbox-community/netbox/pull/107) - Docker support
* [#91](https://github.com/netbox-community/netbox/issues/91) - Support for subdevices within a device
* [#170](https://github.com/netbox-community/netbox/pull/170) - Added MAC address field to interfaces
## Bug Fixes
* [#169](https://github.com/netbox-community/netbox/issues/169) - Fix rendering of cancellation URL when editing objects
* [#183](https://github.com/netbox-community/netbox/issues/183) - Ignore vi swap files
* [#209](https://github.com/netbox-community/netbox/issues/209) - Corrected error when not confirming component template deletions
* [#214](https://github.com/netbox-community/netbox/issues/214) - Fixed redundant message on bulk interface creation
* [#68](https://github.com/netbox-community/netbox/issues/68) - Improved permissions-related error reporting for secrets

View File

@@ -0,0 +1,48 @@
# v1.2.2 (2016-07-14)
## Improvements
* [#174](https://github.com/netbox-community/netbox/issues/174) - Added search and site filter to provider list
* [#270](https://github.com/netbox-community/netbox/issues/270) - Added the ability to filter devices by rack group
## Bug Fixes
* [#115](https://github.com/netbox-community/netbox/issues/115) - Fix deprecated django.core.context_processors reference
* [#268](https://github.com/netbox-community/netbox/issues/268) - Added support for entire 32-bit ASN space
* [#282](https://github.com/netbox-community/netbox/issues/282) - De-select "all" checkbox if one or more objects are deselected
* [#290](https://github.com/netbox-community/netbox/issues/290) - Always display management interfaces for a device type (even if `is_network_device` is not set)
---
# v1.2.1 (2016-07-13)
**Note:** This release introduces a new dependency ([natsort](https://pypi.python.org/pypi/natsort)). Be sure to run `upgrade.sh` if upgrading from a previous release.
## Improvements
* [#285](https://github.com/netbox-community/netbox/issues/285) - Added the ability to prefer IPv4 over IPv6 for primary device IPs
## Bug Fixes
* [#243](https://github.com/netbox-community/netbox/issues/243) - Improved ordering of device object lists
* [#271](https://github.com/netbox-community/netbox/issues/271) - Fixed primary_ip bug in secrets API
* [#274](https://github.com/netbox-community/netbox/issues/274) - Fixed primary_ip bug in DCIM admin UI
* [#275](https://github.com/netbox-community/netbox/issues/275) - Fixed bug preventing the expansion of an existing aggregate
---
# v1.2.0 (2016-07-12)
## New Features
* [#73](https://github.com/netbox-community/netbox/issues/73) - Added optional persistent banner
* [#93](https://github.com/netbox-community/netbox/issues/73) - Ability to set both IPv4 and IPv6 primary IPs for devices
* [#203](https://github.com/netbox-community/netbox/issues/203) - Introduced support for LDAP
## Bug Fixes
* [#162](https://github.com/netbox-community/netbox/issues/228) - Fixed support for Unicode characters in rack/device/VLAN names
* [#228](https://github.com/netbox-community/netbox/issues/228) - Corrected conditional inclusion of device bay templates
* [#246](https://github.com/netbox-community/netbox/issues/246) - Corrected Docker build instructions
* [#260](https://github.com/netbox-community/netbox/issues/260) - Fixed error on admin UI device type list
* Miscellaneous layout improvements for mobile devices

View File

@@ -0,0 +1,54 @@
# v1.3.2 (2016-07-26)
## Improvements
* [#292](https://github.com/netbox-community/netbox/issues/292) - Added part_number field to DeviceType
* [#363](https://github.com/netbox-community/netbox/issues/363) - Added a description field to the VLAN model
* [#374](https://github.com/netbox-community/netbox/issues/374) - Increased VLAN name length to 64 characters
* Enabled bulk deletion of interfaces from devices
## Bug Fixes
* [#359](https://github.com/netbox-community/netbox/issues/359) - Corrected the DCIM API endpoint for finding related connections
* [#370](https://github.com/netbox-community/netbox/issues/370) - Notify user when secret decryption fails
* [#381](https://github.com/netbox-community/netbox/issues/381) - Fix 'u_consumed' error on rack import
* [#384](https://github.com/netbox-community/netbox/issues/384) - Fixed description field's maximum length on IPAM bulk edit forms
* [#385](https://github.com/netbox-community/netbox/issues/385) - Fixed error when deleting a user with one or more associated UserActions
---
# v1.3.1 (2016-07-21)
## Improvements
* [#258](https://github.com/netbox-community/netbox/issues/258) - Add an API endpoint to list interface connections
* [#303](https://github.com/netbox-community/netbox/issues/303) - Improved numeric ordering of sites, racks, and devices
* [#304](https://github.com/netbox-community/netbox/issues/304) - Display utilization percentage on rack list
* [#327](https://github.com/netbox-community/netbox/issues/327) - Disable rack assignment for installed child devices
## Bug Fixes
* [#331](https://github.com/netbox-community/netbox/issues/331) - Add group field to VLAN bulk edit form
* Miscellaneous improvements to Unicode handling
---
# v1.3.0 (2016-07-18)
## New Features
* [#42](https://github.com/netbox-community/netbox/issues/42) - Allow assignment of VLAN on prefix import
* [#43](https://github.com/netbox-community/netbox/issues/43) - Toggling of IP space uniqueness within a VRF
* [#111](https://github.com/netbox-community/netbox/issues/111) - Introduces VLAN groups
* [#227](https://github.com/netbox-community/netbox/issues/227) - Support for bulk import of child devices
## Bug Fixes
* [#301](https://github.com/netbox-community/netbox/issues/301) - Prevent deletion of DeviceBay when installed device is deleted
* [#306](https://github.com/netbox-community/netbox/issues/306) - Fixed device import to allow an unspecified rack face
* [#307](https://github.com/netbox-community/netbox/issues/307) - Catch `RelatedObjectDoesNotExist` when an invalid device type is defined during device import
* [#308](https://github.com/netbox-community/netbox/issues/308) - Update rack assignment for all child devices when moving a parent device
* [#311](https://github.com/netbox-community/netbox/issues/311) - Fix assignment of primary_ip on IP address import
* [#317](https://github.com/netbox-community/netbox/issues/317) - Rack elevation display fix for device types greater than 42U in height
* [#320](https://github.com/netbox-community/netbox/issues/320) - Disallow import of prefixes with host masks
* [#322](https://github.com/netbox-community/netbox/issues/320) - Corrected VLAN import behavior

View File

@@ -0,0 +1,54 @@
# v1.4.2 (2016-08-06)
## Improvements
* [#167](https://github.com/netbox-community/netbox/issues/167) - Added new interface form factors
* [#253](https://github.com/netbox-community/netbox/issues/253) - Added new interface form factors
* [#434](https://github.com/netbox-community/netbox/issues/434) - Restored admin UI access to user action history (however bulk deletion is disabled)
* [#435](https://github.com/netbox-community/netbox/issues/435) - Added an "add prefix" button to the VLAN view
## Bug Fixes
* [#425](https://github.com/netbox-community/netbox/issues/425) - Ignore leading and trailing periods when generating a slug
* [#427](https://github.com/netbox-community/netbox/issues/427) - Prevent error when duplicate IPs are present in a prefix's IP list
* [#429](https://github.com/netbox-community/netbox/issues/429) - Correct redirection of user when adding a secret to a device
---
# v1.4.1 (2016-08-03)
## Improvements
* [#289](https://github.com/netbox-community/netbox/issues/289) - Annotate available ranges in prefix IP list
* [#412](https://github.com/netbox-community/netbox/issues/412) - Tenant group assignment is no longer mandatory
* [#422](https://github.com/netbox-community/netbox/issues/422) - CSV import now supports double-quoting values which contain commas
## Bug Fixes
* [#395](https://github.com/netbox-community/netbox/issues/395) - Show child prefixes from all VRFs if the parent belongs to the global table
* [#406](https://github.com/netbox-community/netbox/issues/406) - Fixed circuit list rendring when filtering on port speed or commit rate
* [#409](https://github.com/netbox-community/netbox/issues/409) - Filter IPs and prefixes by tenant slug rather than by its PK
* [#411](https://github.com/netbox-community/netbox/issues/411) - Corrected title of secret roles view
* [#419](https://github.com/netbox-community/netbox/issues/419) - Fixed a potential database performance issue when gathering tenant statistics
---
# v1.4.0 (2016-08-01)
## New Features
### Multitenancy ([#16](https://github.com/netbox-community/netbox/issues/16))
NetBox now supports tenants and tenant groups. Sites, racks, devices, VRFs, prefixes, IP addresses, VLANs, and circuits can be assigned to tenants to track the allocation of these resources among customers or internal departments. If a prefix or IP address does not have a tenant assigned, it will fall back to the tenant assigned to its parent VRF (where applicable).
## Improvements
* [#176](https://github.com/netbox-community/netbox/issues/176) - Introduced seed data for new installs
* [#358](https://github.com/netbox-community/netbox/issues/358) - Improved search for all objects
* [#394](https://github.com/netbox-community/netbox/issues/394) - Improved VRF selection during bulk editing of prefixes and IP addresses
* Miscellaneous cosmetic improvements to the UI
## Bug Fixes
* [#392](https://github.com/netbox-community/netbox/issues/392) - Don't include child devices in non-racked devices table
* [#397](https://github.com/netbox-community/netbox/issues/397) - Only include child IPs which belong to the same VRF as the parent prefix

View File

@@ -0,0 +1,50 @@
# v1.5.2 (2016-08-16)
## Bug Fixes
* [#460](https://github.com/netbox-community/netbox/issues/460) - Corrected ordering of IP addresses with differing prefix lengths
* [#463](https://github.com/netbox-community/netbox/issues/463) - Prevent pre-population of livesearch field with '---------'
* [#467](https://github.com/netbox-community/netbox/issues/467) - Include prefixes and IPs which inherit tenancy from their VRF in tenant stats
* [#468](https://github.com/netbox-community/netbox/issues/468) - Don't allow connected interfaces to be changed to the "virtual" form factor
* [#469](https://github.com/netbox-community/netbox/issues/469) - Added missing import buttons to list views
* [#472](https://github.com/netbox-community/netbox/issues/472) - Hide the connection button for interfaces which have a circuit terminated to them
---
# v1.5.1 (2016-08-11)
## Improvements
* [#421](https://github.com/netbox-community/netbox/issues/421) - Added an asset tag field to devices
* [#456](https://github.com/netbox-community/netbox/issues/456) - Added IP search box to home page
* Colorized rack and device roles
## Bug Fixes
* [#454](https://github.com/netbox-community/netbox/issues/454) - Corrected error on rack export
* [#457](https://github.com/netbox-community/netbox/issues/457) - Added role field to rack edit form
---
# v1.5.0 (2016-08-10)
## New Features
### Rack Enhancements ([#180](https://github.com/netbox-community/netbox/issues/180), [#241](https://github.com/netbox-community/netbox/issues/241))
Like devices, racks can now be assigned to functional roles. This allows users to group racks by designated function as well as by physical location (rack groups). Additionally, rack can now have a defined rail-to-rail width (19 or 23 inches) and a type (two-post-rack, cabinet, etc.).
## Improvements
* [#149](https://github.com/netbox-community/netbox/issues/149) - Added discrete upstream speed field for circuits
* [#157](https://github.com/netbox-community/netbox/issues/157) - Added manufacturer field for device modules
* We have a logo!
* Upgraded to Django 1.10
## Bug Fixes
* [#433](https://github.com/netbox-community/netbox/issues/433) - Corrected form validation when editing child devices
* [#442](https://github.com/netbox-community/netbox/issues/442) - Corrected child device import instructions
* [#443](https://github.com/netbox-community/netbox/issues/443) - Correctly display and initialize VRF for creation of new IP addresses
* [#444](https://github.com/netbox-community/netbox/issues/444) - Corrected prefix model validation
* [#445](https://github.com/netbox-community/netbox/issues/445) - Limit rack height to between 1U and 100U (inclusive)

View File

@@ -0,0 +1,85 @@
# v1.6.3 (2016-10-19)
## Improvements
* [#353](https://github.com/netbox-community/netbox/issues/353) - Bulk editing of device and device type interfaces
* [#527](https://github.com/netbox-community/netbox/issues/527) - Support for nullification of fields when bulk editing
* [#592](https://github.com/netbox-community/netbox/issues/592) - Allow space-delimited lists of ALLOWED_HOSTS in Docker
* [#608](https://github.com/netbox-community/netbox/issues/608) - Added "select all" button for device and device type components
## Bug Fixes
* [#602](https://github.com/netbox-community/netbox/issues/602) - Correct display of custom integer fields with value of 0 or 1
* [#604](https://github.com/netbox-community/netbox/issues/604) - Correct display of unnamed devices in form selection fields
* [#611](https://github.com/netbox-community/netbox/issues/611) - Power/console/interface connection import: status field should be case-insensitive
* [#615](https://github.com/netbox-community/netbox/issues/615) - Account for BASE_PATH in static URLs and during login
* [#616](https://github.com/netbox-community/netbox/issues/616) - Correct display of custom URL fields
---
# v1.6.2-r1 (2016-10-04)
## Improvements
* [#212](https://github.com/netbox-community/netbox/issues/212) - Introduced the `BASE_PATH` configuration setting to allow running NetBox in a URL subdirectory
* [#345](https://github.com/netbox-community/netbox/issues/345) - Bulk edit: allow user to select all objects on page or all matching query
* [#475](https://github.com/netbox-community/netbox/issues/475) - Display "add" buttons at top and bottom of all device/device type panels
* [#480](https://github.com/netbox-community/netbox/issues/480) - Improved layout on mobile devices
* [#481](https://github.com/netbox-community/netbox/issues/481) - Require interface creation before trying to assign an IP to a device
* [#575](https://github.com/netbox-community/netbox/issues/575) - Allow all valid URL schemes in custom fields
* [#579](https://github.com/netbox-community/netbox/issues/579) - Add a description field to export templates
## Bug Fixes
* [#466](https://github.com/netbox-community/netbox/issues/466) - Validate available free space for all instances when increasing the U height of a device type
* [#571](https://github.com/netbox-community/netbox/issues/571) - Correct rack group filter on device list
* [#576](https://github.com/netbox-community/netbox/issues/576) - Delete all relevant CustomFieldValues when deleting a CustomFieldChoice
* [#581](https://github.com/netbox-community/netbox/issues/581) - Correct initialization of custom boolean and select fields
* [#591](https://github.com/netbox-community/netbox/issues/591) - Correct display of component creation buttons in device type view
---
# v1.6.1-r1 (2016-09-21)
## Improvements
* [#415](https://github.com/netbox-community/netbox/issues/415) - Add an expand/collapse toggle button to the prefix list
* [#552](https://github.com/netbox-community/netbox/issues/552) - Allow filtering on custom select fields by "none"
* [#561](https://github.com/netbox-community/netbox/issues/561) - Make custom fields accessible from within export templates
## Bug Fixes
* [#493](https://github.com/netbox-community/netbox/issues/493) - CSV import support for UTF-8
* [#531](https://github.com/netbox-community/netbox/issues/531) - Order prefix list by VRF assignment
* [#542](https://github.com/netbox-community/netbox/issues/542) - Add LDAP support in Docker
* [#557](https://github.com/netbox-community/netbox/issues/557) - Add 'global' choice to VRF filter for prefixes and IP addresses
* [#558](https://github.com/netbox-community/netbox/issues/558) - Update slug field when name is populated without a key press
* [#562](https://github.com/netbox-community/netbox/issues/562) - Fixed bulk interface creation
* [#564](https://github.com/netbox-community/netbox/issues/564) - Display custom fields for all applicable objects
---
# v1.6.0 (2016-09-13)
## New Features
### Custom Fields ([#129](https://github.com/netbox-community/netbox/issues/129))
Users can now create custom fields to associate arbitrary data with core NetBox objects. For example, you might want to add a geolocation tag to IP prefixes, or a ticket number to each device. Text, integer, boolean, date, URL, and selection fields are supported.
## Improvements
* [#489](https://github.com/netbox-community/netbox/issues/489) - Docker file now builds from a `python:2.7-wheezy` base instead of `ubuntu:14.04`
* [#540](https://github.com/netbox-community/netbox/issues/540) - Add links for VLAN roles under VLAN navigation menu
* Added new interface form factors
* Added address family filters to aggregate and prefix lists
## Bug Fixes
* [#476](https://github.com/netbox-community/netbox/issues/476) - Corrected rack import instructions
* [#484](https://github.com/netbox-community/netbox/issues/484) - Allow bulk deletion of >1K objects
* [#486](https://github.com/netbox-community/netbox/issues/486) - Prompt for secret key only if updating a secret's value
* [#490](https://github.com/netbox-community/netbox/issues/490) - Corrected display of circuit commit rate
* [#495](https://github.com/netbox-community/netbox/issues/495) - Include tenant in prefix and IP CSV export
* [#507](https://github.com/netbox-community/netbox/issues/507) - Corrected rendering of nav menu on screens narrower than 1200px
* [#515](https://github.com/netbox-community/netbox/issues/515) - Clarified instructions for the "face" field when importing devices
* [#522](https://github.com/netbox-community/netbox/issues/522) - Remove obsolete check for staff status when bulk deleting objects
* [#544](https://github.com/netbox-community/netbox/issues/544) - Strip CRLF-style line terminators from rendered export templates

View File

@@ -0,0 +1,75 @@
# v1.7.3 (2016-12-08)
## Bug Fixes
* [#724](https://github.com/netbox-community/netbox/issues/724) - Exempt API views from LoginRequiredMiddleware to enable basic HTTP authentication when LOGIN_REQUIRED is true
* [#729](https://github.com/netbox-community/netbox/issues/729) - Corrected cancellation links when editing secondary objects
* [#732](https://github.com/netbox-community/netbox/issues/732) - Allow custom select field values to be deselected if the field is not required
* [#733](https://github.com/netbox-community/netbox/issues/733) - Fixed MAC address filter on device list
* [#734](https://github.com/netbox-community/netbox/issues/734) - Corrected display of device type when editing a device
---
# v1.7.2-r1 (2016-12-06)
## Improvements
* [#663](https://github.com/netbox-community/netbox/issues/663) - Added MAC address search field to device list
* [#672](https://github.com/netbox-community/netbox/issues/672) - Increased the selection of available colors for rack and device roles
* [#695](https://github.com/netbox-community/netbox/issues/695) - Added is_private field to RIR
## Bug Fixes
* [#677](https://github.com/netbox-community/netbox/issues/677) - Fix setuptools installation error on Debian 8.6
* [#696](https://github.com/netbox-community/netbox/issues/696) - Corrected link to VRF in prefix and IP address breadcrumbs
* [#702](https://github.com/netbox-community/netbox/issues/702) - Improved Unicode support for custom fields
* [#712](https://github.com/netbox-community/netbox/issues/712) - Corrected export of tenants which are not assigned to a group
* [#713](https://github.com/netbox-community/netbox/issues/713) - Include a label for the comments field when editing circuits, providers, or racks in bulk
* [#718](https://github.com/netbox-community/netbox/issues/718) - Restore is_primary field on IP assignment form
* [#723](https://github.com/netbox-community/netbox/issues/723) - API documentation is now accessible when using BASE_PATH
* [#727](https://github.com/netbox-community/netbox/issues/727) - Corrected error in rack elevation display (v1.7.2)
---
# v1.7.1 (2016-11-15)
## Improvements
* [#667](https://github.com/netbox-community/netbox/issues/667) - Added prefix utilization statistics to the RIR list view
* [#685](https://github.com/netbox-community/netbox/issues/685) - When assigning an IP to a device, automatically select the interface if only one exists
## Bug Fixes
* [#674](https://github.com/netbox-community/netbox/issues/674) - Fix assignment of status to imported IP addresses
* [#676](https://github.com/netbox-community/netbox/issues/676) - Server error when bulk editing device types
* [#678](https://github.com/netbox-community/netbox/issues/678) - Server error on device import specifying an invalid device type
* [#691](https://github.com/netbox-community/netbox/issues/691) - Allow the assignment of power ports to PDUs
* [#692](https://github.com/netbox-community/netbox/issues/692) - Form errors are not displayed on checkbox fields
---
# v1.7.0 (2016-11-03)
## New Features
### IP address statuses ([#87](https://github.com/netbox-community/netbox/issues/87))
An IP address can now be designated as active, reserved, or DHCP. The DHCP status implies that the IP address is part of a DHCP pool and may or may not be assigned to a DHCP client.
### Top-to-bottom rack numbering ([#191](https://github.com/netbox-community/netbox/issues/191))
Racks can now be set to have descending rack units, with U1 at the top of the rack. When adding a device to a rack with descending units, be sure to position it in the **lowest-numbered** unit which it occupies (this will be physically the topmost unit).
## Improvements
* [#211](https://github.com/netbox-community/netbox/issues/211) - Allow device assignment and removal from IP address view
* [#630](https://github.com/netbox-community/netbox/issues/630) - Added a custom 404 page
* [#652](https://github.com/netbox-community/netbox/issues/652) - Use password input controls when editing secrets
* [#654](https://github.com/netbox-community/netbox/issues/654) - Added Cisco FlexStack and FlexStack Plus form factors
* [#661](https://github.com/netbox-community/netbox/issues/661) - Display relevant IP addressing when viewing a circuit
## Bug Fixes
* [#632](https://github.com/netbox-community/netbox/issues/632) - Use semicolons instead of commas to separate regexes in topology maps
* [#647](https://github.com/netbox-community/netbox/issues/647) - Extend form used when assigning an IP to a device
* [#657](https://github.com/netbox-community/netbox/issues/657) - Unicode error when adding device modules
* [#660](https://github.com/netbox-community/netbox/issues/660) - Corrected calculation of utilized space in rack list
* [#664](https://github.com/netbox-community/netbox/issues/664) - Fixed bulk creation of interfaces across multiple devices

View File

@@ -0,0 +1,106 @@
# v1.8.4 (2017-02-03)
## Improvements
* [#856](https://github.com/netbox-community/netbox/issues/856) - Strip whitespace from fields during CSV import
## Bug Fixes
* [#851](https://github.com/netbox-community/netbox/issues/851) - Resolve encoding issues during import/export (Python 3)
* [#854](https://github.com/netbox-community/netbox/issues/854) - Correct processing of get_return_url() in ObjectDeleteView
* [#859](https://github.com/netbox-community/netbox/issues/859) - Fix Javascript for connection status toggle button on device view
* [#861](https://github.com/netbox-community/netbox/issues/861) - Avoid overwriting device primary IP assignment from alternate family during bulk import of IP addresses
* [#865](https://github.com/netbox-community/netbox/issues/865) - Fix server error when attempting to delete a protected object parent (Python 3)
---
# v1.8.3 (2017-01-26)
## Improvements
* [#782](https://github.com/netbox-community/netbox/issues/782) - Allow filtering devices list by manufacturer
* [#820](https://github.com/netbox-community/netbox/issues/820) - Add VLAN column to parent prefixes table on IP address view
* [#821](https://github.com/netbox-community/netbox/issues/821) - Support for comma separation in bulk IP/interface creation
* [#827](https://github.com/netbox-community/netbox/issues/827) - **Introduced support for Python 3**
* [#836](https://github.com/netbox-community/netbox/issues/836) - Add "deprecated" status for IP addresses
* [#841](https://github.com/netbox-community/netbox/issues/841) - Merged search and filter forms on all object lists
## Bug Fixes
* [#816](https://github.com/netbox-community/netbox/issues/816) - Redirect back to parent prefix view after deleting child prefixes termination
* [#817](https://github.com/netbox-community/netbox/issues/817) - Update last_updated time of a circuit when editing a child termination
* [#830](https://github.com/netbox-community/netbox/issues/830) - Redirect user to device view after editing a device component
* [#840](https://github.com/netbox-community/netbox/issues/840) - Correct API path resolution for secrets when BASE_PATH is configured
* [#844](https://github.com/netbox-community/netbox/issues/844) - Apply order_naturally() to API interfaces list
* [#845](https://github.com/netbox-community/netbox/issues/845) - Fix missing edit/delete buttons on object tables for non-superusers
---
# v1.8.2 (2017-01-18)
## Improvements
* [#284](https://github.com/netbox-community/netbox/issues/284) - Enabled toggling of interface display order per device type
* [#760](https://github.com/netbox-community/netbox/issues/760) - Redirect user back to device view after deleting an assigned IP address
* [#783](https://github.com/netbox-community/netbox/issues/783) - Add a description field to the Circuit model
* [#797](https://github.com/netbox-community/netbox/issues/797) - Add description column to VLANs table
* [#803](https://github.com/netbox-community/netbox/issues/803) - Clarify that no child objects are deleted when deleting a prefix
* [#805](https://github.com/netbox-community/netbox/issues/805) - Linkify site column in device table
## Bug Fixes
* [#776](https://github.com/netbox-community/netbox/issues/776) - Prevent circuits from appearing twice while searching
* [#778](https://github.com/netbox-community/netbox/issues/778) - Corrected an issue preventing multiple interfaces with the same position ID from appearing in a device's interface list
* [#785](https://github.com/netbox-community/netbox/issues/785) - Trigger validation error when importing a prefix assigned to a nonexistent VLAN
* [#802](https://github.com/netbox-community/netbox/issues/802) - Fixed enforcement of ENFORCE_GLOBAL_UNIQUE for prefixes
* [#807](https://github.com/netbox-community/netbox/issues/807) - Redirect user back to form when adding IP addresses in bulk and "create and add another" is clicked
* [#810](https://github.com/netbox-community/netbox/issues/810) - Suppress unique IP validation on invalid IP addresses and prefixes
---
# v1.8.1 (2017-01-04)
## Improvements
* [#771](https://github.com/netbox-community/netbox/issues/771) - Don't automatically redirect user when only one object is returned in a list
## Bug Fixes
* [#764](https://github.com/netbox-community/netbox/issues/764) - Encapsulate in double quotes values containing commas when exporting to CSV
* [#767](https://github.com/netbox-community/netbox/issues/767) - Fixes xconnect_id error when searching for circuits
* [#769](https://github.com/netbox-community/netbox/issues/769) - Show default value for boolean custom fields
* [#772](https://github.com/netbox-community/netbox/issues/772) - Fixes TypeError in API RackUnitListView when no device is excluded
---
# v1.8.0 (2017-01-03)
## New Features
### Point-to-Point Circuits ([#49](https://github.com/netbox-community/netbox/issues/49))
Until now, NetBox has supported tracking only one end of a data circuit. This is fine for Internet connections where you don't care (or know) much about the provider side of the circuit, but many users need the ability to track inter-site circuits as well. This release expands circuit modeling so that each circuit can have an A and/or Z side. Each endpoint must be terminated to a site, and may optionally be terminated to a specific device and interface within that site.
### L4 Services ([#539](https://github.com/netbox-community/netbox/issues/539))
Our first major community contribution introduces the ability to track discrete TCP and UDP services associated with a device (for example, SSH or HTTP). Each service can optionally be assigned to one or more specific IP addresses belonging to the device. Thanks to [@if-fi](https://github.com/if-fi) for the addition!
## Improvements
* [#122](https://github.com/netbox-community/netbox/issues/122) - Added comments field to device types
* [#181](https://github.com/netbox-community/netbox/issues/181) - Implemented support for bulk IP address creation
* [#613](https://github.com/netbox-community/netbox/issues/613) - Added prefixes column to VLAN list; added VLAN column to prefix list
* [#716](https://github.com/netbox-community/netbox/issues/716) - Add ASN field to site bulk edit form
* [#722](https://github.com/netbox-community/netbox/issues/722) - Enabled custom fields for device types
* [#743](https://github.com/netbox-community/netbox/issues/743) - Enabled bulk creation of all device components
* [#756](https://github.com/netbox-community/netbox/issues/756) - Added contact details to site model
## Bug Fixes
* [#563](https://github.com/netbox-community/netbox/issues/563) - Allow a device to be flipped from one rack face to the other without moving it
* [#658](https://github.com/netbox-community/netbox/issues/658) - Enabled conditional treatment of network/broadcast IPs for a prefix by defining it as a pool
* [#741](https://github.com/netbox-community/netbox/issues/741) - Hide "select all" button for users without edit permissions
* [#744](https://github.com/netbox-community/netbox/issues/744) - Fixed export of sites without an AS number
* [#747](https://github.com/netbox-community/netbox/issues/747) - Fixed natural_order_by integer cast error on large numbers
* [#751](https://github.com/netbox-community/netbox/issues/751) - Fixed python-cryptography installation issue on Debian
* [#763](https://github.com/netbox-community/netbox/issues/763) - Added missing fields to CSV exports for racks and prefixes

View File

@@ -0,0 +1,136 @@
# v1.9.6 (2017-04-21)
## Improvements
* [#878](https://github.com/netbox-community/netbox/issues/878) - Merged IP addresses with interfaces list on device view
* [#1001](https://github.com/netbox-community/netbox/issues/1001) - Interface assignment can be modified when editing an IP address
* [#1084](https://github.com/netbox-community/netbox/issues/1084) - Include custom fields when creating IP addresses in bulk
## Bug Fixes
* [#1057](https://github.com/netbox-community/netbox/issues/1057) - Corrected VLAN validation during prefix import
* [#1061](https://github.com/netbox-community/netbox/issues/1061) - Fixed potential for script injection via create/edit/delete messages
* [#1070](https://github.com/netbox-community/netbox/issues/1070) - Corrected installation instructions for Python3 on CentOS/RHEL
* [#1071](https://github.com/netbox-community/netbox/issues/1071) - Protect assigned circuit termination when an interface is deleted
* [#1072](https://github.com/netbox-community/netbox/issues/1072) - Order LAG interfaces naturally on bulk interface edit form
* [#1074](https://github.com/netbox-community/netbox/issues/1074) - Require ncclient 0.5.3 (Python 3 fix)
* [#1090](https://github.com/netbox-community/netbox/issues/1090) - Improved installation documentation for Python 3
* [#1092](https://github.com/netbox-community/netbox/issues/1092) - Increase randomness in SECRET_KEY generation tool
---
# v1.9.5 (2017-04-06)
## Improvements
* [#1052](https://github.com/netbox-community/netbox/issues/1052) - Added rack reservation list and bulk delete views
## Bug Fixes
* [#1038](https://github.com/netbox-community/netbox/issues/1038) - Suppress upgrading to Django 1.11 (will be supported in v2.0)
* [#1037](https://github.com/netbox-community/netbox/issues/1037) - Fixed error on VLAN import with duplicate VLAN group names
* [#1047](https://github.com/netbox-community/netbox/issues/1047) - Correct ordering of numbered subinterfaces
* [#1051](https://github.com/netbox-community/netbox/issues/1051) - Upgraded django-rest-swagger
---
# v1.9.4-r1 (2017-04-04)
## Improvements
* [#362](https://github.com/netbox-community/netbox/issues/362) - Added per_page query parameter to control pagination page length
## Bug Fixes
* [#991](https://github.com/netbox-community/netbox/issues/991) - Correct server error on "create and connect another" interface connection
* [#1022](https://github.com/netbox-community/netbox/issues/1022) - Record user actions when creating IP addresses in bulk
* [#1027](https://github.com/netbox-community/netbox/issues/1027) - Fixed nav menu highlighting when BASE_PATH is set
* [#1034](https://github.com/netbox-community/netbox/issues/1034) - Added migration missing from v1.9.4 release
---
# v1.9.3 (2017-03-23)
## Improvements
* [#972](https://github.com/netbox-community/netbox/issues/972) - Add ability to filter connections list by device name
* [#974](https://github.com/netbox-community/netbox/issues/974) - Added MAC address filter to API interfaces list
* [#978](https://github.com/netbox-community/netbox/issues/978) - Allow filtering device types by function and subdevice role
* [#981](https://github.com/netbox-community/netbox/issues/981) - Allow filtering primary objects by a given set of IDs
* [#983](https://github.com/netbox-community/netbox/issues/983) - Include peer device names when listing circuits in device view
## Bug Fixes
* [#967](https://github.com/netbox-community/netbox/issues/967) - Fix error when assigning a new interface to a LAG
---
# v1.9.2 (2017-03-14)
## Bug Fixes
* [#950](https://github.com/netbox-community/netbox/issues/950) - Fix site_id error on child device import
* [#956](https://github.com/netbox-community/netbox/issues/956) - Correct bug affecting unnamed rackless devices
* [#957](https://github.com/netbox-community/netbox/issues/957) - Correct device site filter count to include unracked devices
* [#963](https://github.com/netbox-community/netbox/issues/963) - Fix bug in IPv6 address range expansion
* [#964](https://github.com/netbox-community/netbox/issues/964) - Fix bug when bulk editing/deleting filtered set of objects
---
# v1.9.1 (2017-03-08)
## Improvements
* [#945](https://github.com/netbox-community/netbox/issues/945) - Display the current user in the navigation menu
* [#946](https://github.com/netbox-community/netbox/issues/946) - Disregard mask length when filtering IP addresses by a parent prefix
## Bug Fixes
* [#941](https://github.com/netbox-community/netbox/issues/941) - Corrected old references to rack.site on Device
* [#943](https://github.com/netbox-community/netbox/issues/943) - Child prefixes missing on Python 3
* [#944](https://github.com/netbox-community/netbox/issues/944) - Corrected console and power connection form behavior
* [#948](https://github.com/netbox-community/netbox/issues/948) - Region name should be hyperlinked to site list
---
# v1.9.0-r1 (2017-03-03)
## New Features
### Rack Reservations ([#36](https://github.com/netbox-community/netbox/issues/36))
Users can now reserve an arbitrary number of units within a rack, adding a comment noting their intentions. Reservations do not interfere with installed devices: It is possible to reserve a unit for future use even if it is currently occupied by a device.
### Interface Groups ([#105](https://github.com/netbox-community/netbox/issues/105))
A new Link Aggregation Group (LAG) virtual form factor has been added. Physical interfaces can be assigned to a parent LAG interface to represent a port-channel or similar logical bundling of links.
### Regions ([#164](https://github.com/netbox-community/netbox/issues/164))
A new region model has been introduced to allow for the geographic organization of sites. Regions can be nested recursively to form a hierarchy.
### Rackless Devices ([#198](https://github.com/netbox-community/netbox/issues/198))
Previous releases required each device to be assigned to a particular rack within a site. This requirement has been relaxed so that devices must only be assigned to a site, and may optionally be assigned to a rack.
### Global VLANs ([#235](https://github.com/netbox-community/netbox/issues/235))
Assignment of VLANs and VLAN groups to sites is now optional, allowing for the representation of a VLAN spanning multiple sites.
## Improvements
* [#862](https://github.com/netbox-community/netbox/issues/862) - Show both IPv6 and IPv4 primary IPs in device list
* [#894](https://github.com/netbox-community/netbox/issues/894) - Expand device name max length to 64 characters
* [#898](https://github.com/netbox-community/netbox/issues/898) - Expanded circuits list in provider view rack face
* [#901](https://github.com/netbox-community/netbox/issues/901) - Support for filtering prefixes and IP addresses by mask length
## Bug Fixes
* [#872](https://github.com/netbox-community/netbox/issues/872) - Fixed TypeError on bulk IP address creation (Python 3)
* [#884](https://github.com/netbox-community/netbox/issues/884) - Preserve selected rack unit when changing a device's rack face
* [#892](https://github.com/netbox-community/netbox/issues/892) - Restored missing edit/delete buttons when viewing child prefixes and IP addresses from a parent object
* [#897](https://github.com/netbox-community/netbox/issues/897) - Fixed power connections CSV export
* [#903](https://github.com/netbox-community/netbox/issues/903) - Only alert on missing critical connections if present in the parent device type
* [#935](https://github.com/netbox-community/netbox/issues/935) - Fix form validation error when connecting an interface using live search
* [#937](https://github.com/netbox-community/netbox/issues/937) - Region assignment should be optional when creating a site
* [#938](https://github.com/netbox-community/netbox/issues/938) - Provider view yields an error if one or more circuits is assigned to a tenant

View File

@@ -0,0 +1,230 @@
# v2.0.10 (2017-07-14)
## Bug Fixes
* [#1312](https://github.com/netbox-community/netbox/issues/1312) - Catch error when attempting to activate a user key with an invalid private key
* [#1333](https://github.com/netbox-community/netbox/issues/1333) - Corrected label on is_console_server field of DeviceType bulk edit form
* [#1338](https://github.com/netbox-community/netbox/issues/1338) - Allow importing prefixes with "container" status
* [#1339](https://github.com/netbox-community/netbox/issues/1339) - Fixed disappearing checkbox column under django-tables2 v1.7+
* [#1342](https://github.com/netbox-community/netbox/issues/1342) - Allow designation of users and groups when creating/editing a secret role
---
# v2.0.9 (2017-07-10)
## Bug Fixes
* [#1319](https://github.com/netbox-community/netbox/issues/1319) - Fixed server error when attempting to create console/power connections
* [#1325](https://github.com/netbox-community/netbox/issues/1325) - Retain interface attachment when editing a circuit termination
---
# v2.0.8 (2017-07-05)
## Enhancements
* [#1298](https://github.com/netbox-community/netbox/issues/1298) - Calculate prefix utilization based on its status (container or non-container)
* [#1303](https://github.com/netbox-community/netbox/issues/1303) - Highlight installed interface connections in green on device view
* [#1315](https://github.com/netbox-community/netbox/issues/1315) - Enforce lowercase file extensions for image attachments
## Bug Fixes
* [#1279](https://github.com/netbox-community/netbox/issues/1279) - Fix primary_ip assignment during IP address import
* [#1281](https://github.com/netbox-community/netbox/issues/1281) - Show LLDP neighbors tab on device view only if necessary conditions are met
* [#1282](https://github.com/netbox-community/netbox/issues/1282) - Fixed tooltips on "mark connected/planned" toggle buttons for device connections
* [#1288](https://github.com/netbox-community/netbox/issues/1288) - Corrected permission name for deleting image attachments
* [#1289](https://github.com/netbox-community/netbox/issues/1289) - Retain inside NAT assignment when editing an IP address
* [#1297](https://github.com/netbox-community/netbox/issues/1297) - Allow passing custom field choice selection PKs to API as string-quoted integers
* [#1299](https://github.com/netbox-community/netbox/issues/1299) - Corrected permission name for adding services to devices
---
# v2.0.7 (2017-06-15)
## Enhancements
* [#626](https://github.com/netbox-community/netbox/issues/626) - Added bulk disconnect function for console/power/interface connections on device view
## Bug Fixes
* [#1238](https://github.com/netbox-community/netbox/issues/1238) - Fix error when editing an IP with a NAT assignment which has no assigned device
* [#1263](https://github.com/netbox-community/netbox/issues/1263) - Differentiate add and edit permissions for objects
* [#1265](https://github.com/netbox-community/netbox/issues/1265) - Fix console/power/interface connection validation when selecting a device via live search
* [#1266](https://github.com/netbox-community/netbox/issues/1266) - Prevent terminating a circuit to an already-connected interface
* [#1268](https://github.com/netbox-community/netbox/issues/1268) - Fix CSV import error under Python 3
* [#1273](https://github.com/netbox-community/netbox/issues/1273) - Corrected status choices in IP address import form
* [#1274](https://github.com/netbox-community/netbox/issues/1274) - Exclude unterminated circuits from topology maps
* [#1275](https://github.com/netbox-community/netbox/issues/1275) - Raise validation error on prefix import when multiple VLANs are found
---
# v2.0.6 (2017-06-12)
## Enhancements
* [#40](https://github.com/netbox-community/netbox/issues/40) - Added IP utilization graph to prefix list
* [#704](https://github.com/netbox-community/netbox/issues/704) - Allow filtering VLANs by group when editing prefixes
* [#913](https://github.com/netbox-community/netbox/issues/913) - Added headers to object CSV exports
* [#990](https://github.com/netbox-community/netbox/issues/990) - Enable logging configuration in configuration.py
* [#1180](https://github.com/netbox-community/netbox/issues/1180) - Simplified the process of finding related devices when viewing a device
## Bug Fixes
* [#1253](https://github.com/netbox-community/netbox/issues/1253) - Improved `upgrade.sh` to allow forcing Python2
---
# v2.0.5 (2017-06-08)
## Notes
The maximum number of objects an API consumer can request has been set to 1000 (e.g. `?limit=1000`). This limit can be modified by defining `MAX_PAGE_SIZE` in confgiuration.py. (To remove this limit, set `MAX_PAGE_SIZE=0`.)
## Enhancements
* [#655](https://github.com/netbox-community/netbox/issues/655) - Implemented header-based CSV import of objects
* [#1190](https://github.com/netbox-community/netbox/issues/1190) - Allow partial string matching when searching on custom fields
* [#1237](https://github.com/netbox-community/netbox/issues/1237) - Enabled setting limit=0 to disable pagination in API requests; added `MAX_PAGE_SIZE` configuration setting
## Bug Fixes
* [#837](https://github.com/netbox-community/netbox/issues/837) - Enforce uniqueness where applicable during bulk import of IP addresses
* [#1226](https://github.com/netbox-community/netbox/issues/1226) - Improved validation for custom field values submitted via the API
* [#1232](https://github.com/netbox-community/netbox/issues/1232) - Improved rack space validation on bulk import of devices (see #655)
* [#1235](https://github.com/netbox-community/netbox/issues/1235) - Fix permission name for adding/editing inventory items
* [#1236](https://github.com/netbox-community/netbox/issues/1236) - Truncate rack names in elevations list; add facility ID
* [#1239](https://github.com/netbox-community/netbox/issues/1239) - Fix server error when creating VLANGroup via API
* [#1243](https://github.com/netbox-community/netbox/issues/1243) - Catch ValueError in IP-based object filters
* [#1244](https://github.com/netbox-community/netbox/issues/1244) - Corrected "device" secrets filter to accept a device name
---
# v2.0.4 (2017-05-25)
## Bug Fixes
* [#1206](https://github.com/netbox-community/netbox/issues/1206) - Fix redirection in admin UI after activating secret keys when BASE_PATH is set
* [#1207](https://github.com/netbox-community/netbox/issues/1207) - Include nested LAG serializer when showing interface connections (API)
* [#1210](https://github.com/netbox-community/netbox/issues/1210) - Fix TemplateDoesNotExist errors on browsable API views
* [#1212](https://github.com/netbox-community/netbox/issues/1212) - Allow assigning new VLANs to global VLAN groups
* [#1213](https://github.com/netbox-community/netbox/issues/1213) - Corrected table header ordering links on object list views
* [#1214](https://github.com/netbox-community/netbox/issues/1214) - Add status to list of required fields on child device import form
* [#1219](https://github.com/netbox-community/netbox/issues/1219) - Fix image attachment URLs when BASE_PATH is set
* [#1220](https://github.com/netbox-community/netbox/issues/1220) - Suppressed innocuous warning about untracked migrations under Python 3
* [#1229](https://github.com/netbox-community/netbox/issues/1229) - Fix validation error on forms where API search is used
---
# v2.0.3 (2017-05-18)
## Enhancements
* [#1196](https://github.com/netbox-community/netbox/issues/1196) - Added a lag_id filter to the API interfaces view
* [#1198](https://github.com/netbox-community/netbox/issues/1198) - Allow filtering unracked devices on device list
## Bug Fixes
* [#1157](https://github.com/netbox-community/netbox/issues/1157) - Hide nav menu search bar on small displays
* [#1186](https://github.com/netbox-community/netbox/issues/1186) - Corrected VLAN edit form so that site assignment is not required
* [#1187](https://github.com/netbox-community/netbox/issues/1187) - Fixed table pagination by introducing a custom table template
* [#1188](https://github.com/netbox-community/netbox/issues/1188) - Serialize interface LAG as nested objected (API)
* [#1189](https://github.com/netbox-community/netbox/issues/1189) - Enforce consistent ordering of objects returned by a global search
* [#1191](https://github.com/netbox-community/netbox/issues/1191) - Bulk selection of IPs under a prefix incorrect when "select all" is used
* [#1195](https://github.com/netbox-community/netbox/issues/1195) - Unable to create an interface connection when searching for peer device
* [#1197](https://github.com/netbox-community/netbox/issues/1197) - Fixed status assignment during bulk import of devices, prefixes, IPs, and VLANs
* [#1199](https://github.com/netbox-community/netbox/issues/1199) - Bulk import of secrets does not prompt user to generate a session key
* [#1200](https://github.com/netbox-community/netbox/issues/1200) - Form validation error when connecting power ports to power outlets
---
# v2.0.2 (2017-05-15)
## Enhancements
* [#1122](https://github.com/netbox-community/netbox/issues/1122) - Include NAT inside IPs in IP address list
* [#1137](https://github.com/netbox-community/netbox/issues/1137) - Allow filtering devices list by rack
* [#1170](https://github.com/netbox-community/netbox/issues/1170) - Include A and Z sites for circuits in global search results
* [#1172](https://github.com/netbox-community/netbox/issues/1172) - Linkify racks in side-by-side elevations view
* [#1177](https://github.com/netbox-community/netbox/issues/1177) - Render planned connections as dashed lines on topology maps
* [#1179](https://github.com/netbox-community/netbox/issues/1179) - Adjust topology map text color based on node background
* On all object edit forms, allow filtering the tenant list by tenant group
## Bug Fixes
* [#1158](https://github.com/netbox-community/netbox/issues/1158) - Exception thrown when creating a device component with an invalid name
* [#1159](https://github.com/netbox-community/netbox/issues/1159) - Only superusers can see "edit IP" buttons on the device interfaces list
* [#1160](https://github.com/netbox-community/netbox/issues/1160) - Linkify secrets and tenants in global search results
* [#1161](https://github.com/netbox-community/netbox/issues/1161) - Fix "add another" behavior when creating an API token
* [#1166](https://github.com/netbox-community/netbox/issues/1166) - Fixed bulk IP address creation when assigning tenants
* [#1168](https://github.com/netbox-community/netbox/issues/1168) - Total count of objects missing from list view paginator
* [#1171](https://github.com/netbox-community/netbox/issues/1171) - Allow removing site assignment when bulk editing VLANs
* [#1173](https://github.com/netbox-community/netbox/issues/1173) - Tweak interface manager to fall back to naive ordering
---
# v2.0.1 (2017-05-10)
## Bug Fixes
* [#1149](https://github.com/netbox-community/netbox/issues/1149) - Port list does not populate when creating a console or power connection
* [#1150](https://github.com/netbox-community/netbox/issues/1150) - Error when uploading image attachments with Unicode names under Python 2
* [#1151](https://github.com/netbox-community/netbox/issues/1151) - Server error: name 'escape' is not defined
* [#1152](https://github.com/netbox-community/netbox/issues/1152) - Unable to edit user keys
* [#1153](https://github.com/netbox-community/netbox/issues/1153) - UnicodeEncodeError when searching for non-ASCII characters on Python 2
---
# v2.0.0 (2017-05-09)
## New Features
### API 2.0 ([#113](https://github.com/netbox-community/netbox/issues/113))
The NetBox API has been completely rewritten and now features full read/write ability.
### Image Attachments ([#152](https://github.com/netbox-community/netbox/issues/152))
Users are now able to attach photos and other images to sites, racks, and devices. (Please ensure that the new `media` directory is writable by the system account NetBox runs as.)
### Global Search ([#159](https://github.com/netbox-community/netbox/issues/159))
NetBox now supports searching across all primary object types at once.
### Rack Elevations View ([#951](https://github.com/netbox-community/netbox/issues/951))
A new view has been introduced to display the elevations of multiple racks side-by-side.
## Enhancements
* [#154](https://github.com/netbox-community/netbox/issues/154) - Expanded device status field to include options other than active/offline
* [#430](https://github.com/netbox-community/netbox/issues/430) - Include circuits when rendering topology maps
* [#578](https://github.com/netbox-community/netbox/issues/578) - Show topology maps not assigned to a site on the home view
* [#1100](https://github.com/netbox-community/netbox/issues/1100) - Add a "view all" link to completed bulk import views is_pool for prefixes)
* [#1110](https://github.com/netbox-community/netbox/issues/1110) - Expand bulk edit forms to include boolean fields (e.g. toggle is_pool for prefixes)
## Bug Fixes
From v1.9.6:
* [#403](https://github.com/netbox-community/netbox/issues/403) - Record console/power/interface connects and disconnects as user actions
* [#853](https://github.com/netbox-community/netbox/issues/853) - Added "status" field to device bulk import form
* [#1101](https://github.com/netbox-community/netbox/issues/1101) - Fix AJAX scripting for device component selection forms
* [#1103](https://github.com/netbox-community/netbox/issues/1103) - Correct handling of validation errors when creating IP addresses in bulk
* [#1104](https://github.com/netbox-community/netbox/issues/1104) - Fix VLAN assignment on prefix import
* [#1115](https://github.com/netbox-community/netbox/issues/1115) - Enabled responsive (side-scrolling) tables for small screens
* [#1116](https://github.com/netbox-community/netbox/issues/1116) - Correct object links on recursive deletion error
* [#1125](https://github.com/netbox-community/netbox/issues/1125) - Include MAC addresses on a device's interface list
* [#1144](https://github.com/netbox-community/netbox/issues/1144) - Allow multiple status selections for Prefix, IP address, and VLAN filters
From beta3:
* [#1113](https://github.com/netbox-community/netbox/issues/1113) - Fixed server error when attempting to delete an image attachment
* [#1114](https://github.com/netbox-community/netbox/issues/1114) - Suppress OSError when attempting to access a deleted image attachment
* [#1126](https://github.com/netbox-community/netbox/issues/1126) - Fixed server error when editing a user key via admin UI attachment
* [#1132](https://github.com/netbox-community/netbox/issues/1132) - Prompt user to unlock session key when importing secrets
## Additional Changes
* The Module DCIM model has been renamed to InventoryItem to better reflect its intended function, and to make room for work on [#824](https://github.com/netbox-community/netbox/issues/824).
* Redundant portions of the admin UI have been removed ([#973](https://github.com/netbox-community/netbox/issues/973)).
* The Docker build components have been moved into [their own repository](https://github.com/netbox-community/netbox-docker).

View File

@@ -0,0 +1,152 @@
# v2.1.6 (2017-10-11)
## Enhancements
* [#1548](https://github.com/netbox-community/netbox/issues/1548) - Automatically populate tenant assignment when adding an IP address from the prefix view
* [#1561](https://github.com/netbox-community/netbox/issues/1561) - Added primary IP to the devices table in global search
* [#1563](https://github.com/netbox-community/netbox/issues/1563) - Made necessary updates for Django REST Framework v3.7.0
---
# v2.1.5 (2017-09-25)
## Enhancements
* [#1484](https://github.com/netbox-community/netbox/issues/1484) - Added individual "add VLAN" buttons on the VLAN groups list
* [#1485](https://github.com/netbox-community/netbox/issues/1485) - Added `BANNER_LOGIN` configuration setting to display a banner on the login page
* [#1499](https://github.com/netbox-community/netbox/issues/1499) - Added utilization graph to child prefixes table
* [#1523](https://github.com/netbox-community/netbox/issues/1523) - Improved the natural ordering of interfaces (thanks to [@tarkatronic](https://github.com/tarkatronic))
* [#1536](https://github.com/netbox-community/netbox/issues/1536) - Improved formatting of aggregate prefix statistics
## Bug Fixes
* [#1469](https://github.com/netbox-community/netbox/issues/1469) - Allow a NAT IP to be assigned as the primary IP for a device
* [#1472](https://github.com/netbox-community/netbox/issues/1472) - Prevented truncation when displaying secret strings containing HTML characters
* [#1486](https://github.com/netbox-community/netbox/issues/1486) - Ignore subinterface IDs when validating LLDP neighbor connections
* [#1489](https://github.com/netbox-community/netbox/issues/1489) - Corrected server error on validation of empty required custom field
* [#1507](https://github.com/netbox-community/netbox/issues/1507) - Fixed error when creating the next available IP from a prefix within a VRF
* [#1520](https://github.com/netbox-community/netbox/issues/1520) - Redirect on GET request to bulk edit/delete views
* [#1522](https://github.com/netbox-community/netbox/issues/1522) - Removed object create/edit forms from the browsable API
---
# v2.1.4 (2017-08-30)
## Enhancements
* [#1326](https://github.com/netbox-community/netbox/issues/1326) - Added dropdown widget with common values for circuit speed fields
* [#1341](https://github.com/netbox-community/netbox/issues/1341) - Added a `MEDIA_ROOT` configuration setting to specify where uploaded files are stored on disk
* [#1376](https://github.com/netbox-community/netbox/issues/1376) - Ignore anycast addresses when detecting duplicate IPs
* [#1402](https://github.com/netbox-community/netbox/issues/1402) - Increased max length of name field for device components
* [#1431](https://github.com/netbox-community/netbox/issues/1431) - Added interface form factor for 10GBASE-CX4
* [#1432](https://github.com/netbox-community/netbox/issues/1432) - Added a `commit_rate` field to the circuits list search form
* [#1460](https://github.com/netbox-community/netbox/issues/1460) - Hostnames with no domain are now acceptable in custom URL fields
## Bug Fixes
* [#1429](https://github.com/netbox-community/netbox/issues/1429) - Fixed uptime formatting on device status page
* [#1433](https://github.com/netbox-community/netbox/issues/1433) - Fixed `devicetype_id` filter for DeviceType components
* [#1443](https://github.com/netbox-community/netbox/issues/1443) - Fixed API validation error involving custom field data
* [#1458](https://github.com/netbox-community/netbox/issues/1458) - Corrected permission name on prefix/VLAN roles list
---
# v2.1.3 (2017-08-15)
## Bug Fixes
* [#1330](https://github.com/netbox-community/netbox/issues/1330) - Raise validation error when assigning an unrelated IP as the primary IP for a device
* [#1389](https://github.com/netbox-community/netbox/issues/1389) - Avoid splitting carat/prefix on prefix list
* [#1400](https://github.com/netbox-community/netbox/issues/1400) - Removed redundant display of assigned device interface from IP address list
* [#1414](https://github.com/netbox-community/netbox/issues/1414) - Selecting a site from the rack filters automatically updates the available rack groups
* [#1419](https://github.com/netbox-community/netbox/issues/1419) - Allow editing image attachments without re-uploading an image
* [#1420](https://github.com/netbox-community/netbox/issues/1420) - Exclude virtual interfaces from device LLDP neighbors view
* [#1421](https://github.com/netbox-community/netbox/issues/1421) - Improved model validation logic for API serializers
* Fixed page title capitalization in the browsable API
---
# v2.1.2 (2017-08-04)
## Enhancements
* [#992](https://github.com/netbox-community/netbox/issues/992) - Allow the creation of multiple services per device with the same protocol and port
* Tweaked navigation menu styling
## Bug Fixes
* [#1388](https://github.com/netbox-community/netbox/issues/1388) - Fixed server error when searching globally for IPs/prefixes (rolled back #1379)
* [#1390](https://github.com/netbox-community/netbox/issues/1390) - Fixed IndexError when viewing available IPs within large IPv6 prefixes
---
# v2.1.1 (2017-08-02)
## Enhancements
* [#893](https://github.com/netbox-community/netbox/issues/893) - Allow filtering by null values for NullCharacterFields (e.g. return only unnamed devices)
* [#1368](https://github.com/netbox-community/netbox/issues/1368) - Render reservations in rack elevations view
* [#1374](https://github.com/netbox-community/netbox/issues/1374) - Added NAPALM_ARGS and NAPALM_TIMEOUT configiuration parameters
* [#1375](https://github.com/netbox-community/netbox/issues/1375) - Renamed `NETBOX_USERNAME` and `NETBOX_PASSWORD` configuration parameters to `NAPALM_USERNAME` and `NAPALM_PASSWORD`
* [#1379](https://github.com/netbox-community/netbox/issues/1379) - Allow searching devices by interface MAC address in global search
## Bug Fixes
* [#461](https://github.com/netbox-community/netbox/issues/461) - Display a validation error when attempting to assigning a new child device to a rack face/position
* [#1385](https://github.com/netbox-community/netbox/issues/1385) - Connected device API endpoint no longer requires authentication if `LOGIN_REQUIRED` is False
---
# v2.1.0 (2017-07-25)
## New Features
### IP Address Roles ([#819](https://github.com/netbox-community/netbox/issues/819))
The IP address model now supports the assignment of a functional role to help identify special-purpose IPs. These include:
* Loopback
* Secondary
* Anycast
* VIP
* VRRP
* HSRP
* GLBP
### Automatic Provisioning of Next Available IP ([#1246](https://github.com/netbox-community/netbox/issues/1246))
A new API endpoint has been added at `/api/ipam/prefixes/<pk>/available-ips/`. A GET request to this endpoint will return a list of available IP addresses within the prefix (up to the pagination limit). A POST request will automatically create and return the next available IP address.
### NAPALM Integration ([#1348](https://github.com/netbox-community/netbox/issues/1348))
The [NAPALM automation](https://napalm-automation.net/) library provides an abstracted interface for pulling live data (e.g. uptime, software version, running config, LLDP neighbors, etc.) from network devices. The NetBox API has been extended to support executing read-only NAPALM methods on devices defined in NetBox. To enable this functionality, ensure that NAPALM has been installed (`pip install napalm`) and the `NETBOX_USERNAME` and `NETBOX_PASSWORD` [configuration parameters](http://netbox.readthedocs.io/en/stable/configuration/optional-settings/#netbox_username) have been set in configuration.py.
## Enhancements
* [#838](https://github.com/netbox-community/netbox/issues/838) - Display details of all objects being edited/deleted in bulk
* [#1041](https://github.com/netbox-community/netbox/issues/1041) - Added enabled and MTU fields to the interface model
* [#1121](https://github.com/netbox-community/netbox/issues/1121) - Added asset_tag and description fields to the InventoryItem model
* [#1141](https://github.com/netbox-community/netbox/issues/1141) - Include RD when listing VRFs in a form selection field
* [#1203](https://github.com/netbox-community/netbox/issues/1203) - Implemented query filters for all models
* [#1218](https://github.com/netbox-community/netbox/issues/1218) - Added IEEE 802.11 wireless interface types
* [#1269](https://github.com/netbox-community/netbox/issues/1269) - Added circuit termination to interface serializer
* [#1320](https://github.com/netbox-community/netbox/issues/1320) - Removed checkbox from confirmation dialog
## Bug Fixes
* [#1079](https://github.com/netbox-community/netbox/issues/1079) - Order interfaces naturally via API
* [#1285](https://github.com/netbox-community/netbox/issues/1285) - Enforce model validation when creating/editing objects via the API
* [#1358](https://github.com/netbox-community/netbox/issues/1358) - Correct VRF example values in IP/prefix import forms
* [#1362](https://github.com/netbox-community/netbox/issues/1362) - Raise validation error when attempting to create an API key that's too short
* [#1371](https://github.com/netbox-community/netbox/issues/1371) - Extend DeviceSerializer.parent_device to include standard fields
## API changes
* Added a new API endpoint which makes [NAPALM](https://github.com/napalm-automation/napalm) accessible via NetBox
* Device components (console ports, power ports, interfaces, etc.) can only be filtered by a single device name or ID. This limitation was necessary to allow the natural ordering of interfaces according to the device's parent device type.
* Added two new fields to the interface serializer: `enabled` (boolean) and `mtu` (unsigned integer)
* Modified the interface serializer to include three discrete fields relating to connections: `is_connected` (boolean), `interface_connection`, and `circuit_termination`
* Added two new fields to the inventory item serializer: `asset_tag` and `description`
* Added "wireless" to interface type filter (in addition to physical, virtual, and LAG)
* Added a new endpoint at /api/ipam/prefixes/<pk>/available-ips/ to retrieve or create available IPs within a prefix
* Extended `parent_device` on DeviceSerializer to include the `url` and `display_name` of the parent Device, and the `url` of the DeviceBay

View File

@@ -0,0 +1,225 @@
# v2.2.10 (2018-02-21)
## Enhancements
* [#78](https://github.com/netbox-community/netbox/issues/78) - Extended topology maps to support console and power connections
* [#1693](https://github.com/netbox-community/netbox/issues/1693) - Allow specifying loose or exact matching for custom field filters
* [#1714](https://github.com/netbox-community/netbox/issues/1714) - Standardized CSV export functionality for all object lists
* [#1876](https://github.com/netbox-community/netbox/issues/1876) - Added explanatory title text to disabled NAPALM buttons on device view
* [#1885](https://github.com/netbox-community/netbox/issues/1885) - Added a device filter field for primary IP
## Bug Fixes
* [#1858](https://github.com/netbox-community/netbox/issues/1858) - Include device/VM count for cluster list in global search results
* [#1859](https://github.com/netbox-community/netbox/issues/1859) - Implemented support for line breaks within CSV fields
* [#1860](https://github.com/netbox-community/netbox/issues/1860) - Do not populate initial values for custom fields when editing objects in bulk
* [#1869](https://github.com/netbox-community/netbox/issues/1869) - Corrected ordering of VRFs with duplicate names
* [#1886](https://github.com/netbox-community/netbox/issues/1886) - Allow setting the primary IPv4/v6 address for a virtual machine via the web UI
---
# v2.2.9 (2018-01-31)
## Enhancements
* [#144](https://github.com/netbox-community/netbox/issues/144) - Implemented bulk import/edit/delete views for InventoryItems
* [#1073](https://github.com/netbox-community/netbox/issues/1073) - Include prefixes/IPs from all VRFs when viewing the children of a container prefix in the global table
* [#1366](https://github.com/netbox-community/netbox/issues/1366) - Enable searching for regions by name/slug
* [#1406](https://github.com/netbox-community/netbox/issues/1406) - Display tenant description as title text in object tables
* [#1824](https://github.com/netbox-community/netbox/issues/1824) - Add virtual machine count to platforms list
* [#1835](https://github.com/netbox-community/netbox/issues/1835) - Consistent positioning of previous/next rack buttons
## Bug Fixes
* [#1621](https://github.com/netbox-community/netbox/issues/1621) - Tweaked LLDP interface name evaluation logic
* [#1765](https://github.com/netbox-community/netbox/issues/1765) - Improved rendering of null options for model choice fields in filter forms
* [#1807](https://github.com/netbox-community/netbox/issues/1807) - Populate VRF from parent when creating a new prefix
* [#1809](https://github.com/netbox-community/netbox/issues/1809) - Populate tenant assignment from parent when creating a new prefix
* [#1818](https://github.com/netbox-community/netbox/issues/1818) - InventoryItem API serializer no longer requires specifying a null value for items with no parent
* [#1845](https://github.com/netbox-community/netbox/issues/1845) - Correct display of VMs in list with no role assigned
* [#1850](https://github.com/netbox-community/netbox/issues/1850) - Fix TypeError when attempting IP address import if only unnamed devices exist
---
# v2.2.8 (2017-12-20)
## Enhancements
* [#1771](https://github.com/netbox-community/netbox/issues/1771) - Added name filter for racks
* [#1772](https://github.com/netbox-community/netbox/issues/1772) - Added position filter for devices
* [#1773](https://github.com/netbox-community/netbox/issues/1773) - Moved child prefixes table to its own view
* [#1774](https://github.com/netbox-community/netbox/issues/1774) - Include a button to refine search results for all object types under global search
* [#1784](https://github.com/netbox-community/netbox/issues/1784) - Added `cluster_type` filters for virtual machines
## Bug Fixes
* [#1766](https://github.com/netbox-community/netbox/issues/1766) - Fixed display of "select all" button on device power outlets list
* [#1767](https://github.com/netbox-community/netbox/issues/1767) - Use proper template for 404 responses
* [#1778](https://github.com/netbox-community/netbox/issues/1778) - Preserve initial VRF assignment when adding IP addresses in bulk from a prefix
* [#1783](https://github.com/netbox-community/netbox/issues/1783) - Added `vm_role` filter for device roles
* [#1785](https://github.com/netbox-community/netbox/issues/1785) - Omit filter forms from browsable API
* [#1787](https://github.com/netbox-community/netbox/issues/1787) - Added missing site field to virtualization cluster CSV export
---
# v2.2.7 (2017-12-07)
## Enhancements
* [#1722](https://github.com/netbox-community/netbox/issues/1722) - Added virtual machine count to site view
* [#1737](https://github.com/netbox-community/netbox/issues/1737) - Added a `contains` API filter to find all prefixes containing a given IP or prefix
## Bug Fixes
* [#1712](https://github.com/netbox-community/netbox/issues/1712) - Corrected tenant inheritance for new IP addresses created from a parent prefix
* [#1721](https://github.com/netbox-community/netbox/issues/1721) - Differentiated child IP count from utilization percentage for prefixes
* [#1740](https://github.com/netbox-community/netbox/issues/1740) - Delete session_key cookie on logout
* [#1741](https://github.com/netbox-community/netbox/issues/1741) - Fixed Unicode support for secret plaintexts
* [#1743](https://github.com/netbox-community/netbox/issues/1743) - Include number of instances for device types in global search
* [#1751](https://github.com/netbox-community/netbox/issues/1751) - Corrected filtering for IPv6 addresses containing letters
* [#1756](https://github.com/netbox-community/netbox/issues/1756) - Improved natural ordering of console server ports and power outlets
---
# v2.2.6 (2017-11-16)
## Enhancements
* [#1669](https://github.com/netbox-community/netbox/issues/1669) - Clicking "add an IP" from the prefix view will default to the first available IP within the prefix
## Bug Fixes
* [#1397](https://github.com/netbox-community/netbox/issues/1397) - Display global search in navigation menu unless display is less than 1200px wide
* [#1599](https://github.com/netbox-community/netbox/issues/1599) - Reduce mobile cut-off for navigation menu to 960px
* [#1715](https://github.com/netbox-community/netbox/issues/1715) - Added missing import buttons on object lists
* [#1717](https://github.com/netbox-community/netbox/issues/1717) - Fixed interface validation for virtual machines
* [#1718](https://github.com/netbox-community/netbox/issues/1718) - Set empty label to "Global" or VRF field in IP assignment form
---
# v2.2.5 (2017-11-14)
## Enhancements
* [#1512](https://github.com/netbox-community/netbox/issues/1512) - Added a view to search for an IP address being assigned to an interface
* [#1679](https://github.com/netbox-community/netbox/issues/1679) - Added IP address roles to device/VM interface lists
* [#1683](https://github.com/netbox-community/netbox/issues/1683) - Replaced default 500 handler with custom middleware to provide preliminary troubleshooting assistance
* [#1684](https://github.com/netbox-community/netbox/issues/1684) - Replaced prefix `parent` filter with `within` and `within_include`
## Bug Fixes
* [#1471](https://github.com/netbox-community/netbox/issues/1471) - Correct bulk selection of IP addresses within a prefix assigned to a VRF
* [#1642](https://github.com/netbox-community/netbox/issues/1642) - Validate device type classification when creating console server ports and power outlets
* [#1650](https://github.com/netbox-community/netbox/issues/1650) - Correct numeric ordering for interfaces with no alphabetic type
* [#1676](https://github.com/netbox-community/netbox/issues/1676) - Correct filtering of child prefixes upon bulk edit/delete from the parent prefix view
* [#1689](https://github.com/netbox-community/netbox/issues/1689) - Disregard IP address mask when filtering for child IPs of a prefix
* [#1696](https://github.com/netbox-community/netbox/issues/1696) - Fix for NAPALM v2.0+
* [#1699](https://github.com/netbox-community/netbox/issues/1699) - Correct nested representation in the API of primary IPs for virtual machines and add missing primary_ip property
* [#1701](https://github.com/netbox-community/netbox/issues/1701) - Fixed validation in `extras/0008_reports.py` migration for certain versions of PostgreSQL
* [#1703](https://github.com/netbox-community/netbox/issues/1703) - Added API serializer validation for custom integer fields
* [#1705](https://github.com/netbox-community/netbox/issues/1705) - Fixed filtering of devices with a status of offline
---
# v2.2.4 (2017-10-31)
## Bug Fixes
* [#1670](https://github.com/netbox-community/netbox/issues/1670) - Fixed server error when calling certain filters (regression from #1649)
---
# v2.2.3 (2017-10-31)
## Enhancements
* [#999](https://github.com/netbox-community/netbox/issues/999) - Display devices on which circuits are terminated in circuits list
* [#1491](https://github.com/netbox-community/netbox/issues/1491) - Added initial data for the virtualization app
* [#1620](https://github.com/netbox-community/netbox/issues/1620) - Loosen IP address search filter to match all IPs that start with the given string
* [#1631](https://github.com/netbox-community/netbox/issues/1631) - Added a `post_run` method to the Report class
* [#1666](https://github.com/netbox-community/netbox/issues/1666) - Allow modifying the owner of a rack reservation
## Bug Fixes
* [#1513](https://github.com/netbox-community/netbox/issues/1513) - Correct filtering of custom field choices
* [#1603](https://github.com/netbox-community/netbox/issues/1603) - Hide selection checkboxes for tables with no available actions
* [#1618](https://github.com/netbox-community/netbox/issues/1618) - Allow bulk deletion of all virtual machines
* [#1619](https://github.com/netbox-community/netbox/issues/1619) - Correct text-based filtering of IP network and address fields
* [#1624](https://github.com/netbox-community/netbox/issues/1624) - Add VM count to device roles table
* [#1634](https://github.com/netbox-community/netbox/issues/1634) - Cluster should not be a required field when importing child devices
* [#1649](https://github.com/netbox-community/netbox/issues/1649) - Correct filtering on null values (e.g. ?tenant_id=0) for django-filters v1.1.0+
* [#1653](https://github.com/netbox-community/netbox/issues/1653) - Remove outdated description for DeviceType's `is_network_device` flag
* [#1664](https://github.com/netbox-community/netbox/issues/1664) - Added missing `serial` field in default rack CSV export
---
# v2.2.2 (2017-10-17)
## Enhancements
* [#1580](https://github.com/netbox-community/netbox/issues/1580) - Allow cluster assignment when bulk importing devices
* [#1587](https://github.com/netbox-community/netbox/issues/1587) - Add primary IP column for virtual machines in global search results
## Bug Fixes
* [#1498](https://github.com/netbox-community/netbox/issues/1498) - Avoid duplicating nodes when generating topology maps
* [#1579](https://github.com/netbox-community/netbox/issues/1579) - Devices already assigned to a cluster cannot be added to a different cluster
* [#1582](https://github.com/netbox-community/netbox/issues/1582) - Add `virtual_machine` attribute to IPAddress
* [#1584](https://github.com/netbox-community/netbox/issues/1584) - Colorized virtual machine role column
* [#1585](https://github.com/netbox-community/netbox/issues/1585) - Fixed slug-based filtering of virtual machines
* [#1605](https://github.com/netbox-community/netbox/issues/1605) - Added clusters and virtual machines to object list for global search
* [#1609](https://github.com/netbox-community/netbox/issues/1609) - Added missing `virtual_machine` field to IP address interface serializer
---
# v2.2.1 (2017-10-12)
## Bug Fixes
* [#1576](https://github.com/netbox-community/netbox/issues/1576) - Moved PostgreSQL validation logic into the relevant migration (fixed ImproperlyConfigured exception on init)
---
# v2.2.0 (2017-10-12)
**Note:** This release requires PostgreSQL 9.4 or higher. Do not attempt to upgrade unless you are running at least PostgreSQL 9.4.
**Note:** The release replaces the deprecated pycrypto library with [pycryptodome](https://github.com/Legrandin/pycryptodome). The upgrade script has been extended to automatically uninstall the old library, but please verify your installed packages with `pip freeze | grep pycrypto` if you run into problems.
## New Features
### Virtual Machines and Clusters ([#142](https://github.com/netbox-community/netbox/issues/142))
Our second-most popular feature request has arrived! NetBox now supports the creation of virtual machines, which can be assigned virtual interfaces and IP addresses. VMs are arranged into clusters, each of which has a type and (optionally) a group.
### Custom Validation Reports ([#1511](https://github.com/netbox-community/netbox/issues/1511))
Users can now create custom reports which are run to validate data in NetBox. Reports work very similar to Python unit tests: Each report inherits from NetBox's Report class and contains one or more test method. Reports can be run and retrieved via the web UI, API, or CLI. See [the docs](http://netbox.readthedocs.io/en/stable/miscellaneous/reports/) for more info.
## Enhancements
* [#494](https://github.com/netbox-community/netbox/issues/494) - Include asset tag in device info pop-up on rack elevation
* [#1444](https://github.com/netbox-community/netbox/issues/1444) - Added a `serial` field to the rack model
* [#1479](https://github.com/netbox-community/netbox/issues/1479) - Added an IP address role for CARP
* [#1506](https://github.com/netbox-community/netbox/issues/1506) - Extended rack facility ID field from 30 to 50 characters
* [#1510](https://github.com/netbox-community/netbox/issues/1510) - Added ability to search by name when adding devices to a cluster
* [#1527](https://github.com/netbox-community/netbox/issues/1527) - Replace deprecated pycrypto library with pycryptodome
* [#1551](https://github.com/netbox-community/netbox/issues/1551) - Added API endpoints listing static field choices for each app
* [#1556](https://github.com/netbox-community/netbox/issues/1556) - Added CPAK, CFP2, and CFP4 100GE interface form factors
* Added CSV import views for all object types
## Bug Fixes
* [#1550](https://github.com/netbox-community/netbox/issues/1550) - Corrected interface connections link in navigation menu
* [#1554](https://github.com/netbox-community/netbox/issues/1554) - Don't require form_factor when creating an interface assigned to a virtual machine
* [#1557](https://github.com/netbox-community/netbox/issues/1557) - Added filtering for virtual machine interfaces
* [#1567](https://github.com/netbox-community/netbox/issues/1567) - Prompt user for session key when importing secrets
## API Changes
* Introduced the virtualization app and its associated endpoints at `/api/virtualization`
* Added the `/api/extras/reports` endpoint for fetching and running reports
* The `ipam.Service` and `dcim.Interface` models now have a `virtual_machine` field in addition to the `device` field. Only one of the two fields may be defined for each object
* Added a `vm_role` field to `dcim.DeviceRole`, which indicates whether a role is suitable for assigned to a virtual machine
* Added a `serial` field to 'dcim.Rack` for serial numbers
* Each app now has a `_choices` endpoint, which lists the available options for all model field with static choices (e.g. interface form factors)

View File

@@ -0,0 +1,223 @@
# v2.3.7 (2018-07-26)
## Enhancements
* [#2166](https://github.com/netbox-community/netbox/issues/2166) - Enable partial matching on device asset_tag during search
## Bug Fixes
* [#1977](https://github.com/netbox-community/netbox/issues/1977) - Fixed exception when creating a virtual chassis with a non-master device in position 1
* [#1992](https://github.com/netbox-community/netbox/issues/1992) - Isolate errors when one of multiple NAPALM methods fails
* [#2202](https://github.com/netbox-community/netbox/issues/2202) - Ditched half-baked concept of tenancy inheritance via VRF
* [#2222](https://github.com/netbox-community/netbox/issues/2222) - IP addresses created via the `available-ips` API endpoint should have the same mask as their parent prefix (not /32)
* [#2231](https://github.com/netbox-community/netbox/issues/2231) - Remove `get_absolute_url()` from DeviceRole (can apply to devices or VMs)
* [#2250](https://github.com/netbox-community/netbox/issues/2250) - Include stat counters on report result navigation
* [#2255](https://github.com/netbox-community/netbox/issues/2255) - Corrected display of results in reports list
* [#2256](https://github.com/netbox-community/netbox/issues/2256) - Prevent navigation menu overlap when jumping to test results on report page
* [#2257](https://github.com/netbox-community/netbox/issues/2257) - Corrected casting of RIR utilization stats as floats
* [#2266](https://github.com/netbox-community/netbox/issues/2266) - Permit additional logging of exceptions beyond custom middleware
---
# v2.3.6 (2018-07-16)
## Enhancements
* [#2107](https://github.com/netbox-community/netbox/issues/2107) - Added virtual chassis to global search
* [#2125](https://github.com/netbox-community/netbox/issues/2125) - Show child status in device bay list
## Bug Fixes
* [#2214](https://github.com/netbox-community/netbox/issues/2214) - Error when assigning a VLAN to an interface on a VM in a cluster with no assigned site
* [#2239](https://github.com/netbox-community/netbox/issues/2239) - Pin django-filter to version 1.1.0
---
# v2.3.5 (2018-07-02)
## Enhancements
* [#2159](https://github.com/netbox-community/netbox/issues/2159) - Allow custom choice field to specify a default choice
* [#2177](https://github.com/netbox-community/netbox/issues/2177) - Include device serial number in rack elevation pop-up
* [#2194](https://github.com/netbox-community/netbox/issues/2194) - Added `address` filter to IPAddress model
## Bug Fixes
* [#1826](https://github.com/netbox-community/netbox/issues/1826) - Corrected description of security parameters under API definition
* [#2021](https://github.com/netbox-community/netbox/issues/2021) - Fix recursion error when viewing API docs under Python 3.4
* [#2064](https://github.com/netbox-community/netbox/issues/2064) - Disable calls to online swagger validator
* [#2173](https://github.com/netbox-community/netbox/issues/2173) - Fixed IndexError when automatically allocating IP addresses from large IPv6 prefixes
* [#2181](https://github.com/netbox-community/netbox/issues/2181) - Raise validation error on invalid `prefix_length` when allocating next-available prefix
* [#2182](https://github.com/netbox-community/netbox/issues/2182) - ValueError can be raised when viewing the interface connections table
* [#2191](https://github.com/netbox-community/netbox/issues/2191) - Added missing static choices to circuits and DCIM API endpoints
* [#2192](https://github.com/netbox-community/netbox/issues/2192) - Prevent a 0U device from being assigned to a rack position
---
# v2.3.4 (2018-06-07)
## Bug Fixes
* [#2066](https://github.com/netbox-community/netbox/issues/2066) - Catch `AddrFormatError` exception on invalid IP addresses
* [#2075](https://github.com/netbox-community/netbox/issues/2075) - Enable tenant assignment when creating a rack reservation via the API
* [#2083](https://github.com/netbox-community/netbox/issues/2083) - Add missing export button to rack roles list view
* [#2087](https://github.com/netbox-community/netbox/issues/2087) - Don't overwrite existing vc_position of master device when creating a virtual chassis
* [#2093](https://github.com/netbox-community/netbox/issues/2093) - Fix link to circuit termination in device interfaces table
* [#2097](https://github.com/netbox-community/netbox/issues/2097) - Fixed queryset-based bulk deletion of clusters and regions
* [#2098](https://github.com/netbox-community/netbox/issues/2098) - Fixed missing checkboxes for host devices in cluster view
* [#2127](https://github.com/netbox-community/netbox/issues/2127) - Prevent non-conntectable interfaces from being connected
* [#2143](https://github.com/netbox-community/netbox/issues/2143) - Accept null value for empty time zone field
* [#2148](https://github.com/netbox-community/netbox/issues/2148) - Do not force timezone selection when editing sites in bulk
* [#2150](https://github.com/netbox-community/netbox/issues/2150) - Fix display of LLDP neighbors when interface name contains a colon
---
# v2.3.3 (2018-04-19)
## Enhancements
* [#1990](https://github.com/netbox-community/netbox/issues/1990) - Improved search function when assigning an IP address to an interface
## Bug Fixes
* [#1975](https://github.com/netbox-community/netbox/issues/1975) - Correct filtering logic for custom boolean fields
* [#1988](https://github.com/netbox-community/netbox/issues/1988) - Order interfaces naturally when bulk renaming
* [#1993](https://github.com/netbox-community/netbox/issues/1993) - Corrected status choices in site CSV import form
* [#1999](https://github.com/netbox-community/netbox/issues/1999) - Added missing description field to site edit form
* [#2012](https://github.com/netbox-community/netbox/issues/2012) - Fixed deselection of an IP address as the primary IP for its parent device/VM
* [#2014](https://github.com/netbox-community/netbox/issues/2014) - Allow assignment of VLANs to VM interfaces via the API
* [#2019](https://github.com/netbox-community/netbox/issues/2019) - Avoid casting oversized numbers as integers
* [#2022](https://github.com/netbox-community/netbox/issues/2022) - Show 0 for zero-value fields on CSV export
* [#2023](https://github.com/netbox-community/netbox/issues/2023) - Manufacturer should not be a required field when importing platforms
* [#2037](https://github.com/netbox-community/netbox/issues/2037) - Fixed IndexError exception when attempting to create a new rack reservation
---
# v2.3.2 (2018-03-22)
## Enhancements
* [#1586](https://github.com/netbox-community/netbox/issues/1586) - Extend bulk interface creation to support alphanumeric characters
* [#1866](https://github.com/netbox-community/netbox/issues/1866) - Introduced AnnotatedMultipleChoiceField for filter forms
* [#1930](https://github.com/netbox-community/netbox/issues/1930) - Switched to drf-yasg for Swagger API documentation
* [#1944](https://github.com/netbox-community/netbox/issues/1944) - Enable assigning VLANs to virtual machine interfaces
* [#1945](https://github.com/netbox-community/netbox/issues/1945) - Implemented a VLAN members view
* [#1949](https://github.com/netbox-community/netbox/issues/1949) - Added a button to view elevations on rack groups list
* [#1952](https://github.com/netbox-community/netbox/issues/1952) - Implemented a more robust mechanism for assigning VLANs to interfaces
## Bug Fixes
* [#1948](https://github.com/netbox-community/netbox/issues/1948) - Fix TypeError when attempting to add a member to an existing virtual chassis
* [#1951](https://github.com/netbox-community/netbox/issues/1951) - Fix TypeError exception when importing platforms
* [#1953](https://github.com/netbox-community/netbox/issues/1953) - Ignore duplicate IPs when calculating prefix utilization
* [#1955](https://github.com/netbox-community/netbox/issues/1955) - Require a plaintext value when creating a new secret
* [#1978](https://github.com/netbox-community/netbox/issues/1978) - Include all virtual chassis member interfaces in LLDP neighbors view
* [#1980](https://github.com/netbox-community/netbox/issues/1980) - Fixed bug when trying to nullify a selection custom field under Python 2
---
# v2.3.1 (2018-03-01)
## Enhancements
* [#1910](https://github.com/netbox-community/netbox/issues/1910) - Added filters for cluster group and cluster type
## Bug Fixes
* [#1915](https://github.com/netbox-community/netbox/issues/1915) - Redirect to device view after deleting a component
* [#1919](https://github.com/netbox-community/netbox/issues/1919) - Prevent exception when attempting to create a virtual machine without selecting devices
* [#1921](https://github.com/netbox-community/netbox/issues/1921) - Ignore ManyToManyFields when validating a new object created via the API
* [#1924](https://github.com/netbox-community/netbox/issues/1924) - Include VID in VLAN lists when editing an interface
* [#1926](https://github.com/netbox-community/netbox/issues/1926) - Prevent reassignment of parent device when bulk editing VC member interfaces
* [#1927](https://github.com/netbox-community/netbox/issues/1927) - Include all VC member interfaces on A side when creating a new interface connection
* [#1928](https://github.com/netbox-community/netbox/issues/1928) - Fixed form validation when modifying VLANs assigned to an interface
* [#1934](https://github.com/netbox-community/netbox/issues/1934) - Fixed exception when rendering export template on an object type with custom fields assigned
* [#1935](https://github.com/netbox-community/netbox/issues/1935) - Correct API validation of VLANs assigned to interfaces
* [#1936](https://github.com/netbox-community/netbox/issues/1936) - Trigger validation error when attempting to create a virtual chassis without specifying member positions
---
# v2.3.0 (2018-02-26)
## New Features
### Virtual Chassis ([#99](https://github.com/netbox-community/netbox/issues/99))
A virtual chassis represents a set of physical devices with a shared control plane; for example, a stack of switches managed as a single device. Viewing the master device of a virtual chassis will show all member interfaces and IP addresses.
### Interface VLAN Assignments ([#150](https://github.com/netbox-community/netbox/issues/150))
Interfaces can now be assigned an 802.1Q mode (access or trunked) and associated with particular VLANs. Thanks to [John Anderson](https://github.com/lampwins) for his work on this!
### Bulk Object Creation via the API ([#1553](https://github.com/netbox-community/netbox/issues/1553))
The REST API now supports the creation of multiple objects of the same type using a single POST request. For example, to create multiple devices:
```
curl -X POST -H "Authorization: Token <TOKEN>" -H "Content-Type: application/json" -H "Accept: application/json; indent=4" http://localhost:8000/api/dcim/devices/ --data '[
{"name": "device1", "device_type": 24, "device_role": 17, "site": 6},
{"name": "device2", "device_type": 24, "device_role": 17, "site": 6},
{"name": "device3", "device_type": 24, "device_role": 17, "site": 6},
]'
```
Bulk creation is all-or-none: If any of the creations fails, the entire operation is rolled back.
### Automatic Provisioning of Next Available Prefixes ([#1694](https://github.com/netbox-community/netbox/issues/1694))
Similar to IP addresses, NetBox now supports automated provisioning of available prefixes from within a parent prefix. For example, to retrieve the next three available /28s within a parent /24:
```
curl -X POST -H "Authorization: Token <TOKEN>" -H "Content-Type: application/json" -H "Accept: application/json; indent=4" http://localhost:8000/api/ipam/prefixes/10153/available-prefixes/ --data '[
{"prefix_length": 28},
{"prefix_length": 28},
{"prefix_length": 28}
]'
```
If the parent prefix cannot accommodate all requested prefixes, the operation is cancelled and no new prefixes are created.
### Bulk Renaming of Device/VM Components ([#1781](https://github.com/netbox-community/netbox/issues/1781))
Device components (interfaces, console ports, etc.) can now be renamed in bulk via the web interface. This was implemented primarily to support the bulk renumbering of interfaces whose parent is part of a virtual chassis.
## Enhancements
* [#1283](https://github.com/netbox-community/netbox/issues/1283) - Added a `time_zone` field to the site model
* [#1321](https://github.com/netbox-community/netbox/issues/1321) - Added `created` and `last_updated` fields for relevant models to their API serializers
* [#1553](https://github.com/netbox-community/netbox/issues/1553) - Introduced support for bulk object creation via the API
* [#1592](https://github.com/netbox-community/netbox/issues/1592) - Added tenancy assignment for rack reservations
* [#1744](https://github.com/netbox-community/netbox/issues/1744) - Allow associating a platform with a specific manufacturer
* [#1758](https://github.com/netbox-community/netbox/issues/1758) - Added a `status` field to the site model
* [#1821](https://github.com/netbox-community/netbox/issues/1821) - Added a `description` field to the site model
* [#1864](https://github.com/netbox-community/netbox/issues/1864) - Added a `status` field to the circuit model
## Bug Fixes
* [#1136](https://github.com/netbox-community/netbox/issues/1136) - Enforce model validation during bulk update
* [#1645](https://github.com/netbox-community/netbox/issues/1645) - Simplified interface serialzier for IP addresses and optimized API view queryset
* [#1838](https://github.com/netbox-community/netbox/issues/1838) - Fix KeyError when attempting to create a VirtualChassis with no devices selected
* [#1847](https://github.com/netbox-community/netbox/issues/1847) - RecursionError when a virtual chasis master device has no name
* [#1848](https://github.com/netbox-community/netbox/issues/1848) - Allow null value for interface encapsulation mode
* [#1867](https://github.com/netbox-community/netbox/issues/1867) - Allow filtering on device status with multiple values
* [#1881](https://github.com/netbox-community/netbox/issues/1881)* - Fixed bulk editing of interface 802.1Q settings
* [#1884](https://github.com/netbox-community/netbox/issues/1884)* - Provide additional context to identify devices when creating/editing a virtual chassis
* [#1907](https://github.com/netbox-community/netbox/issues/1907) - Allow removing an IP as the primary for a device when editing the IP directly
\* New since v2.3-beta2
## Breaking Changes
* Constants representing device status have been renamed for clarity (for example, `STATUS_ACTIVE` is now `DEVICE_STATUS_ACTIVE`). Custom validation reports will need to be updated if they reference any of these constants.
## API Changes
* API creation calls now accept either a single JSON object or a list of JSON objects. If multiple objects are passed and one or more them fail validation, no objects will be created.
* Added `created` and `last_updated` fields for objects inheriting from CreatedUpdatedModel.
* Removed the `parent` filter for prefixes (use `within` or `within_include` instead).
* The IP address serializer now includes only a minimal nested representation of the assigned interface (if any) and its parent device or virtual machine.
* The rack reservation serializer now includes a nested representation of its owning user (as well as the assigned tenant, if any).
* Added endpoints for virtual chassis and VC memberships.
* Added `status`, `time_zone` (pytz format), and `description` fields to dcim.Site.
* Added a `manufacturer` foreign key field on dcim.Platform.
* Added a `status` field on circuits.Circuit.

View File

@@ -0,0 +1,233 @@
# v2.4.9 (2018-12-07)
## Enhancements
* [#2089](https://github.com/netbox-community/netbox/issues/2089) - Add SONET interface form factors
* [#2495](https://github.com/netbox-community/netbox/issues/2495) - Enable deep-merging of config context data
* [#2597](https://github.com/netbox-community/netbox/issues/2597) - Add FibreChannel SFP28 (32GFC) interface form factor
## Bug Fixes
* [#2400](https://github.com/netbox-community/netbox/issues/2400) - Correct representation of nested object assignment in API docs
* [#2576](https://github.com/netbox-community/netbox/issues/2576) - Correct type for count_* fields in site API representation
* [#2606](https://github.com/netbox-community/netbox/issues/2606) - Fixed filtering for interfaces with a virtual form factor
* [#2611](https://github.com/netbox-community/netbox/issues/2611) - Fix error handling when assigning a clustered device to a different site
* [#2613](https://github.com/netbox-community/netbox/issues/2613) - Decrease live search minimum characters to three
* [#2615](https://github.com/netbox-community/netbox/issues/2615) - Tweak live search widget to use brief format for API requests
* [#2623](https://github.com/netbox-community/netbox/issues/2623) - Removed the need to pass the model class to the rqworker process for webhooks
* [#2634](https://github.com/netbox-community/netbox/issues/2634) - Enforce consistent representation of unnamed devices in rack view
---
# v2.4.8 (2018-11-20)
## Enhancements
* [#2490](https://github.com/netbox-community/netbox/issues/2490) - Added bulk editing for config contexts
* [#2557](https://github.com/netbox-community/netbox/issues/2557) - Added object view for tags
## Bug Fixes
* [#2473](https://github.com/netbox-community/netbox/issues/2473) - Fix encoding of long (>127 character) secrets
* [#2558](https://github.com/netbox-community/netbox/issues/2558) - Filter on all tags when multiple are passed
* [#2565](https://github.com/netbox-community/netbox/issues/2565) - Improved rendering of Markdown tables
* [#2575](https://github.com/netbox-community/netbox/issues/2575) - Correct model specified for rack roles table
* [#2588](https://github.com/netbox-community/netbox/issues/2588) - Catch all exceptions from failed NAPALM API Calls
* [#2589](https://github.com/netbox-community/netbox/issues/2589) - Virtual machine API serializer should require cluster assignment
---
# v2.4.7 (2018-11-06)
## Enhancements
* [#2388](https://github.com/netbox-community/netbox/issues/2388) - Enable filtering of devices/VMs by region
* [#2427](https://github.com/netbox-community/netbox/issues/2427) - Allow filtering of interfaces by assigned VLAN or VLAN ID
* [#2512](https://github.com/netbox-community/netbox/issues/2512) - Add device field to inventory item filter form
## Bug Fixes
* [#2502](https://github.com/netbox-community/netbox/issues/2502) - Allow duplicate VIPs inside a uniqueness-enforced VRF
* [#2514](https://github.com/netbox-community/netbox/issues/2514) - Prevent new connections to already connected interfaces
* [#2515](https://github.com/netbox-community/netbox/issues/2515) - Only use django-rq admin tmeplate if webhooks are enabled
* [#2528](https://github.com/netbox-community/netbox/issues/2528) - Enable creating circuit terminations with interface assignment via API
* [#2549](https://github.com/netbox-community/netbox/issues/2549) - Changed naming of `peer_device` and `peer_interface` on API /dcim/connected-device/ endpoint to use underscores
---
# v2.4.6 (2018-10-05)
## Enhancements
* [#2479](https://github.com/netbox-community/netbox/issues/2479) - Add user permissions for creating/modifying API tokens
* [#2487](https://github.com/netbox-community/netbox/issues/2487) - Return abbreviated API output when passed `?brief=1`
## Bug Fixes
* [#2393](https://github.com/netbox-community/netbox/issues/2393) - Fix Unicode support for CSV import under Python 2
* [#2483](https://github.com/netbox-community/netbox/issues/2483) - Set max item count of API-populated form fields to MAX_PAGE_SIZE
* [#2484](https://github.com/netbox-community/netbox/issues/2484) - Local config context not available on the Virtual Machine Edit Form
* [#2485](https://github.com/netbox-community/netbox/issues/2485) - Fix cancel button when assigning a service to a device/VM
* [#2491](https://github.com/netbox-community/netbox/issues/2491) - Fix exception when importing devices with invalid device type
* [#2492](https://github.com/netbox-community/netbox/issues/2492) - Sanitize hostname and port values returned through LLDP
---
# v2.4.5 (2018-10-02)
## Enhancements
* [#2392](https://github.com/netbox-community/netbox/issues/2392) - Implemented local context data for devices and virtual machines
* [#2402](https://github.com/netbox-community/netbox/issues/2402) - Order and format JSON data in form fields
* [#2432](https://github.com/netbox-community/netbox/issues/2432) - Link remote interface connections to the Interface view
* [#2438](https://github.com/netbox-community/netbox/issues/2438) - API optimizations for tagged objects
## Bug Fixes
* [#2406](https://github.com/netbox-community/netbox/issues/2406) - Remove hard-coded limit of 1000 objects from API-populated form fields
* [#2414](https://github.com/netbox-community/netbox/issues/2414) - Tags field missing from device/VM component creation forms
* [#2442](https://github.com/netbox-community/netbox/issues/2442) - Nullify "next" link in API when limit=0 is passed
* [#2443](https://github.com/netbox-community/netbox/issues/2443) - Enforce JSON object format when creating config contexts
* [#2444](https://github.com/netbox-community/netbox/issues/2444) - Improve validation of interface MAC addresses
* [#2455](https://github.com/netbox-community/netbox/issues/2455) - Ignore unique address enforcement for IPs with a shared/virtual role
* [#2470](https://github.com/netbox-community/netbox/issues/2470) - Log the creation of device/VM components as object changes
---
# v2.4.4 (2018-08-22)
## Enhancements
* [#2168](https://github.com/netbox-community/netbox/issues/2168) - Added Extreme SummitStack interface form factors
* [#2356](https://github.com/netbox-community/netbox/issues/2356) - Include cluster site as read-only field in VirtualMachine serializer
* [#2362](https://github.com/netbox-community/netbox/issues/2362) - Implemented custom admin site to properly handle BASE_PATH
* [#2254](https://github.com/netbox-community/netbox/issues/2254) - Implemented searchability for Rack Groups
## Bug Fixes
* [#2353](https://github.com/netbox-community/netbox/issues/2353) - Handle `DoesNotExist` exception when deleting a device with connected interfaces
* [#2354](https://github.com/netbox-community/netbox/issues/2354) - Increased maximum MTU for interfaces to 65536 bytes
* [#2355](https://github.com/netbox-community/netbox/issues/2355) - Added item count to inventory tab on device view
* [#2368](https://github.com/netbox-community/netbox/issues/2368) - Record change in device changelog when altering cluster assignment
* [#2369](https://github.com/netbox-community/netbox/issues/2369) - Corrected time zone validation on site API serializer
* [#2370](https://github.com/netbox-community/netbox/issues/2370) - Redirect to parent device after deleting device bays
* [#2374](https://github.com/netbox-community/netbox/issues/2374) - Fix toggling display of IP addresses in virtual machine interfaces list
* [#2378](https://github.com/netbox-community/netbox/issues/2378) - Corrected "edit" link for virtual machine interfaces
---
# v2.4.3 (2018-08-09)
## Enhancements
* [#2333](https://github.com/netbox-community/netbox/issues/2333) - Added search filters for ConfigContexts
## Bug Fixes
* [#2334](https://github.com/netbox-community/netbox/issues/2334) - TypeError raised when WritableNestedSerializer receives a non-integer value
* [#2335](https://github.com/netbox-community/netbox/issues/2335) - API requires group field when creating/updating a rack
* [#2336](https://github.com/netbox-community/netbox/issues/2336) - Bulk deleting power outlets and console server ports from a device redirects to home page
* [#2337](https://github.com/netbox-community/netbox/issues/2337) - Attempting to create the next available prefix within a parent assigned to a VRF raises an AssertionError
* [#2340](https://github.com/netbox-community/netbox/issues/2340) - API requires manufacturer field when creating/updating an inventory item
* [#2342](https://github.com/netbox-community/netbox/issues/2342) - IntegrityError raised when attempting to assign an invalid IP address as the primary for a VM
* [#2344](https://github.com/netbox-community/netbox/issues/2344) - AttributeError when assigning VLANs to an interface on a device/VM not assigned to a site
---
# v2.4.2 (2018-08-08)
## Bug Fixes
* [#2318](https://github.com/netbox-community/netbox/issues/2318) - ImportError when viewing a report
* [#2319](https://github.com/netbox-community/netbox/issues/2319) - Extend ChoiceField to properly handle true/false choice keys
* [#2320](https://github.com/netbox-community/netbox/issues/2320) - TypeError when dispatching a webhook with a secret key configured
* [#2321](https://github.com/netbox-community/netbox/issues/2321) - Allow explicitly setting a null value on nullable ChoiceFields
* [#2322](https://github.com/netbox-community/netbox/issues/2322) - Webhooks firing on non-enabled event types
* [#2323](https://github.com/netbox-community/netbox/issues/2323) - DoesNotExist raised when deleting devices or virtual machines
* [#2330](https://github.com/netbox-community/netbox/issues/2330) - Incorrect tab link in VRF changelog view
---
# v2.4.1 (2018-08-07)
## Bug Fixes
* [#2303](https://github.com/netbox-community/netbox/issues/2303) - Always redirect to parent object when bulk editing/deleting components
* [#2308](https://github.com/netbox-community/netbox/issues/2308) - Custom fields panel absent from object view in UI
* [#2310](https://github.com/netbox-community/netbox/issues/2310) - False validation error on certain nested serializers
* [#2311](https://github.com/netbox-community/netbox/issues/2311) - Redirect to parent after editing interface from device/VM view
* [#2312](https://github.com/netbox-community/netbox/issues/2312) - Running a report yields a ValueError exception
* [#2314](https://github.com/netbox-community/netbox/issues/2314) - Serialized representation of object in change log does not include assigned tags
---
# v2.4.0 (2018-08-06)
## New Features
### Webhooks ([#81](https://github.com/netbox-community/netbox/issues/81))
Webhooks enable NetBox to send a representation of an object every time one is created, updated, or deleted. Webhooks are sent from NetBox to external services via HTTP, and can be limited by object type. Services which receive a webhook can act on the data provided by NetBox to automate other tasks.
Special thanks to [John Anderson](https://github.com/lampwins) for doing the heavy lifting for this feature!
### Tagging ([#132](https://github.com/netbox-community/netbox/issues/132))
Tags are free-form labels which can be assigned to a variety of objects in NetBox. Tags can be used to categorize and filter objects in addition to built-in and custom fields. Objects to which tags apply now include a `tags` field in the API.
### Contextual Configuration Data ([#1349](https://github.com/netbox-community/netbox/issues/1349))
Sometimes it is desirable to associate arbitrary data with a group of devices to aid in their configuration. (For example, you might want to associate a set of syslog servers for all devices at a particular site.) Context data enables the association of arbitrary data (expressed in JSON format) to devices and virtual machines grouped by region, site, role, platform, and/or tenancy. Context data is arranged hierarchically, so that data with a higher weight can be entered to override more general lower-weight data. Multiple instances of data are automatically merged by NetBox to present a single dictionary for each object.
### Change Logging ([#1898](https://github.com/netbox-community/netbox/issues/1898))
When an object is created, updated, or deleted, NetBox now automatically records a serialized representation of that object (similar to how it appears in the REST API) as well the event time and user account associated with the change.
## Enhancements
* [#238](https://github.com/netbox-community/netbox/issues/238) - Allow racks with the same name within a site (but in different groups)
* [#971](https://github.com/netbox-community/netbox/issues/971) - Add a view to show all VLAN IDs available within a group
* [#1673](https://github.com/netbox-community/netbox/issues/1673) - Added object/list views for services
* [#1687](https://github.com/netbox-community/netbox/issues/1687) - Enabled custom fields for services
* [#1739](https://github.com/netbox-community/netbox/issues/1739) - Enabled custom fields for secrets
* [#1794](https://github.com/netbox-community/netbox/issues/1794) - Improved POST/PATCH representation of nested objects
* [#2029](https://github.com/netbox-community/netbox/issues/2029) - Added optional NAPALM arguments to Platform model
* [#2034](https://github.com/netbox-community/netbox/issues/2034) - Include the ID when showing nested interface connections (API change)
* [#2118](https://github.com/netbox-community/netbox/issues/2118) - Added `latitude` and `longitude` fields to Site for GPS coordinates
* [#2131](https://github.com/netbox-community/netbox/issues/2131) - Added `created` and `last_updated` fields to DeviceType
* [#2157](https://github.com/netbox-community/netbox/issues/2157) - Fixed natural ordering of objects when sorted by name
* [#2225](https://github.com/netbox-community/netbox/issues/2225) - Add "view elevations" button for site rack groups
## Bug Fixes
* [#2272](https://github.com/netbox-community/netbox/issues/2272) - Allow subdevice_role to be null on DeviceTypeSerializer"
* [#2286](https://github.com/netbox-community/netbox/issues/2286) - Fixed "mark connected" button for PDU outlet connections
## API Changes
* Introduced the `/extras/config-contexts/`, `/extras/object-changes/`, and `/extras/tags/` API endpoints
* API writes now return a nested representation of related objects (rather than only a numeric ID)
* The dcim.DeviceType serializer now includes `created` and `last_updated` fields
* The dcim.Site serializer now includes `latitude` and `longitude` fields
* The ipam.Service and secrets.Secret serializers now include custom fields
* The dcim.Platform serializer now includes a free-form (JSON) `napalm_args` field
## Changes Since v2.4-beta1
### Enhancements
* [#2229](https://github.com/netbox-community/netbox/issues/2229) - Allow mapping of ConfigContexts to tenant groups
* [#2259](https://github.com/netbox-community/netbox/issues/2259) - Add changelog tab to interface view
* [#2264](https://github.com/netbox-community/netbox/issues/2264) - Added "map it" link for site GPS coordinates
### Bug Fixes
* [#2137](https://github.com/netbox-community/netbox/issues/2137) - Fixed JSON serialization of dates
* [#2258](https://github.com/netbox-community/netbox/issues/2258) - Include changed object type on home page changelog
* [#2265](https://github.com/netbox-community/netbox/issues/2265) - Include parent regions when filtering applicable ConfigContexts
* [#2288](https://github.com/netbox-community/netbox/issues/2288) - Fix exception when assigning objects to a ConfigContext via the API
* [#2296](https://github.com/netbox-community/netbox/issues/2296) - Fix AttributeError when creating a new object with tags assigned
* [#2300](https://github.com/netbox-community/netbox/issues/2300) - Fix assignment of an interface to an IP address via API PATCH
* [#2301](https://github.com/netbox-community/netbox/issues/2301) - Fix model validation on assignment of ManyToMany fields via API PATCH
* [#2305](https://github.com/netbox-community/netbox/issues/2305) - Make VLAN fields optional when creating a VM interface via the API

View File

@@ -0,0 +1,365 @@
# v2.5.13 (2019-05-31)
## Enhancements
* [#2813](https://github.com/netbox-community/netbox/issues/2813) - Add tenant group filters
* [#3085](https://github.com/netbox-community/netbox/issues/3085) - Catch all exceptions during export template rendering
* [#3138](https://github.com/netbox-community/netbox/issues/3138) - Add 2.5GE and 5GE interface form factors
* [#3151](https://github.com/netbox-community/netbox/issues/3151) - Add inventory item count to manufacturers list
* [#3156](https://github.com/netbox-community/netbox/issues/3156) - Add site link to rack reservations overview
* [#3183](https://github.com/netbox-community/netbox/issues/3183) - Enable bulk deletion of sites
* [#3185](https://github.com/netbox-community/netbox/issues/3185) - Improve performance for custom field access within templates
* [#3186](https://github.com/netbox-community/netbox/issues/3186) - Add interface name filter for IP addresses
## Bug Fixes
* [#3031](https://github.com/netbox-community/netbox/issues/3031) - Fixed form field population of tags with spaces
* [#3132](https://github.com/netbox-community/netbox/issues/3132) - Circuit termination missing from available cable termination types
* [#3150](https://github.com/netbox-community/netbox/issues/3150) - Fix formatting of cable length during cable trace
* [#3184](https://github.com/netbox-community/netbox/issues/3184) - Correctly display color block for white cables
* [#3190](https://github.com/netbox-community/netbox/issues/3190) - Fix custom field rendering for Jinja2 export templates
* [#3211](https://github.com/netbox-community/netbox/issues/3211) - Fix error handling when attempting to delete a protected object via API
* [#3223](https://github.com/netbox-community/netbox/issues/3223) - Fix filtering devices by "has power outlets"
* [#3227](https://github.com/netbox-community/netbox/issues/3227) - Fix exception when deleting a circuit with a termination(s)
* [#3228](https://github.com/netbox-community/netbox/issues/3228) - Fixed login link retaining query parameters
---
# v2.5.12 (2019-05-01)
## Bug Fixes
* [#3127](https://github.com/netbox-community/netbox/issues/3127) - Fix natural ordering of device components
---
2.5.11 (2019-04-29)
## Notes
This release upgrades the Django framework to version 2.2.
## Enhancements
* [#2986](https://github.com/netbox-community/netbox/issues/2986) - Improve natural ordering of device components
* [#3023](https://github.com/netbox-community/netbox/issues/3023) - Add support for filtering cables by connected device
* [#3070](https://github.com/netbox-community/netbox/issues/3070) - Add decommissioning status for devices
## Bug Fixes
* [#2621](https://github.com/netbox-community/netbox/issues/2621) - Upgrade Django requirement to 2.2 to fix object deletion issue in the changelog middleware
* [#3072](https://github.com/netbox-community/netbox/issues/3072) - Preserve multiselect filter values when updating per-page count for list views
* [#3112](https://github.com/netbox-community/netbox/issues/3112) - Fix ordering of interface connections list by termination B name/device
* [#3116](https://github.com/netbox-community/netbox/issues/3116) - Fix `tagged_items` count in tags API endpoint
* [#3118](https://github.com/netbox-community/netbox/issues/3118) - Disable `last_login` update on login when maintenance mode is enabled
---
# v2.5.10 (2019-04-08)
## Enhancements
* [#3052](https://github.com/netbox-community/netbox/issues/3052) - Add Jinja2 support for export templates
## Bug Fixes
* [#2937](https://github.com/netbox-community/netbox/issues/2937) - Redirect to list view after editing an object from list view
* [#3036](https://github.com/netbox-community/netbox/issues/3036) - DCIM interfaces API endpoint should not include VM interfaces
* [#3039](https://github.com/netbox-community/netbox/issues/3039) - Fix exception when retrieving change object for a component template via API
* [#3041](https://github.com/netbox-community/netbox/issues/3041) - Fix form widget for bulk cable label update
* [#3044](https://github.com/netbox-community/netbox/issues/3044) - Ignore site/rack fields when connecting a new cable via device search
* [#3046](https://github.com/netbox-community/netbox/issues/3046) - Fix exception at reports API endpoint
* [#3047](https://github.com/netbox-community/netbox/issues/3047) - Fix exception when writing mac address for an interface via API
---
# v2.5.9 (2019-04-01)
## Enhancements
* [#2933](https://github.com/netbox-community/netbox/issues/2933) - Add username to outbound webhook requests
* [#3011](https://github.com/netbox-community/netbox/issues/3011) - Add SSL support for django-rq (requires django-rq v1.3.1+)
* [#3025](https://github.com/netbox-community/netbox/issues/3025) - Add request ID to outbound webhook requests (for correlating all changes part of a single request)
## Bug Fixes
* [#2207](https://github.com/netbox-community/netbox/issues/2207) - Fixes deterministic ordering of interfaces
* [#2577](https://github.com/netbox-community/netbox/issues/2577) - Clarification of wording in API regarding filtering
* [#2924](https://github.com/netbox-community/netbox/issues/2924) - Add interface type for QSFP28 50GE
* [#2936](https://github.com/netbox-community/netbox/issues/2936) - Fix device role selection showing duplicate first entry
* [#2998](https://github.com/netbox-community/netbox/issues/2998) - Limit device query to non-racked devices if no rack selected when creating a cable
* [#3001](https://github.com/netbox-community/netbox/issues/3001) - Fix API representation of ObjectChange `action` and add `changed_object_type`
* [#3014](https://github.com/netbox-community/netbox/issues/3014) - Fixes VM Role filtering
* [#3019](https://github.com/netbox-community/netbox/issues/3019) - Fix tag population when running NetBox within a path
* [#3022](https://github.com/netbox-community/netbox/issues/3022) - Add missing cable termination types to DCIM `_choices` endpoint
* [#3026](https://github.com/netbox-community/netbox/issues/3026) - Tweak prefix/IP filter forms to filter using VRF ID rather than route distinguisher
* [#3027](https://github.com/netbox-community/netbox/issues/3027) - Ignore empty local context data when rendering config contexts
* [#3032](https://github.com/netbox-community/netbox/issues/3032) - Save assigned tags when creating a new secret
---
# v2.5.8 (2019-03-11)
## Enhancements
* [#2435](https://github.com/netbox-community/netbox/issues/2435) - Printer friendly CSS
## Bug Fixes
* [#2065](https://github.com/netbox-community/netbox/issues/2065) - Correct documentation for VM interface serializer
* [#2705](https://github.com/netbox-community/netbox/issues/2705) - Fix endpoint grouping in API docs
* [#2781](https://github.com/netbox-community/netbox/issues/2781) - Fix filtering of sites/devices/VMs by multiple regions
* [#2923](https://github.com/netbox-community/netbox/issues/2923) - Provider filter form's site field should be blank by default
* [#2938](https://github.com/netbox-community/netbox/issues/2938) - Enforce deterministic ordering of device components returned by API
* [#2939](https://github.com/netbox-community/netbox/issues/2939) - Exclude circuit terminations from API interface connections endpoint
* [#2940](https://github.com/netbox-community/netbox/issues/2940) - Allow CSV import of prefixes/IPs to VRF without an RD assigned
* [#2944](https://github.com/netbox-community/netbox/issues/2944) - Record the deletion of an IP address in the changelog of its parent interface (if any)
* [#2952](https://github.com/netbox-community/netbox/issues/2952) - Added the `slug` field to the Tenant filter for use in the API and search function
* [#2954](https://github.com/netbox-community/netbox/issues/2954) - Remove trailing slashes to fix root/template paths on Windows
* [#2961](https://github.com/netbox-community/netbox/issues/2961) - Prevent exception when exporting inventory items belonging to unnamed devices
* [#2962](https://github.com/netbox-community/netbox/issues/2962) - Increase ExportTemplate `mime_type` field length
* [#2966](https://github.com/netbox-community/netbox/issues/2966) - Accept `null` cable length_unit via API
* [#2972](https://github.com/netbox-community/netbox/issues/2972) - Improve ContentTypeField serializer to elegantly handle invalid data
* [#2976](https://github.com/netbox-community/netbox/issues/2976) - Add delete button to tag view
* [#2980](https://github.com/netbox-community/netbox/issues/2980) - Improve rendering time for API docs
* [#2982](https://github.com/netbox-community/netbox/issues/2982) - Correct CSS class assignment on color picker
* [#2984](https://github.com/netbox-community/netbox/issues/2984) - Fix logging of unlabeled cable ID on cable deletion
* [#2985](https://github.com/netbox-community/netbox/issues/2985) - Fix pagination page length for rack elevations
---
# v2.5.7 (2019-02-21)
## Enhancements
* [#2357](https://github.com/netbox-community/netbox/issues/2357) - Enable filtering of devices by rack face
* [#2638](https://github.com/netbox-community/netbox/issues/2638) - Add button to copy unlocked secret to clipboard
* [#2870](https://github.com/netbox-community/netbox/issues/2870) - Add Markdown rendering for provider NOC/admin contact fields
* [#2878](https://github.com/netbox-community/netbox/issues/2878) - Add cable types for OS1/OS2 singlemode fiber
* [#2890](https://github.com/netbox-community/netbox/issues/2890) - Add port types for APC fiber
* [#2898](https://github.com/netbox-community/netbox/issues/2898) - Enable filtering cables list by connection status
* [#2903](https://github.com/netbox-community/netbox/issues/2903) - Clarify purpose of tags field on interface edit form
## Bug Fixes
* [#2852](https://github.com/netbox-community/netbox/issues/2852) - Allow filtering devices by null rack position
* [#2884](https://github.com/netbox-community/netbox/issues/2884) - Don't display connect button for wireless interfaces
* [#2888](https://github.com/netbox-community/netbox/issues/2888) - Correct foreground color of device roles in rack elevations
* [#2893](https://github.com/netbox-community/netbox/issues/2893) - Remove duplicate display of VRF RD on IP address view
* [#2895](https://github.com/netbox-community/netbox/issues/2895) - Fix filtering of nullable character fields
* [#2901](https://github.com/netbox-community/netbox/issues/2901) - Fix ordering regions by site count
* [#2910](https://github.com/netbox-community/netbox/issues/2910) - Fix config context list and edit forms to use Select2 elements
* [#2912](https://github.com/netbox-community/netbox/issues/2912) - Cable type in filter form should be blank by default
* [#2913](https://github.com/netbox-community/netbox/issues/2913) - Fix assigned prefixes link on VRF view
* [#2914](https://github.com/netbox-community/netbox/issues/2914) - Fix empty connected circuit link on device interfaces list
* [#2915](https://github.com/netbox-community/netbox/issues/2915) - Fix bulk editing of pass-through ports
---
# v2.5.6 (2019-02-13)
## Enhancements
* [#2758](https://github.com/netbox-community/netbox/issues/2758) - Add cable trace button to pass-through ports
* [#2839](https://github.com/netbox-community/netbox/issues/2839) - Add "110 punch" type for pass-through ports
* [#2854](https://github.com/netbox-community/netbox/issues/2854) - Enable bulk editing of pass-through ports
* [#2866](https://github.com/netbox-community/netbox/issues/2866) - Add cellular interface types (GSM/CDMA/LTE)
## Bug Fixes
* [#2841](https://github.com/netbox-community/netbox/issues/2841) - Fix filtering by VRF for prefix and IP address lists
* [#2844](https://github.com/netbox-community/netbox/issues/2844) - Correct display of far cable end for pass-through ports
* [#2845](https://github.com/netbox-community/netbox/issues/2845) - Enable filtering of rack unit list by unit ID
* [#2856](https://github.com/netbox-community/netbox/issues/2856) - Fix navigation links between LAG interfaces and their members on device view
* [#2857](https://github.com/netbox-community/netbox/issues/2857) - Add `display_name` to DeviceType API serializer; fix DeviceType list for bulk device edit
* [#2862](https://github.com/netbox-community/netbox/issues/2862) - Follow return URL when connecting a cable
* [#2864](https://github.com/netbox-community/netbox/issues/2864) - Correct display of VRF name when no RD is assigned
* [#2877](https://github.com/netbox-community/netbox/issues/2877) - Fixed device role label display on light background color
* [#2880](https://github.com/netbox-community/netbox/issues/2880) - Sanitize user password if an exception is raised during login
---
# v2.5.5 (2019-01-31)
## Enhancements
* [#2805](https://github.com/netbox-community/netbox/issues/2805) - Allow null route distinguisher for VRFs
* [#2809](https://github.com/netbox-community/netbox/issues/2809) - Remove VRF child prefixes table; link to main prefixes view
* [#2825](https://github.com/netbox-community/netbox/issues/2825) - Include directly connected device for front/rear ports
## Bug Fixes
* [#2824](https://github.com/netbox-community/netbox/issues/2824) - Fix template exception when viewing rack elevations list
* [#2833](https://github.com/netbox-community/netbox/issues/2833) - Fix form widget for front port template creation
* [#2835](https://github.com/netbox-community/netbox/issues/2835) - Fix certain model filters did not support the `q` query param
* [#2837](https://github.com/netbox-community/netbox/issues/2837) - Fix select2 nullable filter fields add multiple null_option elements when paging
---
# v2.5.4 (2019-01-29)
## Enhancements
* [#2516](https://github.com/netbox-community/netbox/issues/2516) - Implemented Select2 for all Model backed selection fields
* [#2590](https://github.com/netbox-community/netbox/issues/2590) - Implemented the color picker with Select2 to show colors in the background
* [#2733](https://github.com/netbox-community/netbox/issues/2733) - Enable bulk assignment of MAC addresses to interfaces
* [#2735](https://github.com/netbox-community/netbox/issues/2735) - Implemented Select2 for all list filter form select elements
* [#2753](https://github.com/netbox-community/netbox/issues/2753) - Implemented Select2 to replace most all instances of select fields in forms
* [#2766](https://github.com/netbox-community/netbox/issues/2766) - Extend users admin table to include superuser and active fields
* [#2782](https://github.com/netbox-community/netbox/issues/2782) - Add `is_pool` field for prefix filtering
* [#2807](https://github.com/netbox-community/netbox/issues/2807) - Include device site/rack assignment in cable trace view
* [#2808](https://github.com/netbox-community/netbox/issues/2808) - Loosen version pinning for Django to allow patch releases
* [#2810](https://github.com/netbox-community/netbox/issues/2810) - Include description fields in interface connections export
## Bug Fixes
* [#2779](https://github.com/netbox-community/netbox/issues/2779) - Include "none" option when filter IP addresses by role
* [#2783](https://github.com/netbox-community/netbox/issues/2783) - Fix AttributeError exception when attempting to delete region(s)
* [#2795](https://github.com/netbox-community/netbox/issues/2795) - Fix duplicate display of pagination controls on child prefix/IP tables
* [#2798](https://github.com/netbox-community/netbox/issues/2798) - Properly URL-encode "map it" link on site view
* [#2802](https://github.com/netbox-community/netbox/issues/2802) - Better error handling for unsupported NAPALM methods
* [#2816](https://github.com/netbox-community/netbox/issues/2816) - Handle exception when deleting a device with connected components
---
# v2.5.3 (2019-01-11)
## Enhancements
* [#1630](https://github.com/netbox-community/netbox/issues/1630) - Enable bulk editing of prefix/IP mask length
* [#1870](https://github.com/netbox-community/netbox/issues/1870) - Add per-page toggle to object lists
* [#1871](https://github.com/netbox-community/netbox/issues/1871) - Enable filtering sites by parent region
* [#1983](https://github.com/netbox-community/netbox/issues/1983) - Enable regular expressions when bulk renaming device components
* [#2682](https://github.com/netbox-community/netbox/issues/2682) - Add DAC and AOC cable types
* [#2693](https://github.com/netbox-community/netbox/issues/2693) - Additional cable colors
* [#2726](https://github.com/netbox-community/netbox/issues/2726) - Include cables in global search
## Bug Fixes
* [#2742](https://github.com/netbox-community/netbox/issues/2742) - Preserve cluster assignment when editing a device
* [#2757](https://github.com/netbox-community/netbox/issues/2757) - Always treat first/last IPs within a /31 or /127 as usable
* [#2762](https://github.com/netbox-community/netbox/issues/2762) - Add missing DCIM field values to API `_choices` endpoint
* [#2777](https://github.com/netbox-community/netbox/issues/2777) - Fix cable validation to handle duplicate connections on import
---
# v2.5.2 (2018-12-21)
## Enhancements
* [#2561](https://github.com/netbox-community/netbox/issues/2561) - Add 200G and 400G interface types
* [#2701](https://github.com/netbox-community/netbox/issues/2701) - Enable filtering of prefixes by exact prefix value
## Bug Fixes
* [#2673](https://github.com/netbox-community/netbox/issues/2673) - Fix exception on LLDP neighbors view for device with a circuit connected
* [#2691](https://github.com/netbox-community/netbox/issues/2691) - Cable trace should follow circuits
* [#2698](https://github.com/netbox-community/netbox/issues/2698) - Remove pagination restriction on bulk component creation for devices/VMs
* [#2704](https://github.com/netbox-community/netbox/issues/2704) - Fix form select widget population on parent with null value
* [#2707](https://github.com/netbox-community/netbox/issues/2707) - Correct permission evaluation for circuit termination cabling
* [#2712](https://github.com/netbox-community/netbox/issues/2712) - Preserve list filtering after editing objects in bulk
* [#2717](https://github.com/netbox-community/netbox/issues/2717) - Fix bulk deletion of tags
* [#2721](https://github.com/netbox-community/netbox/issues/2721) - Detect loops when tracing front/rear ports
* [#2723](https://github.com/netbox-community/netbox/issues/2723) - Correct permission evaluation when bulk deleting tags
* [#2724](https://github.com/netbox-community/netbox/issues/2724) - Limit rear port choices to current device when editing a front port
---
# v2.5.1 (2018-12-13)
## Enhancements
* [#2655](https://github.com/netbox-community/netbox/issues/2655) - Add 128GFC Fibrechannel interface type
* [#2674](https://github.com/netbox-community/netbox/issues/2674) - Enable filtering changelog by object type under web UI
## Bug Fixes
* [#2662](https://github.com/netbox-community/netbox/issues/2662) - Fix ImproperlyConfigured exception when rendering API docs
* [#2663](https://github.com/netbox-community/netbox/issues/2663) - Prevent duplicate interfaces from appearing under VLAN members view
* [#2666](https://github.com/netbox-community/netbox/issues/2666) - Correct display of length unit in cables list
* [#2676](https://github.com/netbox-community/netbox/issues/2676) - Fix exception when passing dictionary value to a ChoiceField
* [#2678](https://github.com/netbox-community/netbox/issues/2678) - Fix error when viewing webhook in admin UI without write permission
* [#2680](https://github.com/netbox-community/netbox/issues/2680) - Disallow POST requests to `/dcim/interface-connections/` API endpoint
* [#2683](https://github.com/netbox-community/netbox/issues/2683) - Fix exception when connecting a cable to a RearPort with no corresponding FrontPort
* [#2684](https://github.com/netbox-community/netbox/issues/2684) - Fix custom field filtering
* [#2687](https://github.com/netbox-community/netbox/issues/2687) - Correct naming of before/after filters for changelog entries
---
# v2.5.0 (2018-12-10)
## Notes
### Python 3 Required
As promised, Python 2 support has been completed removed. Python 3.5 or higher is now required to run NetBox. Please see [our Python 3 migration guide](https://netbox.readthedocs.io/en/stable/installation/migrating-to-python3/) for assistance with upgrading.
### Removed Deprecated User Activity Log
The UserAction model, which was deprecated by the new change logging feature in NetBox v2.4, has been removed. If you need to archive legacy user activity, do so prior to upgrading to NetBox v2.5, as the database migration will remove all data associated with this model.
### View Permissions in Django 2.1
Django 2.1 introduces view permissions for object types (not to be confused with object-level permissions). Implementation of [#323](https://github.com/netbox-community/netbox/issues/323) is planned for NetBox v2.6. Users are encourage to begin assigning view permissions as desired in preparation for their eventual enforcement.
### upgrade.sh No Longer Invokes sudo
The `upgrade.sh` script has been tweaked so that it no longer invokes `sudo` internally. This was done to ensure compatibility when running NetBox inside a Python virtual environment. If you need elevated permissions when upgrading NetBox, call the upgrade script with `sudo upgrade.sh`.
## New Features
### Patch Panels and Cables ([#20](https://github.com/netbox-community/netbox/issues/20))
NetBox now supports modeling physical cables for console, power, and interface connections. The new pass-through port component type has also been introduced to model patch panels and similar devices.
## Enhancements
* [#450](https://github.com/netbox-community/netbox/issues/450) - Added `outer_width` and `outer_depth` fields to rack model
* [#867](https://github.com/netbox-community/netbox/issues/867) - Added `description` field to circuit terminations
* [#1444](https://github.com/netbox-community/netbox/issues/1444) - Added an `asset_tag` field for racks
* [#1931](https://github.com/netbox-community/netbox/issues/1931) - Added a count of assigned IP addresses to the interface API serializer
* [#2000](https://github.com/netbox-community/netbox/issues/2000) - Dropped support for Python 2
* [#2053](https://github.com/netbox-community/netbox/issues/2053) - Introduced the `LOGIN_TIMEOUT` configuration setting
* [#2057](https://github.com/netbox-community/netbox/issues/2057) - Added description columns to interface connections list
* [#2104](https://github.com/netbox-community/netbox/issues/2104) - Added a `status` field for racks
* [#2165](https://github.com/netbox-community/netbox/issues/2165) - Improved natural ordering of Interfaces
* [#2292](https://github.com/netbox-community/netbox/issues/2292) - Removed the deprecated UserAction model
* [#2367](https://github.com/netbox-community/netbox/issues/2367) - Removed deprecated RPCClient functionality
* [#2426](https://github.com/netbox-community/netbox/issues/2426) - Introduced `SESSION_FILE_PATH` configuration setting for authentication without write access to database
* [#2594](https://github.com/netbox-community/netbox/issues/2594) - `upgrade.sh` no longer invokes sudo
## Changes From v2.5-beta2
* [#2474](https://github.com/netbox-community/netbox/issues/2474) - Add `cabled` and `connection_status` filters for device components
* [#2616](https://github.com/netbox-community/netbox/issues/2616) - Convert Rack `outer_unit` and Cable `length_unit` to integer-based choice fields
* [#2622](https://github.com/netbox-community/netbox/issues/2622) - Enable filtering cables by multiple types/colors
* [#2624](https://github.com/netbox-community/netbox/issues/2624) - Delete associated content type and permissions when removing InterfaceConnection model
* [#2626](https://github.com/netbox-community/netbox/issues/2626) - Remove extraneous permissions generated from proxy models
* [#2632](https://github.com/netbox-community/netbox/issues/2632) - Change representation of null values from `0` to `null`
* [#2639](https://github.com/netbox-community/netbox/issues/2639) - Fix preservation of length/dimensions unit for racks and cables
* [#2648](https://github.com/netbox-community/netbox/issues/2648) - Include the `connection_status` field in nested represenations of connectable device components
* [#2649](https://github.com/netbox-community/netbox/issues/2649) - Add `connected_endpoint_type` to connectable device component API representations
## API Changes
* The `/extras/recent-activity/` endpoint (replaced by change logging in v2.4) has been removed
* The `rpc_client` field has been removed from dcim.Platform (see #2367)
* Introduced a new API endpoint for cables at `/dcim/cables/`
* New endpoints for front and rear pass-through ports (and their templates) in parallel with existing device components
* The fields `interface_connection` on Interface and `interface` on CircuitTermination have been replaced with `connected_endpoint` and `connection_status`
* A new `cable` field has been added to console, power, and interface components and to circuit terminations
* New fields for dcim.Rack: `status`, `asset_tag`, `outer_width`, `outer_depth`, `outer_unit`
* The following boolean filters on dcim.Device and dcim.DeviceType have been renamed:
* `is_console_server`: `console_server_ports`
* `is_pdu`: `power_outlets`
* `is_network_device`: `interfaces`
* The following new boolean filters have been introduced for dcim.Device and dcim.DeviceType:
* `console_ports`
* `power_ports`
* `pass_through_ports`
* The field `interface_ordering` has been removed from the DeviceType serializer
* Added a `description` field to the CircuitTermination serializer
* Added `ipaddress_count` to InterfaceSerializer to show the count of assigned IP addresses for each interface
* The `available-prefixes` and `available-ips` IPAM endpoints now return an HTTP 204 response instead of HTTP 400 when no new objects can be created
* Filtering on null values now uses the string `null` instead of zero

View File

@@ -0,0 +1,392 @@
# v2.6.7 (2019-11-01)
## Enhancements
* [#3445](https://github.com/netbox-community/netbox/issues/3445) - Add support for additional user defined headers to be added to webhook requests
* [#3499](https://github.com/netbox-community/netbox/issues/3499) - Add `ca_file_path` to Webhook model to support user supplied CA certificate verification of webhook requests
* [#3594](https://github.com/netbox-community/netbox/issues/3594) - Add ChoiceVar for custom scripts
* [#3619](https://github.com/netbox-community/netbox/issues/3619) - Add 400GE OSFP interface type
* [#3659](https://github.com/netbox-community/netbox/issues/3659) - Add filtering for objects in admin UI
## Bug Fixes
* [#3309](https://github.com/netbox-community/netbox/issues/3309) - Rewrite change logging middleware to resolve sporadic testing failures
* [#3340](https://github.com/netbox-community/netbox/issues/3340) - Add missing options to connect front ports to console ports
* [#3357](https://github.com/netbox-community/netbox/issues/3357) - Enable filter sites/devices/VMs by null region
* [#3460](https://github.com/netbox-community/netbox/issues/3460) - Extend upgrade script to validate Python dependencies
* [#3596](https://github.com/netbox-community/netbox/issues/3596) - Prevent server error when reassigning a device to a new device bay
* [#3629](https://github.com/netbox-community/netbox/issues/3629) - Use `get_lldp_neighors_detail` to validation LLDP neighbors
* [#3635](https://github.com/netbox-community/netbox/issues/3635) - Add missing cache support for the circuits app
* [#3636](https://github.com/netbox-community/netbox/issues/3636) - Add missing `rack_group` field to PowerFeed CSV export
* [#3652](https://github.com/netbox-community/netbox/issues/3652) - Limit next/previous rack by assigned rack group
---
# v2.6.6 (2019-10-10)
## Notes
* This release includes a migration which automatically updates all existing cables to enable filtering by site/rack (see [#3259](https://github.com/netbox-community/netbox/issues/3259)). This migration may take several minutes to complete on installations with tens of thousands of cables defined.
## Enhancements
* [#1941](https://github.com/netbox-community/netbox/issues/1941) - Add InfiniBand interface types
* [#3259](https://github.com/netbox-community/netbox/issues/3259) - Add `rack` and `site` filters for cables
* [#3471](https://github.com/netbox-community/netbox/issues/3471) - Disallow raw HTML in Markdown-rendered fields
* [#3545](https://github.com/netbox-community/netbox/issues/3545) - Add `MultiObjectVar` for custom scripts
* [#3563](https://github.com/netbox-community/netbox/issues/3563) - Enable editing of individual DeviceType components
* [#3580](https://github.com/netbox-community/netbox/issues/3580) - Render text and URL fields as textareas in the custom link form
* [#3581](https://github.com/netbox-community/netbox/issues/3581) - Introduce `commit_default` custom script attribute to not commit changes by default
## Bug Fixes
* [#3458](https://github.com/netbox-community/netbox/issues/3458) - Prevent primary IP address for a device/VM from being reassigned
* [#3463](https://github.com/netbox-community/netbox/issues/3463) - Correct CSV headers for exported power feeds
* [#3474](https://github.com/netbox-community/netbox/issues/3474) - Fix device status page loading when NAPALM call fails
* [#3571](https://github.com/netbox-community/netbox/issues/3571) - Prevent erroneous redirects when editing tags
* [#3573](https://github.com/netbox-community/netbox/issues/3573) - Ensure consistent display of changelog retention period
* [#3574](https://github.com/netbox-community/netbox/issues/3574) - Change `device` to `parent` in interface editing VLAN filtering logic
* [#3575](https://github.com/netbox-community/netbox/issues/3575) - Restore label for comments field when bulk editing circuits
* [#3582](https://github.com/netbox-community/netbox/issues/3582) - Enforce view permissions on global search results
* [#3588](https://github.com/netbox-community/netbox/issues/3588) - Enforce object-form JSON for local context data on devices and VMs
---
# v2.6.5 (2019-09-25)
## Enhancements
* [#3297](https://github.com/netbox-community/netbox/issues/3297) - Include reserved units when calculating rack utilization
* [#3347](https://github.com/netbox-community/netbox/issues/3347) - Extend upgrade script to automatically remove stale content types
* [#3352](https://github.com/netbox-community/netbox/issues/3352) - Enable filtering changelog API by `changed_object_id`
* [#3515](https://github.com/netbox-community/netbox/issues/3515) - Enable export templates for inventory items
* [#3524](https://github.com/netbox-community/netbox/issues/3524) - Enable bulk editing of power outlet/power port associations
* [#3529](https://github.com/netbox-community/netbox/issues/3529) - Enable filtering circuits list by region
## Bug Fixes
* [#3435](https://github.com/netbox-community/netbox/issues/3435) - Change IP/prefix CSV export to reference VRF name instead of RD
* [#3464](https://github.com/netbox-community/netbox/issues/3464) - Fix foreground text color on color picker fields
* [#3519](https://github.com/netbox-community/netbox/issues/3519) - Prevent cables from being terminated to virtual/wireless interfaces via API
* [#3521](https://github.com/netbox-community/netbox/issues/3521) - Fix error in `parseURL` related to variables in API URL
* [#3531](https://github.com/netbox-community/netbox/issues/3531) - Fixed rack role foreground color
* [#3534](https://github.com/netbox-community/netbox/issues/3534) - Added blank option for untagged VLANs
* [#3540](https://github.com/netbox-community/netbox/issues/3540) - Fixed virtual machine interface edit with new inline vlan edit fields
* [#3543](https://github.com/netbox-community/netbox/issues/3543) - Added inline VLAN editing to virtual machine interfaces
---
# v2.6.4 (2019-09-19)
## Enhancements
* [#2160](https://github.com/netbox-community/netbox/issues/2160) - Add bulk editing for interface VLAN assignment
* [#3027](https://github.com/netbox-community/netbox/issues/3028) - Add `local_context_data` boolean filter for devices
* [#3318](https://github.com/netbox-community/netbox/issues/3318) - Increase length of platform name and slug to 100 characters
* [#3341](https://github.com/netbox-community/netbox/issues/3341) - Enable inline VLAN assignment while editing an interface
* [#3485](https://github.com/netbox-community/netbox/issues/3485) - Enable embedded graphs for devices
* [#3510](https://github.com/netbox-community/netbox/issues/3510) - Add minimum/maximum prefix length enforcement for `IPNetworkVar`
## Bug Fixes
* [#3489](https://github.com/netbox-community/netbox/issues/3489) - Prevent exception triggered by webhook upon object deletion
* [#3501](https://github.com/netbox-community/netbox/issues/3501) - Fix rendering of checkboxes on custom script forms
* [#3511](https://github.com/netbox-community/netbox/issues/3511) - Correct API URL for nested device bays
* [#3513](https://github.com/netbox-community/netbox/issues/3513) - Fix assignment of tags when creating front/rear ports
* [#3514](https://github.com/netbox-community/netbox/issues/3514) - Label TextVar fields when rendering custom script forms
---
# v2.6.3 (2019-09-04)
## New Features
### Custom Scripts ([#3415](https://github.com/netbox-community/netbox/issues/3415))
Custom scripts allow for the execution of arbitrary code via the NetBox UI. They can be used to automatically create, manipulate, or clean up objects or perform other tasks within NetBox. Scripts are defined as Python files which contain one or more subclasses of `extras.scripts.Script`. Variable fields can be defined within scripts, which render as form fields within the web UI to prompt the user for input data. Scripts are executed and information is logged via the web UI. Please see [the docs](https://netbox.readthedocs.io/en/stable/additional-features/custom-scripts/) for more detail.
Note: There are currently no API endpoints for this feature. These are planned for the upcoming v2.7 release.
## Enhancements
* [#3386](https://github.com/netbox-community/netbox/issues/3386) - Add `mac_address` filter for virtual machines
* [#3391](https://github.com/netbox-community/netbox/issues/3391) - Update Bootstrap CSS to v3.4.1
* [#3405](https://github.com/netbox-community/netbox/issues/3405) - Fix population of power port/outlet details on device creation
* [#3422](https://github.com/netbox-community/netbox/issues/3422) - Prevent navigation menu from overlapping page content
* [#3430](https://github.com/netbox-community/netbox/issues/3430) - Linkify platform field on device view
* [#3454](https://github.com/netbox-community/netbox/issues/3454) - Enable filtering circuits by region
* [#3456](https://github.com/netbox-community/netbox/issues/3456) - Enable bulk editing of tag color
## Bug Fixes
* [#3392](https://github.com/netbox-community/netbox/issues/3392) - Add database index for ObjectChange time
* [#3420](https://github.com/netbox-community/netbox/issues/3420) - Serial number filter for racks, devices, and inventory items is now case-insensitive
* [#3428](https://github.com/netbox-community/netbox/issues/3428) - Fixed cache invalidation issues ([#3300](https://github.com/netbox-community/netbox/issues/3300), [#3363](https://github.com/netbox-community/netbox/issues/3363), [#3379](https://github.com/netbox-community/netbox/issues/3379), [#3382](https://github.com/netbox-community/netbox/issues/3382)) by switching to `prefetch_related()` instead of `select_related()` and removing use of `update()`
* [#3421](https://github.com/netbox-community/netbox/issues/3421) - Fix exception when ordering power connections list by PDU
* [#3424](https://github.com/netbox-community/netbox/issues/3424) - Fix tag coloring for non-linked tags
* [#3426](https://github.com/netbox-community/netbox/issues/3426) - Improve API error handling for ChoiceFields
---
# v2.6.2 (2019-08-02)
## Enhancements
* [#984](https://github.com/netbox-community/netbox/issues/984) - Allow ordering circuits by A/Z side
* [#3307](https://github.com/netbox-community/netbox/issues/3307) - Add power panels count to home page
* [#3314](https://github.com/netbox-community/netbox/issues/3314) - Paginate object changelog entries
* [#3367](https://github.com/netbox-community/netbox/issues/3367) - Add BNC port type and coaxial cable type
* [#3368](https://github.com/netbox-community/netbox/issues/3368) - Indicate indefinite changelog retention when applicable
* [#3370](https://github.com/netbox-community/netbox/issues/3370) - Add filter class to VirtualChassis API
## Bug Fixes
* [#3018](https://github.com/netbox-community/netbox/issues/3018) - Components connected via a cable must have an equal number of positions
* [#3289](https://github.com/netbox-community/netbox/issues/3289) - Prevent position from being nullified when moving a device to a new rack
* [#3293](https://github.com/netbox-community/netbox/issues/3293) - Enable filtering device components by multiple device IDs
* [#3315](https://github.com/netbox-community/netbox/issues/3315) - Enable filtering devices/interfaces by multiple MAC addresses
* [#3317](https://github.com/netbox-community/netbox/issues/3317) - Fix permissions for ConfigContextBulkDeleteView
* [#3323](https://github.com/netbox-community/netbox/issues/3323) - Fix permission evaluation for interface connections view
* [#3342](https://github.com/netbox-community/netbox/issues/3342) - Fix cluster delete button
* [#3384](https://github.com/netbox-community/netbox/issues/3384) - Maximum and allocated draw fields should be included on power port template creation form
* [#3385](https://github.com/netbox-community/netbox/issues/3385) - Fix power panels list when bulk editing power feeds
---
# v2.6.1 (2019-06-25)
## Enhancements
* [#3154](https://github.com/netbox-community/netbox/issues/3154) - Add `virtual_chassis_member` device filter
* [#3277](https://github.com/netbox-community/netbox/issues/3277) - Add cable trace buttons for console and power ports
* [#3281](https://github.com/netbox-community/netbox/issues/3281) - Hide custom links which render as empty text
## Bug Fixes
* [#3229](https://github.com/netbox-community/netbox/issues/3229) - Limit rack group selection by parent site on racks list
* [#3269](https://github.com/netbox-community/netbox/issues/3269) - Raise validation error when specifying non-existent cable terminations
* [#3275](https://github.com/netbox-community/netbox/issues/3275) - Fix error when adding power outlets to a device type
* [#3279](https://github.com/netbox-community/netbox/issues/3279) - Reset the PostgreSQL sequence for Tag and TaggedItem IDs
* [#3283](https://github.com/netbox-community/netbox/issues/3283) - Fix rack group assignment on PowerFeed CSV import
* [#3290](https://github.com/netbox-community/netbox/issues/3290) - Fix server error when viewing cascaded PDUs
* [#3292](https://github.com/netbox-community/netbox/issues/3292) - Ignore empty URL query parameters
---
# v2.6.0 (2019-06-20)
## New Features
### Power Panels and Feeds ([#54](https://github.com/netbox-community/netbox/issues/54))
NetBox now supports power circuit modeling via two new models: power panels and power feeds. Power feeds are terminated
to power panels and are optionally associated with individual racks. Each power feed defines a supply type (AC/DC),
amperage, voltage, and phase. A power port can be connected directly to a power feed, but a power feed may have only one
power port connected to it.
Additionally, the power port model, which represents a device's power input, has been extended to include fields
denoting maximum and allocated draw, in volt-amperes. This allows a device (e.g. a PDU) to calculate its total load
compared to its connected power feed.
### Caching ([#2647](https://github.com/netbox-community/netbox/issues/2647))
To improve performance, NetBox now supports caching for most object and list views. Caching is implemented using Redis,
which is now a required dependency. (Previously, Redis was required only if webhooks were enabled.)
A new configuration parameter is available to control the cache timeout:
```
# Cache timeout (in seconds)
CACHE_TIMEOUT = 900
```
### View Permissions ([#323](https://github.com/netbox-community/netbox/issues/323))
Django 2.1 introduced the ability to enforce view-only permissions for different object types. NetBox now enforces
these by default. You can grant view permission to a user or group by assigning the "can view" permission for the
desired object(s).
To exempt certain object types from the enforcement of view permissions, so that any user (including anonymous users)
can view them, add them to the new `EXEMPT_VIEW_PERMISSIONS` setting in `configuration.py`:
```
EXEMPT_VIEW_PERMISSIONS = [
'dcim.site',
'ipam.prefix',
]
```
To exclude _all_ objects, effectively disabling view permissions and restoring pre-v2.6 behavior, set:
```
EXEMPT_VIEW_PERMISSIONS = ['*']
```
### Custom Links ([#969](https://github.com/netbox-community/netbox/issues/969))
Custom links are created under the admin UI and will be displayed on each object of the selected type. Link text and
URLs can be formed from Jinja2 template code, with the viewed object passed as context data. For example, to link to an
external NMS from the device view, you might create a custom link with the following URL:
```
https://nms.example.com/nodes/?name={{ obj.name }}
```
Custom links appear as buttons at the top of the object view. Grouped links will render as a dropdown menu beneath a
single button.
### Prometheus Metrics ([#3104](https://github.com/netbox-community/netbox/issues/3104))
NetBox now supports exposing native Prometheus metrics from the application. [Prometheus](https://prometheus.io/) is a
popular time series metric platform used for monitoring. Metric exposition can be toggled with the `METRICS_ENABLED`
configuration setting; it is not enabled by default. NetBox exposes metrics at the `/metrics` HTTP endpoint, e.g.
`https://netbox.local/metrics`.
NetBox makes use of the [django-prometheus](https://github.com/korfuri/django-prometheus) library to export a number of
different types of metrics, including:
* Per model insert, update, and delete counters
* Per view request counters
* Per view request latency histograms
* Request body size histograms
* Response body size histograms
* Response code counters
* Database connection, execution, and error counters
* Cache hit, miss, and invalidation counters
* Django middleware latency histograms
* Other Django related metadata metrics
For the exhaustive list of exposed metrics, visit the `/metrics` endpoint on your NetBox instance. See the documentation
for more details on using Prometheus metrics in NetBox.
## Changes
### New Dependency: Redis
[Redis](https://redis.io/) is an in-memory data store similar to memcached. While Redis has been an optional component
of NetBox since the introduction of webhooks in version 2.4, it is now required to support NetBox's new caching
functionality (as well as other planned features). Redis can be installed via your platform's package manager: for
example, `sudo apt-get install redis-server` on Ubuntu or `sudo yum install redis` on CentOS.
The Redis database is configured using a configuration setting similar to `DATABASE` in `configuration.py`:
```
REDIS = {
'HOST': 'localhost',
'PORT': 6379,
'PASSWORD': '',
'DATABASE': 0,
'CACHE_DATABASE': 1,
'DEFAULT_TIMEOUT': 300,
'SSL': False,
}
```
Note that if you were using these settings in a prior release with webhooks, the `DATABASE` setting remains the same but
an additional `CACHE_DATABASE` setting has been added with a default value of 1 to support the caching backend. The
`DATABASE` setting will be renamed in a future release of NetBox to better relay the meaning of the setting. It is
highly recommended to keep the webhook and cache databases seperate. Using the same database number for both may result
in webhook processing data being lost during cache flushing events.
### API Support for Specifying Related Objects by Attributes([#3077](https://github.com/netbox-community/netbox/issues/3077))
Previously, specifying a related object in an API request required knowing the primary key (integer ID) of that object.
For example, when creating a new device, its rack would be specified as an integer:
```
{
"name": "MyNewDevice",
"rack": 123,
...
}
```
The NetBox API now also supports referencing related objects by a set of sufficiently unique attrbiutes. For example, a
rack can be identified by its name and parent site:
```
{
"name": "MyNewDevice",
"rack": {
"site": {
"name": "Equinix DC6"
},
"name": "R204"
},
...
}
```
There is no limit to the depth of nested references. Note that if the provided parameters do not return exactly one
object, a validation error is raised.
### API Device/VM Config Context Included by Default ([#2350](https://github.com/netbox-community/netbox/issues/2350))
The rendered config context for devices and VMs is now included by default in all API results (list and detail views).
Previously, the rendered config context was available only in the detail view for individual objects. Users with large
amounts of context data may observe a performance drop when returning multiple objects. To combat this, in cases where
the rendered config context is not needed, the query parameter `?exclude=config_context` may be appended to the request
URL to exclude the config context data from the API response.
### Changes to Tag Permissions
NetBox now makes use of its own `Tag` model instead of the stock model which ships with django-taggit. This new model
lives in the `extras` app and thus any permissions that you may have configured using "Taggit | Tag" should be changed
to now use "Extras | Tag." Also note that the admin interface for tags has been removed as it was redundant to the
functionality provided by the front end UI.
### CORS_ORIGIN_WHITELIST Requires URI Scheme
If you have the `CORS_ORIGIN_WHITELIST` configuration parameter defined, note that each origin must now incldue a URI
scheme. This change was introuced in django-cors-headers 3.0.
## Enhancements
* [#166](https://github.com/netbox-community/netbox/issues/166) - Add `dns_name` field to IPAddress
* [#524](https://github.com/netbox-community/netbox/issues/524) - Added power utilization graphs to power feeds, devices, and racks
* [#1792](https://github.com/netbox-community/netbox/issues/1792) - Add CustomFieldChoices API endpoint at `/api/extras/_custom_field_choices/`
* [#1863](https://github.com/netbox-community/netbox/issues/1863) - Add child object counts to API representation of organizational objects
* [#2324](https://github.com/netbox-community/netbox/issues/2324) - Add `color` field for tags
* [#2643](https://github.com/netbox-community/netbox/issues/2643) - Add `description` field to console/power components and device bays
* [#2791](https://github.com/netbox-community/netbox/issues/2791) - Add `comments` field for tags
* [#2920](https://github.com/netbox-community/netbox/issues/2920) - Rename Interface `form_factor` to `type` (backward-compatible until v2.7)
* [#2926](https://github.com/netbox-community/netbox/issues/2926) - Add change logging to the Tag model
* [#3038](https://github.com/netbox-community/netbox/issues/3038) - OR logic now used when multiple values of a query filter are passed
* [#3264](https://github.com/netbox-community/netbox/issues/3264) - Annotate changelog retention time on UI
## Bug Fixes
* [#2968](https://github.com/netbox-community/netbox/issues/2968) - Correct API documentation for SerializerMethodFields
* [#3176](https://github.com/netbox-community/netbox/issues/3176) - Add cable trace button for console server ports and power outlets
* [#3231](https://github.com/netbox-community/netbox/issues/3231) - Fixed cosmetic error indicating a missing schema migration
* [#3239](https://github.com/netbox-community/netbox/issues/3239) - Corrected count of tags reported via API
## Bug Fixes From v2.6-beta1
* [#3123](https://github.com/netbox-community/netbox/issues/3123) - Exempt `/metrics` view from authentication
* [#3125](https://github.com/netbox-community/netbox/issues/3125) - Fix exception when viewing PDUs
* [#3126](https://github.com/netbox-community/netbox/issues/3126) - Incorrect calculation of PowerFeed available power
* [#3130](https://github.com/netbox-community/netbox/issues/3130) - Fix exception when creating a new power outlet
* [#3136](https://github.com/netbox-community/netbox/issues/3136) - Add power draw fields to power port creation form
* [#3137](https://github.com/netbox-community/netbox/issues/3137) - Add `power_port` and `feed_leg` fields to power outlet creation form
* [#3140](https://github.com/netbox-community/netbox/issues/3140) - Add bulk edit capability for power outlets and console server ports
* [#3204](https://github.com/netbox-community/netbox/issues/3204) - Fix interface filtering when connecting cables
* [#3207](https://github.com/netbox-community/netbox/issues/3207) - Fix link for connecting interface to rear port
* [#3258](https://github.com/netbox-community/netbox/issues/3258) - Exception raised when creating/viewing a circuit with a non-connected termination
## API Changes
* New API endpoints for power modeling: `/api/dcim/power-panels/` and `/api/dcim/power-feeds/`
* New API endpoint for custom field choices: `/api/extras/_custom_field_choices/`
* ForeignKey fields now accept either the related object PK or a dictionary of attributes describing the related object.
* Organizational objects now include child object counts. For example, the Role serializer includes `prefix_count` and `vlan_count`.
* The `id__in` filter is now deprecated and will be removed in v2.7. (Begin using the `?id=1&id=2` format instead.)
* Added a `description` field for all device components.
* dcim.Device: The devices list endpoint now includes rendered context data.
* dcim.DeviceType: `instance_count` has been renamed to `device_count`.
* dcim.Interface: `form_factor` has been renamed to `type`. Backward compatibility for `form_factor` will be maintained until NetBox v2.7.
* dcim.Interface: The `type` filter has been renamed to `kind`.
* dcim.Site: The `count_*` read-only fields have been renamed to `*_count` for consistency with other objects.
* dcim.Site: Added the `virtualmachine_count` read-only field.
* extras.Tag: Added `color` and `comments` fields to the Tag serializer.
* virtualization.VirtualMachine: The virtual machines list endpoint now includes rendered context data.

View File

@@ -1,6 +1,6 @@
site_name: NetBox
theme: readthedocs
repo_url: https://github.com/digitalocean/netbox
repo_url: https://github.com/netbox-community/netbox
pages:
- Introduction: 'index.md'
@@ -27,15 +27,18 @@ pages:
- Secrets: 'core-functionality/secrets.md'
- Tenancy: 'core-functionality/tenancy.md'
- Additional Features:
- Tags: 'additional-features/tags.md'
- Custom Fields: 'additional-features/custom-fields.md'
- Caching: 'additional-features/caching.md'
- Change Logging: 'additional-features/change-logging.md'
- Context Data: 'additional-features/context-data.md'
- Custom Fields: 'additional-features/custom-fields.md'
- Custom Scripts: 'additional-features/custom-scripts.md'
- Export Templates: 'additional-features/export-templates.md'
- Graphs: 'additional-features/graphs.md'
- Topology Maps: 'additional-features/topology-maps.md'
- Prometheus Metrics: 'additional-features/prometheus-metrics.md'
- Reports: 'additional-features/reports.md'
- Tags: 'additional-features/tags.md'
- Topology Maps: 'additional-features/topology-maps.md'
- Webhooks: 'additional-features/webhooks.md'
- Change Logging: 'additional-features/change-logging.md'
- Administration:
- Replicating NetBox: 'administration/replicating-netbox.md'
- NetBox Shell: 'administration/netbox-shell.md'
@@ -50,6 +53,24 @@ pages:
- Utility Views: 'development/utility-views.md'
- Extending Models: 'development/extending-models.md'
- Release Checklist: 'development/release-checklist.md'
- Release Notes:
- Version 2.6: 'release-notes/version-2.6.md'
- Version 2.5: 'release-notes/version-2.5.md'
- Version 2.4: 'release-notes/version-2.4.md'
- Version 2.3: 'release-notes/version-2.3.md'
- Version 2.2: 'release-notes/version-2.2.md'
- Version 2.1: 'release-notes/version-2.1.md'
- Version 2.0: 'release-notes/version-2.0.md'
- Version 1.9: 'release-notes/version-1.9.md'
- Version 1.8: 'release-notes/version-1.8.md'
- Version 1.7: 'release-notes/version-1.7.md'
- Version 1.6: 'release-notes/version-1.6.md'
- Version 1.5: 'release-notes/version-1.5.md'
- Version 1.4: 'release-notes/version-1.4.md'
- Version 1.3: 'release-notes/version-1.3.md'
- Version 1.2: 'release-notes/version-1.2.md'
- Version 1.1: 'release-notes/version-1.1.md'
- Version 1.0: 'release-notes/version-1.0.md'
markdown_extensions:
- admonition:

View File

@@ -17,10 +17,11 @@ __all__ = [
class NestedProviderSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:provider-detail')
circuit_count = serializers.IntegerField(read_only=True)
class Meta:
model = Provider
fields = ['id', 'url', 'name', 'slug']
fields = ['id', 'url', 'name', 'slug', 'circuit_count']
#
@@ -29,10 +30,11 @@ class NestedProviderSerializer(WritableNestedSerializer):
class NestedCircuitTypeSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittype-detail')
circuit_count = serializers.IntegerField(read_only=True)
class Meta:
model = CircuitType
fields = ['id', 'url', 'name', 'slug']
fields = ['id', 'url', 'name', 'slug', 'circuit_count']
class NestedCircuitSerializer(WritableNestedSerializer):

View File

@@ -1,3 +1,4 @@
from rest_framework import serializers
from taggit_serializer.serializers import TaggitSerializer, TagListSerializerField
from circuits.constants import CIRCUIT_STATUS_CHOICES
@@ -16,12 +17,13 @@ from .nested_serializers import *
class ProviderSerializer(TaggitSerializer, CustomFieldModelSerializer):
tags = TagListSerializerField(required=False)
circuit_count = serializers.IntegerField(read_only=True)
class Meta:
model = Provider
fields = [
'id', 'name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments', 'tags',
'custom_fields', 'created', 'last_updated',
'custom_fields', 'created', 'last_updated', 'circuit_count',
]
@@ -30,10 +32,11 @@ class ProviderSerializer(TaggitSerializer, CustomFieldModelSerializer):
#
class CircuitTypeSerializer(ValidatedModelSerializer):
circuit_count = serializers.IntegerField(read_only=True)
class Meta:
model = CircuitType
fields = ['id', 'name', 'slug']
fields = ['id', 'name', 'slug', 'circuit_count']
class CircuitSerializer(TaggitSerializer, CustomFieldModelSerializer):

View File

@@ -1,3 +1,4 @@
from django.db.models import Count
from django.shortcuts import get_object_or_404
from rest_framework.decorators import action
from rest_framework.response import Response
@@ -27,12 +28,14 @@ class CircuitsFieldChoicesViewSet(FieldChoicesViewSet):
#
class ProviderViewSet(CustomFieldModelViewSet):
queryset = Provider.objects.prefetch_related('tags')
queryset = Provider.objects.prefetch_related('tags').annotate(
circuit_count=Count('circuits')
)
serializer_class = serializers.ProviderSerializer
filterset_class = filters.ProviderFilter
@action(detail=True)
def graphs(self, request, pk=None):
def graphs(self, request, pk):
"""
A convenience method for rendering graphs for a particular provider.
"""
@@ -47,7 +50,9 @@ class ProviderViewSet(CustomFieldModelViewSet):
#
class CircuitTypeViewSet(ModelViewSet):
queryset = CircuitType.objects.all()
queryset = CircuitType.objects.annotate(
circuit_count=Count('circuits')
)
serializer_class = serializers.CircuitTypeSerializer
filterset_class = filters.CircuitTypeFilter
@@ -57,7 +62,7 @@ class CircuitTypeViewSet(ModelViewSet):
#
class CircuitViewSet(CustomFieldModelViewSet):
queryset = Circuit.objects.select_related('type', 'tenant', 'provider').prefetch_related('tags')
queryset = Circuit.objects.prefetch_related('type', 'tenant', 'provider').prefetch_related('tags')
serializer_class = serializers.CircuitSerializer
filterset_class = filters.CircuitFilter
@@ -67,7 +72,7 @@ class CircuitViewSet(CustomFieldModelViewSet):
#
class CircuitTerminationViewSet(ModelViewSet):
queryset = CircuitTermination.objects.select_related(
queryset = CircuitTermination.objects.prefetch_related(
'circuit', 'site', 'connected_endpoint__device', 'cable'
)
serializer_class = serializers.CircuitTerminationSerializer

View File

@@ -1,4 +1,3 @@
# Circuit statuses
CIRCUIT_STATUS_DEPROVISIONING = 0
CIRCUIT_STATUS_ACTIVE = 1

View File

@@ -1,15 +1,15 @@
import django_filters
from django.db.models import Q
from dcim.models import Site
from dcim.models import Region, Site
from extras.filters import CustomFieldFilterSet
from tenancy.models import Tenant
from utilities.filters import NameSlugSearchFilterSet, NumericInFilter, TagFilter
from .constants import CIRCUIT_STATUS_CHOICES
from .models import Provider, Circuit, CircuitTermination, CircuitType
from tenancy.filtersets import TenancyFilterSet
from utilities.filters import NameSlugSearchFilterSet, NumericInFilter, TagFilter, TreeNodeMultipleChoiceFilter
from .constants import *
from .models import Circuit, CircuitTermination, CircuitType, Provider
class ProviderFilter(CustomFieldFilterSet, django_filters.FilterSet):
class ProviderFilter(CustomFieldFilterSet):
id__in = NumericInFilter(
field_name='id',
lookup_expr='in'
@@ -51,10 +51,10 @@ class CircuitTypeFilter(NameSlugSearchFilterSet):
class Meta:
model = CircuitType
fields = ['name', 'slug']
fields = ['id', 'name', 'slug']
class CircuitFilter(CustomFieldFilterSet, django_filters.FilterSet):
class CircuitFilter(CustomFieldFilterSet, TenancyFilterSet):
id__in = NumericInFilter(
field_name='id',
lookup_expr='in'
@@ -87,16 +87,6 @@ class CircuitFilter(CustomFieldFilterSet, django_filters.FilterSet):
choices=CIRCUIT_STATUS_CHOICES,
null_value=None
)
tenant_id = django_filters.ModelMultipleChoiceFilter(
queryset=Tenant.objects.all(),
label='Tenant (ID)',
)
tenant = django_filters.ModelMultipleChoiceFilter(
field_name='tenant__slug',
queryset=Tenant.objects.all(),
to_field_name='slug',
label='Tenant (slug)',
)
site_id = django_filters.ModelMultipleChoiceFilter(
field_name='terminations__site',
queryset=Site.objects.all(),
@@ -108,6 +98,17 @@ class CircuitFilter(CustomFieldFilterSet, django_filters.FilterSet):
to_field_name='slug',
label='Site (slug)',
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='terminations__site__region__in',
label='Region (ID)',
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='terminations__site__region__in',
to_field_name='slug',
label='Region (slug)',
)
tag = TagFilter()
class Meta:

View File

@@ -1,15 +1,15 @@
from django import forms
from taggit.forms import TagField
from dcim.models import Site
from dcim.models import Region, Site
from extras.forms import AddRemoveTagsForm, CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm
from tenancy.forms import TenancyForm
from tenancy.forms import TenancyFilterForm, TenancyForm
from tenancy.models import Tenant
from utilities.forms import (
APISelect, APISelectMultiple, add_blank_choice, BootstrapMixin, CommentField, CSVChoiceField,
FilterChoiceField, SmallTextarea, SlugField, StaticSelect2, StaticSelect2Multiple
)
from .constants import CIRCUIT_STATUS_CHOICES
from .constants import *
from .models import Circuit, CircuitTermination, CircuitType, Provider
@@ -107,7 +107,7 @@ class ProviderFilterForm(BootstrapMixin, CustomFieldFilterForm):
site = FilterChoiceField(
queryset=Site.objects.all(),
to_field_name='slug',
widget=APISelect(
widget=APISelectMultiple(
api_url="/api/dcim/sites/",
value_field="slug",
)
@@ -256,7 +256,8 @@ class CircuitBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEdit
required=False
)
comments = CommentField(
widget=SmallTextarea
widget=SmallTextarea,
label='Comments'
)
class Meta:
@@ -265,8 +266,11 @@ class CircuitBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEdit
]
class CircuitFilterForm(BootstrapMixin, CustomFieldFilterForm):
class CircuitFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
model = Circuit
field_order = [
'q', 'type', 'provider', 'status', 'region', 'site', 'tenant_group', 'tenant', 'commit_rate',
]
q = forms.CharField(
required=False,
label='Search'
@@ -292,14 +296,13 @@ class CircuitFilterForm(BootstrapMixin, CustomFieldFilterForm):
required=False,
widget=StaticSelect2Multiple()
)
tenant = FilterChoiceField(
queryset=Tenant.objects.all(),
region = forms.ModelMultipleChoiceField(
queryset=Region.objects.all(),
to_field_name='slug',
null_label='-- None --',
required=False,
widget=APISelectMultiple(
api_url="/api/tenancy/tenants/",
api_url="/api/dcim/regions/",
value_field="slug",
null_option=True,
)
)
site = FilterChoiceField(

View File

@@ -0,0 +1,25 @@
# Generated by Django 2.1.4 on 2019-02-20 06:56
from django.db import migrations
import taggit.managers
class Migration(migrations.Migration):
dependencies = [
('circuits', '0014_circuittermination_description'),
('extras', '0019_tag_taggeditem'),
]
operations = [
migrations.AlterField(
model_name='circuit',
name='tags',
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
),
migrations.AlterField(
model_name='provider',
name='tags',
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
),
]

View File

@@ -6,10 +6,10 @@ from taggit.managers import TaggableManager
from dcim.constants import CONNECTION_STATUS_CHOICES, STATUS_CLASSES
from dcim.fields import ASNField
from dcim.models import CableTermination
from extras.models import CustomFieldModel, ObjectChange
from extras.models import CustomFieldModel, ObjectChange, TaggedItem
from utilities.models import ChangeLoggedModel
from utilities.utils import serialize_object
from .constants import CIRCUIT_STATUS_ACTIVE, CIRCUIT_STATUS_CHOICES, TERM_SIDE_CHOICES
from .constants import *
class Provider(ChangeLoggedModel, CustomFieldModel):
@@ -55,7 +55,7 @@ class Provider(ChangeLoggedModel, CustomFieldModel):
object_id_field='obj_id'
)
tags = TaggableManager()
tags = TaggableManager(through=TaggedItem)
csv_headers = ['name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments']
@@ -165,7 +165,7 @@ class Circuit(ChangeLoggedModel, CustomFieldModel):
object_id_field='obj_id'
)
tags = TaggableManager()
tags = TaggableManager(through=TaggedItem)
csv_headers = [
'cid', 'provider', 'type', 'status', 'tenant', 'install_date', 'commit_rate', 'description', 'comments',
@@ -270,18 +270,21 @@ class CircuitTermination(CableTermination):
def __str__(self):
return 'Side {}'.format(self.get_term_side_display())
def log_change(self, user, request_id, action):
"""
Reference the parent circuit when recording the change.
"""
ObjectChange(
user=user,
request_id=request_id,
def to_objectchange(self, action):
# Annotate the parent Circuit
try:
related_object = self.circuit
except Circuit.DoesNotExist:
# Parent circuit has been deleted
related_object = None
return ObjectChange(
changed_object=self,
related_object=self.circuit,
object_repr=str(self),
action=action,
related_object=related_object,
object_data=serialize_object(self)
).save()
)
@property
def parent(self):
@@ -290,6 +293,6 @@ class CircuitTermination(CableTermination):
def get_peer_termination(self):
peer_side = 'Z' if self.term_side == 'A' else 'A'
try:
return CircuitTermination.objects.select_related('site').get(circuit=self.circuit, term_side=peer_side)
return CircuitTermination.objects.prefetch_related('site').get(circuit=self.circuit, term_side=peer_side)
except CircuitTermination.DoesNotExist:
return None

View File

@@ -10,4 +10,8 @@ def update_circuit(instance, **kwargs):
"""
When a CircuitTermination has been modified, update the last_updated time of its parent Circuit.
"""
Circuit.objects.filter(pk=instance.circuit_id).update(last_updated=timezone.now())
circuits = Circuit.objects.filter(pk=instance.circuit_id)
time = timezone.now()
for circuit in circuits:
circuit.last_updated = time
circuit.save()

View File

@@ -1,5 +1,4 @@
import django_tables2 as tables
from django.utils.safestring import mark_safe
from django_tables2.utils import Accessor
from tenancy.tables import COL_TENANT
@@ -11,7 +10,8 @@ CIRCUITTYPE_ACTIONS = """
<i class="fa fa-history"></i>
</a>
{% if perms.circuit.change_circuittype %}
<a href="{% url 'circuits:circuittype_edit' slug=record.slug %}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
<a href="{% url 'circuits:circuittype_edit' slug=record.slug %}?return_url={{ request.path }}"
class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
{% endif %}
"""
@@ -20,15 +20,6 @@ STATUS_LABEL = """
"""
class CircuitTerminationColumn(tables.Column):
def render(self, value):
return mark_safe('<a href="{}">{}</a>'.format(
value.site.get_absolute_url(),
value.site
))
#
# Providers
#
@@ -59,7 +50,7 @@ class CircuitTypeTable(BaseTable):
name = tables.LinkColumn()
circuit_count = tables.Column(verbose_name='Circuits')
actions = tables.TemplateColumn(
template_code=CIRCUITTYPE_ACTIONS, attrs={'td': {'class': 'text-right'}}, verbose_name=''
template_code=CIRCUITTYPE_ACTIONS, attrs={'td': {'class': 'text-right noprint'}}, verbose_name=''
)
class Meta(BaseTable.Meta):
@@ -77,9 +68,13 @@ class CircuitTable(BaseTable):
provider = tables.LinkColumn('circuits:provider', args=[Accessor('provider.slug')])
status = tables.TemplateColumn(template_code=STATUS_LABEL, verbose_name='Status')
tenant = tables.TemplateColumn(template_code=COL_TENANT)
termination_a = CircuitTerminationColumn(orderable=False, verbose_name='A Side')
termination_z = CircuitTerminationColumn(orderable=False, verbose_name='Z Side')
a_side = tables.Column(
verbose_name='A Side'
)
z_side = tables.Column(
verbose_name='Z Side'
)
class Meta(BaseTable.Meta):
model = Circuit
fields = ('pk', 'cid', 'status', 'type', 'provider', 'tenant', 'termination_a', 'termination_z', 'description')
fields = ('pk', 'cid', 'status', 'type', 'provider', 'tenant', 'a_side', 'z_side', 'description')

View File

@@ -61,7 +61,7 @@ class ProviderTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
['circuit_count', 'id', 'name', 'slug', 'url']
)
def test_create_provider(self):
@@ -162,7 +162,7 @@ class CircuitTypeTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
['circuit_count', 'id', 'name', 'slug', 'url']
)
def test_create_circuittype(self):

View File

@@ -4,13 +4,15 @@ from django.test import Client, TestCase
from django.urls import reverse
from circuits.models import Circuit, CircuitType, Provider
from utilities.testing import create_test_user
class ProviderTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['circuits.view_provider'])
self.client = Client()
self.client.force_login(user)
Provider.objects.bulk_create([
Provider(name='Provider 1', slug='provider-1', asn=65001),
@@ -38,8 +40,9 @@ class ProviderTestCase(TestCase):
class CircuitTypeTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['circuits.view_circuittype'])
self.client = Client()
self.client.force_login(user)
CircuitType.objects.bulk_create([
CircuitType(name='Circuit Type 1', slug='circuit-type-1'),
@@ -58,8 +61,9 @@ class CircuitTypeTestCase(TestCase):
class CircuitTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['circuits.view_circuit'])
self.client = Client()
self.client.force_login(user)
provider = Provider(name='Provider 1', slug='provider-1', asn=65001)
provider.save()
@@ -84,8 +88,8 @@ class CircuitTestCase(TestCase):
response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params)))
self.assertEqual(response.status_code, 200)
def test_provider(self):
def test_circuit(self):
provider = Provider.objects.first()
response = self.client.get(provider.get_absolute_url())
circuit = Circuit.objects.first()
response = self.client.get(circuit.get_absolute_url())
self.assertEqual(response.status_code, 200)

View File

@@ -1,4 +1,4 @@
from django.conf.urls import url
from django.urls import path
from dcim.views import CableCreateView, CableTraceView
from extras.views import ObjectChangeLogView
@@ -9,41 +9,42 @@ app_name = 'circuits'
urlpatterns = [
# Providers
url(r'^providers/$', views.ProviderListView.as_view(), name='provider_list'),
url(r'^providers/add/$', views.ProviderCreateView.as_view(), name='provider_add'),
url(r'^providers/import/$', views.ProviderBulkImportView.as_view(), name='provider_import'),
url(r'^providers/edit/$', views.ProviderBulkEditView.as_view(), name='provider_bulk_edit'),
url(r'^providers/delete/$', views.ProviderBulkDeleteView.as_view(), name='provider_bulk_delete'),
url(r'^providers/(?P<slug>[\w-]+)/$', views.ProviderView.as_view(), name='provider'),
url(r'^providers/(?P<slug>[\w-]+)/edit/$', views.ProviderEditView.as_view(), name='provider_edit'),
url(r'^providers/(?P<slug>[\w-]+)/delete/$', views.ProviderDeleteView.as_view(), name='provider_delete'),
url(r'^providers/(?P<slug>[\w-]+)/changelog/$', ObjectChangeLogView.as_view(), name='provider_changelog', kwargs={'model': Provider}),
path(r'providers/', views.ProviderListView.as_view(), name='provider_list'),
path(r'providers/add/', views.ProviderCreateView.as_view(), name='provider_add'),
path(r'providers/import/', views.ProviderBulkImportView.as_view(), name='provider_import'),
path(r'providers/edit/', views.ProviderBulkEditView.as_view(), name='provider_bulk_edit'),
path(r'providers/delete/', views.ProviderBulkDeleteView.as_view(), name='provider_bulk_delete'),
path(r'providers/<slug:slug>/', views.ProviderView.as_view(), name='provider'),
path(r'providers/<slug:slug>/edit/', views.ProviderEditView.as_view(), name='provider_edit'),
path(r'providers/<slug:slug>/delete/', views.ProviderDeleteView.as_view(), name='provider_delete'),
path(r'providers/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='provider_changelog', kwargs={'model': Provider}),
# Circuit types
url(r'^circuit-types/$', views.CircuitTypeListView.as_view(), name='circuittype_list'),
url(r'^circuit-types/add/$', views.CircuitTypeCreateView.as_view(), name='circuittype_add'),
url(r'^circuit-types/import/$', views.CircuitTypeBulkImportView.as_view(), name='circuittype_import'),
url(r'^circuit-types/delete/$', views.CircuitTypeBulkDeleteView.as_view(), name='circuittype_bulk_delete'),
url(r'^circuit-types/(?P<slug>[\w-]+)/edit/$', views.CircuitTypeEditView.as_view(), name='circuittype_edit'),
url(r'^circuit-types/(?P<slug>[\w-]+)/changelog/$', ObjectChangeLogView.as_view(), name='circuittype_changelog', kwargs={'model': CircuitType}),
path(r'circuit-types/', views.CircuitTypeListView.as_view(), name='circuittype_list'),
path(r'circuit-types/add/', views.CircuitTypeCreateView.as_view(), name='circuittype_add'),
path(r'circuit-types/import/', views.CircuitTypeBulkImportView.as_view(), name='circuittype_import'),
path(r'circuit-types/delete/', views.CircuitTypeBulkDeleteView.as_view(), name='circuittype_bulk_delete'),
path(r'circuit-types/<slug:slug>/edit/', views.CircuitTypeEditView.as_view(), name='circuittype_edit'),
path(r'circuit-types/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='circuittype_changelog', kwargs={'model': CircuitType}),
# Circuits
url(r'^circuits/$', views.CircuitListView.as_view(), name='circuit_list'),
url(r'^circuits/add/$', views.CircuitCreateView.as_view(), name='circuit_add'),
url(r'^circuits/import/$', views.CircuitBulkImportView.as_view(), name='circuit_import'),
url(r'^circuits/edit/$', views.CircuitBulkEditView.as_view(), name='circuit_bulk_edit'),
url(r'^circuits/delete/$', views.CircuitBulkDeleteView.as_view(), name='circuit_bulk_delete'),
url(r'^circuits/(?P<pk>\d+)/$', views.CircuitView.as_view(), name='circuit'),
url(r'^circuits/(?P<pk>\d+)/edit/$', views.CircuitEditView.as_view(), name='circuit_edit'),
url(r'^circuits/(?P<pk>\d+)/delete/$', views.CircuitDeleteView.as_view(), name='circuit_delete'),
url(r'^circuits/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='circuit_changelog', kwargs={'model': Circuit}),
url(r'^circuits/(?P<pk>\d+)/terminations/swap/$', views.circuit_terminations_swap, name='circuit_terminations_swap'),
path(r'circuits/', views.CircuitListView.as_view(), name='circuit_list'),
path(r'circuits/add/', views.CircuitCreateView.as_view(), name='circuit_add'),
path(r'circuits/import/', views.CircuitBulkImportView.as_view(), name='circuit_import'),
path(r'circuits/edit/', views.CircuitBulkEditView.as_view(), name='circuit_bulk_edit'),
path(r'circuits/delete/', views.CircuitBulkDeleteView.as_view(), name='circuit_bulk_delete'),
path(r'circuits/<int:pk>/', views.CircuitView.as_view(), name='circuit'),
path(r'circuits/<int:pk>/edit/', views.CircuitEditView.as_view(), name='circuit_edit'),
path(r'circuits/<int:pk>/delete/', views.CircuitDeleteView.as_view(), name='circuit_delete'),
path(r'circuits/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='circuit_changelog', kwargs={'model': Circuit}),
path(r'circuits/<int:pk>/terminations/swap/', views.circuit_terminations_swap, name='circuit_terminations_swap'),
# Circuit terminations
url(r'^circuits/(?P<circuit>\d+)/terminations/add/$', views.CircuitTerminationCreateView.as_view(), name='circuittermination_add'),
url(r'^circuit-terminations/(?P<pk>\d+)/edit/$', views.CircuitTerminationEditView.as_view(), name='circuittermination_edit'),
url(r'^circuit-terminations/(?P<pk>\d+)/delete/$', views.CircuitTerminationDeleteView.as_view(), name='circuittermination_delete'),
url(r'^circuit-terminations/(?P<termination_a_id>\d+)/connect/$', CableCreateView.as_view(), name='circuittermination_connect', kwargs={'termination_a_type': CircuitTermination}),
url(r'^circuit-terminations/(?P<pk>\d+)/trace/$', CableTraceView.as_view(), name='circuittermination_trace', kwargs={'model': CircuitTermination}),
path(r'circuits/<int:circuit>/terminations/add/', views.CircuitTerminationCreateView.as_view(), name='circuittermination_add'),
path(r'circuit-terminations/<int:pk>/edit/', views.CircuitTerminationEditView.as_view(), name='circuittermination_edit'),
path(r'circuit-terminations/<int:pk>/delete/', views.CircuitTerminationDeleteView.as_view(), name='circuittermination_delete'),
path(r'circuit-terminations/<int:termination_a_id>/connect/<str:termination_b_type>/', CableCreateView.as_view(), name='circuittermination_connect', kwargs={'termination_a_type': CircuitTermination}),
path(r'circuit-terminations/<int:pk>/trace/', CableTraceView.as_view(), name='circuittermination_trace', kwargs={'model': CircuitTermination}),
]

View File

@@ -2,7 +2,7 @@ from django.contrib import messages
from django.contrib.auth.decorators import permission_required
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.db import transaction
from django.db.models import Count
from django.db.models import Count, OuterRef, Subquery
from django.shortcuts import get_object_or_404, redirect, render
from django.views.generic import View
@@ -20,7 +20,8 @@ from .models import Circuit, CircuitTermination, CircuitType, Provider
# Providers
#
class ProviderListView(ObjectListView):
class ProviderListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'circuits.view_provider'
queryset = Provider.objects.annotate(count_circuits=Count('circuits'))
filter = filters.ProviderFilter
filter_form = forms.ProviderFilterForm
@@ -28,16 +29,13 @@ class ProviderListView(ObjectListView):
template_name = 'circuits/provider_list.html'
class ProviderView(View):
class ProviderView(PermissionRequiredMixin, View):
permission_required = 'circuits.view_provider'
def get(self, request, slug):
provider = get_object_or_404(Provider, slug=slug)
circuits = Circuit.objects.filter(provider=provider).select_related(
'type', 'tenant'
).prefetch_related(
'terminations__site'
)
circuits = Circuit.objects.filter(provider=provider).prefetch_related('type', 'tenant', 'terminations__site')
show_graphs = Graph.objects.filter(type=GRAPH_TYPE_PROVIDER).exists()
return render(request, 'circuits/provider.html', {
@@ -93,7 +91,8 @@ class ProviderBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Circuit Types
#
class CircuitTypeListView(ObjectListView):
class CircuitTypeListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'circuits.view_circuittype'
queryset = CircuitType.objects.annotate(circuit_count=Count('circuits'))
table = tables.CircuitTypeTable
template_name = 'circuits/circuittype_list.html'
@@ -128,11 +127,14 @@ class CircuitTypeBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Circuits
#
class CircuitListView(ObjectListView):
queryset = Circuit.objects.select_related(
'provider', 'type', 'tenant'
).prefetch_related(
'terminations__site'
class CircuitListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'circuits.view_circuit'
_terminations = CircuitTermination.objects.filter(circuit=OuterRef('pk'))
queryset = Circuit.objects.prefetch_related(
'provider', 'type', 'tenant', 'terminations__site'
).annotate(
a_side=Subquery(_terminations.filter(term_side='A').values('site__name')[:1]),
z_side=Subquery(_terminations.filter(term_side='Z').values('site__name')[:1]),
)
filter = filters.CircuitFilter
filter_form = forms.CircuitFilterForm
@@ -140,17 +142,18 @@ class CircuitListView(ObjectListView):
template_name = 'circuits/circuit_list.html'
class CircuitView(View):
class CircuitView(PermissionRequiredMixin, View):
permission_required = 'circuits.view_circuit'
def get(self, request, pk):
circuit = get_object_or_404(Circuit.objects.select_related('provider', 'type', 'tenant__group'), pk=pk)
termination_a = CircuitTermination.objects.select_related(
circuit = get_object_or_404(Circuit.objects.prefetch_related('provider', 'type', 'tenant__group'), pk=pk)
termination_a = CircuitTermination.objects.prefetch_related(
'site__region', 'connected_endpoint__device'
).filter(
circuit=circuit, term_side=TERM_SIDE_A
).first()
termination_z = CircuitTermination.objects.select_related(
termination_z = CircuitTermination.objects.prefetch_related(
'site__region', 'connected_endpoint__device'
).filter(
circuit=circuit, term_side=TERM_SIDE_Z
@@ -190,7 +193,7 @@ class CircuitBulkImportView(PermissionRequiredMixin, BulkImportView):
class CircuitBulkEditView(PermissionRequiredMixin, BulkEditView):
permission_required = 'circuits.change_circuit'
queryset = Circuit.objects.select_related('provider', 'type', 'tenant').prefetch_related('terminations__site')
queryset = Circuit.objects.prefetch_related('provider', 'type', 'tenant').prefetch_related('terminations__site')
filter = filters.CircuitFilter
table = tables.CircuitTable
form = forms.CircuitBulkEditForm
@@ -199,7 +202,7 @@ class CircuitBulkEditView(PermissionRequiredMixin, BulkEditView):
class CircuitBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'circuits.delete_circuit'
queryset = Circuit.objects.select_related('provider', 'type', 'tenant').prefetch_related('terminations__site')
queryset = Circuit.objects.prefetch_related('provider', 'type', 'tenant').prefetch_related('terminations__site')
filter = filters.CircuitFilter
table = tables.CircuitTable
default_return_url = 'circuits:circuit_list'

View File

@@ -3,8 +3,8 @@ from rest_framework import serializers
from dcim.constants import CONNECTION_STATUS_CHOICES
from dcim.models import (
Cable, ConsolePort, ConsoleServerPort, Device, DeviceBay, DeviceType, DeviceRole, FrontPort, FrontPortTemplate,
Interface, Manufacturer, Platform, PowerOutlet, PowerPort, Rack, RackGroup, RackRole, RearPort, RearPortTemplate,
Region, Site, VirtualChassis,
Interface, Manufacturer, Platform, PowerFeed, PowerOutlet, PowerPanel, PowerPort, Rack, RackGroup, RackRole,
RearPort, RearPortTemplate, Region, Site, VirtualChassis,
)
from utilities.api import ChoiceField, WritableNestedSerializer
@@ -21,7 +21,9 @@ __all__ = [
'NestedInterfaceSerializer',
'NestedManufacturerSerializer',
'NestedPlatformSerializer',
'NestedPowerFeedSerializer',
'NestedPowerOutletSerializer',
'NestedPowerPanelSerializer',
'NestedPowerPortSerializer',
'NestedRackGroupSerializer',
'NestedRackRoleSerializer',
@@ -40,10 +42,11 @@ __all__ = [
class NestedRegionSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:region-detail')
site_count = serializers.IntegerField(read_only=True)
class Meta:
model = Region
fields = ['id', 'url', 'name', 'slug']
fields = ['id', 'url', 'name', 'slug', 'site_count']
class NestedSiteSerializer(WritableNestedSerializer):
@@ -60,26 +63,29 @@ class NestedSiteSerializer(WritableNestedSerializer):
class NestedRackGroupSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackgroup-detail')
rack_count = serializers.IntegerField(read_only=True)
class Meta:
model = RackGroup
fields = ['id', 'url', 'name', 'slug']
fields = ['id', 'url', 'name', 'slug', 'rack_count']
class NestedRackRoleSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackrole-detail')
rack_count = serializers.IntegerField(read_only=True)
class Meta:
model = RackRole
fields = ['id', 'url', 'name', 'slug']
fields = ['id', 'url', 'name', 'slug', 'rack_count']
class NestedRackSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rack-detail')
device_count = serializers.IntegerField(read_only=True)
class Meta:
model = Rack
fields = ['id', 'url', 'name', 'display_name']
fields = ['id', 'url', 'name', 'display_name', 'device_count']
#
@@ -88,19 +94,21 @@ class NestedRackSerializer(WritableNestedSerializer):
class NestedManufacturerSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:manufacturer-detail')
devicetype_count = serializers.IntegerField(read_only=True)
class Meta:
model = Manufacturer
fields = ['id', 'url', 'name', 'slug']
fields = ['id', 'url', 'name', 'slug', 'devicetype_count']
class NestedDeviceTypeSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicetype-detail')
manufacturer = NestedManufacturerSerializer(read_only=True)
device_count = serializers.IntegerField(read_only=True)
class Meta:
model = DeviceType
fields = ['id', 'url', 'manufacturer', 'model', 'slug', 'display_name']
fields = ['id', 'url', 'manufacturer', 'model', 'slug', 'display_name', 'device_count']
class NestedRearPortTemplateSerializer(WritableNestedSerializer):
@@ -125,18 +133,22 @@ class NestedFrontPortTemplateSerializer(WritableNestedSerializer):
class NestedDeviceRoleSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicerole-detail')
device_count = serializers.IntegerField(read_only=True)
virtualmachine_count = serializers.IntegerField(read_only=True)
class Meta:
model = DeviceRole
fields = ['id', 'url', 'name', 'slug']
fields = ['id', 'url', 'name', 'slug', 'device_count', 'virtualmachine_count']
class NestedPlatformSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:platform-detail')
device_count = serializers.IntegerField(read_only=True)
virtualmachine_count = serializers.IntegerField(read_only=True)
class Meta:
model = Platform
fields = ['id', 'url', 'name', 'slug']
fields = ['id', 'url', 'name', 'slug', 'device_count', 'virtualmachine_count']
class NestedDeviceSerializer(WritableNestedSerializer):
@@ -216,7 +228,7 @@ class NestedFrontPortSerializer(WritableNestedSerializer):
class NestedDeviceBaySerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rearport-detail')
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:devicebay-detail')
device = NestedDeviceSerializer(read_only=True)
class Meta:
@@ -243,7 +255,29 @@ class NestedCableSerializer(serializers.ModelSerializer):
class NestedVirtualChassisSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:virtualchassis-detail')
master = NestedDeviceSerializer()
member_count = serializers.IntegerField(read_only=True)
class Meta:
model = VirtualChassis
fields = ['id', 'url', 'master']
fields = ['id', 'url', 'master', 'member_count']
#
# Power panels/feeds
#
class NestedPowerPanelSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerpanel-detail')
powerfeed_count = serializers.IntegerField(read_only=True)
class Meta:
model = PowerPanel
fields = ['id', 'url', 'name', 'powerfeed_count']
class NestedPowerFeedSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerfeed-detail')
class Meta:
model = PowerFeed
fields = ['id', 'url', 'name']

View File

@@ -1,3 +1,5 @@
from django.contrib.contenttypes.models import ContentType
from drf_yasg.utils import swagger_serializer_method
from rest_framework import serializers
from rest_framework.validators import UniqueTogetherValidator
from taggit_serializer.serializers import TaggitSerializer, TagListSerializerField
@@ -6,8 +8,9 @@ from dcim.constants import *
from dcim.models import (
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
DeviceBayTemplate, DeviceType, DeviceRole, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
Manufacturer, InventoryItem, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack,
RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site, VirtualChassis,
Manufacturer, InventoryItem, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate, PowerPanel, PowerPort,
PowerPortTemplate, Rack, RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site,
VirtualChassis,
)
from extras.api.customfields import CustomFieldModelSerializer
from ipam.api.nested_serializers import NestedIPAddressSerializer, NestedVLANSerializer
@@ -35,6 +38,7 @@ class ConnectedEndpointSerializer(ValidatedModelSerializer):
)
return None
@swagger_serializer_method(serializer_or_field=serializers.DictField)
def get_connected_endpoint(self, obj):
"""
Return the appropriate serializer for the type of connected object.
@@ -55,10 +59,11 @@ class ConnectedEndpointSerializer(ValidatedModelSerializer):
class RegionSerializer(serializers.ModelSerializer):
parent = NestedRegionSerializer(required=False, allow_null=True)
site_count = serializers.IntegerField(read_only=True)
class Meta:
model = Region
fields = ['id', 'name', 'slug', 'parent']
fields = ['id', 'name', 'slug', 'parent', 'site_count']
class SiteSerializer(TaggitSerializer, CustomFieldModelSerializer):
@@ -67,19 +72,20 @@ class SiteSerializer(TaggitSerializer, CustomFieldModelSerializer):
tenant = NestedTenantSerializer(required=False, allow_null=True)
time_zone = TimeZoneField(required=False)
tags = TagListSerializerField(required=False)
count_prefixes = serializers.IntegerField(read_only=True)
count_vlans = serializers.IntegerField(read_only=True)
count_racks = serializers.IntegerField(read_only=True)
count_devices = serializers.IntegerField(read_only=True)
count_circuits = serializers.IntegerField(read_only=True)
circuit_count = serializers.IntegerField(read_only=True)
device_count = serializers.IntegerField(read_only=True)
prefix_count = serializers.IntegerField(read_only=True)
rack_count = serializers.IntegerField(read_only=True)
virtualmachine_count = serializers.IntegerField(read_only=True)
vlan_count = serializers.IntegerField(read_only=True)
class Meta:
model = Site
fields = [
'id', 'name', 'slug', 'status', 'region', 'tenant', 'facility', 'asn', 'time_zone', 'description',
'physical_address', 'shipping_address', 'latitude', 'longitude', 'contact_name', 'contact_phone',
'contact_email', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'count_prefixes',
'count_vlans', 'count_racks', 'count_devices', 'count_circuits',
'contact_email', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'circuit_count',
'device_count', 'prefix_count', 'rack_count', 'virtualmachine_count', 'vlan_count',
]
@@ -89,17 +95,19 @@ class SiteSerializer(TaggitSerializer, CustomFieldModelSerializer):
class RackGroupSerializer(ValidatedModelSerializer):
site = NestedSiteSerializer()
rack_count = serializers.IntegerField(read_only=True)
class Meta:
model = RackGroup
fields = ['id', 'name', 'slug', 'site']
fields = ['id', 'name', 'slug', 'site', 'rack_count']
class RackRoleSerializer(ValidatedModelSerializer):
rack_count = serializers.IntegerField(read_only=True)
class Meta:
model = RackRole
fields = ['id', 'name', 'slug', 'color']
fields = ['id', 'name', 'slug', 'color', 'rack_count']
class RackSerializer(TaggitSerializer, CustomFieldModelSerializer):
@@ -112,13 +120,15 @@ class RackSerializer(TaggitSerializer, CustomFieldModelSerializer):
width = ChoiceField(choices=RACK_WIDTH_CHOICES, required=False)
outer_unit = ChoiceField(choices=RACK_DIMENSION_UNIT_CHOICES, required=False)
tags = TagListSerializerField(required=False)
device_count = serializers.IntegerField(read_only=True)
powerfeed_count = serializers.IntegerField(read_only=True)
class Meta:
model = Rack
fields = [
'id', 'name', 'facility_id', 'display_name', 'site', 'group', 'tenant', 'status', 'role', 'serial',
'asset_tag', 'type', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit',
'comments', 'tags', 'custom_fields', 'created', 'last_updated',
'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'device_count', 'powerfeed_count',
]
# Omit the UniqueTogetherValidator that would be automatically added to validate (group, facility_id). This
# prevents facility_id from being interpreted as a required field.
@@ -165,23 +175,26 @@ class RackReservationSerializer(ValidatedModelSerializer):
#
class ManufacturerSerializer(ValidatedModelSerializer):
devicetype_count = serializers.IntegerField(read_only=True)
inventoryitem_count = serializers.IntegerField(read_only=True)
platform_count = serializers.IntegerField(read_only=True)
class Meta:
model = Manufacturer
fields = ['id', 'name', 'slug']
fields = ['id', 'name', 'slug', 'devicetype_count', 'inventoryitem_count', 'platform_count']
class DeviceTypeSerializer(TaggitSerializer, CustomFieldModelSerializer):
manufacturer = NestedManufacturerSerializer()
subdevice_role = ChoiceField(choices=SUBDEVICE_ROLE_CHOICES, required=False, allow_null=True)
instance_count = serializers.IntegerField(source='instances.count', read_only=True)
tags = TagListSerializerField(required=False)
device_count = serializers.IntegerField(read_only=True)
class Meta:
model = DeviceType
fields = [
'id', 'manufacturer', 'model', 'slug', 'display_name', 'part_number', 'u_height', 'is_full_depth',
'subdevice_role', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'instance_count',
'subdevice_role', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'device_count',
]
@@ -206,24 +219,34 @@ class PowerPortTemplateSerializer(ValidatedModelSerializer):
class Meta:
model = PowerPortTemplate
fields = ['id', 'device_type', 'name']
fields = ['id', 'device_type', 'name', 'maximum_draw', 'allocated_draw']
class PowerOutletTemplateSerializer(ValidatedModelSerializer):
device_type = NestedDeviceTypeSerializer()
power_port = PowerPortTemplateSerializer(
required=False
)
feed_leg = ChoiceField(
choices=POWERFEED_LEG_CHOICES,
required=False,
allow_null=True
)
class Meta:
model = PowerOutletTemplate
fields = ['id', 'device_type', 'name']
fields = ['id', 'device_type', 'name', 'power_port', 'feed_leg']
class InterfaceTemplateSerializer(ValidatedModelSerializer):
device_type = NestedDeviceTypeSerializer()
form_factor = ChoiceField(choices=IFACE_FF_CHOICES, required=False)
type = ChoiceField(choices=IFACE_TYPE_CHOICES, required=False)
# TODO: Remove in v2.7 (backward-compatibility for form_factor)
form_factor = ChoiceField(choices=IFACE_TYPE_CHOICES, required=False)
class Meta:
model = InterfaceTemplate
fields = ['id', 'device_type', 'name', 'form_factor', 'mgmt_only']
fields = ['id', 'device_type', 'name', 'type', 'form_factor', 'mgmt_only']
class RearPortTemplateSerializer(ValidatedModelSerializer):
@@ -258,18 +281,25 @@ class DeviceBayTemplateSerializer(ValidatedModelSerializer):
#
class DeviceRoleSerializer(ValidatedModelSerializer):
device_count = serializers.IntegerField(read_only=True)
virtualmachine_count = serializers.IntegerField(read_only=True)
class Meta:
model = DeviceRole
fields = ['id', 'name', 'slug', 'color', 'vm_role']
fields = ['id', 'name', 'slug', 'color', 'vm_role', 'device_count', 'virtualmachine_count']
class PlatformSerializer(ValidatedModelSerializer):
manufacturer = NestedManufacturerSerializer(required=False, allow_null=True)
device_count = serializers.IntegerField(read_only=True)
virtualmachine_count = serializers.IntegerField(read_only=True)
class Meta:
model = Platform
fields = ['id', 'name', 'slug', 'manufacturer', 'napalm_driver', 'napalm_args']
fields = [
'id', 'name', 'slug', 'manufacturer', 'napalm_driver', 'napalm_args', 'device_count',
'virtualmachine_count',
]
class DeviceSerializer(TaggitSerializer, CustomFieldModelSerializer):
@@ -312,6 +342,7 @@ class DeviceSerializer(TaggitSerializer, CustomFieldModelSerializer):
return data
@swagger_serializer_method(serializer_or_field=NestedDeviceSerializer)
def get_parent_device(self, obj):
try:
device_bay = obj.parent_bay
@@ -334,6 +365,7 @@ class DeviceWithConfigContextSerializer(DeviceSerializer):
'custom_fields', 'config_context', 'created', 'last_updated',
]
@swagger_serializer_method(serializer_or_field=serializers.DictField)
def get_config_context(self, obj):
return obj.get_config_context()
@@ -346,8 +378,8 @@ class ConsoleServerPortSerializer(TaggitSerializer, ConnectedEndpointSerializer)
class Meta:
model = ConsoleServerPort
fields = [
'id', 'device', 'name', 'connected_endpoint_type', 'connected_endpoint', 'connection_status', 'cable',
'tags',
'id', 'device', 'name', 'description', 'connected_endpoint_type', 'connected_endpoint', 'connection_status',
'cable', 'tags',
]
@@ -359,21 +391,33 @@ class ConsolePortSerializer(TaggitSerializer, ConnectedEndpointSerializer):
class Meta:
model = ConsolePort
fields = [
'id', 'device', 'name', 'connected_endpoint_type', 'connected_endpoint', 'connection_status', 'cable',
'tags',
'id', 'device', 'name', 'description', 'connected_endpoint_type', 'connected_endpoint', 'connection_status',
'cable', 'tags',
]
class PowerOutletSerializer(TaggitSerializer, ConnectedEndpointSerializer):
device = NestedDeviceSerializer()
cable = NestedCableSerializer(read_only=True)
tags = TagListSerializerField(required=False)
power_port = NestedPowerPortSerializer(
required=False
)
feed_leg = ChoiceField(
choices=POWERFEED_LEG_CHOICES,
required=False,
allow_null=True
)
cable = NestedCableSerializer(
read_only=True
)
tags = TagListSerializerField(
required=False
)
class Meta:
model = PowerOutlet
fields = [
'id', 'device', 'name', 'connected_endpoint_type', 'connected_endpoint', 'connection_status', 'cable',
'tags',
'id', 'device', 'name', 'power_port', 'feed_leg', 'description', 'connected_endpoint_type',
'connected_endpoint', 'connection_status', 'cable', 'tags',
]
@@ -385,14 +429,16 @@ class PowerPortSerializer(TaggitSerializer, ConnectedEndpointSerializer):
class Meta:
model = PowerPort
fields = [
'id', 'device', 'name', 'connected_endpoint_type', 'connected_endpoint', 'connection_status', 'cable',
'tags',
'id', 'device', 'name', 'maximum_draw', 'allocated_draw', 'description', 'connected_endpoint_type',
'connected_endpoint', 'connection_status', 'cable', 'tags',
]
class InterfaceSerializer(TaggitSerializer, ConnectedEndpointSerializer):
device = NestedDeviceSerializer()
form_factor = ChoiceField(choices=IFACE_FF_CHOICES, required=False)
type = ChoiceField(choices=IFACE_TYPE_CHOICES, required=False)
# TODO: Remove in v2.7 (backward-compatibility for form_factor)
form_factor = ChoiceField(choices=IFACE_TYPE_CHOICES, required=False)
lag = NestedInterfaceSerializer(required=False, allow_null=True)
mode = ChoiceField(choices=IFACE_MODE_CHOICES, required=False, allow_null=True)
untagged_vlan = NestedVLANSerializer(required=False, allow_null=True)
@@ -408,9 +454,9 @@ class InterfaceSerializer(TaggitSerializer, ConnectedEndpointSerializer):
class Meta:
model = Interface
fields = [
'id', 'device', 'name', 'form_factor', 'enabled', 'lag', 'mtu', 'mac_address', 'mgmt_only', 'description',
'connected_endpoint_type', 'connected_endpoint', 'connection_status', 'cable', 'mode', 'untagged_vlan',
'tagged_vlans', 'tags', 'count_ipaddresses',
'id', 'device', 'name', 'type', 'form_factor', 'enabled', 'lag', 'mtu', 'mac_address', 'mgmt_only',
'description', 'connected_endpoint_type', 'connected_endpoint', 'connection_status', 'cable', 'mode',
'untagged_vlan', 'tagged_vlans', 'tags', 'count_ipaddresses',
]
# TODO: This validation should be handled by Interface.clean()
@@ -434,7 +480,7 @@ class InterfaceSerializer(TaggitSerializer, ConnectedEndpointSerializer):
return super().validate(data)
class RearPortSerializer(ValidatedModelSerializer):
class RearPortSerializer(TaggitSerializer, ValidatedModelSerializer):
device = NestedDeviceSerializer()
type = ChoiceField(choices=PORT_TYPE_CHOICES)
cable = NestedCableSerializer(read_only=True)
@@ -456,7 +502,7 @@ class FrontPortRearPortSerializer(WritableNestedSerializer):
fields = ['id', 'url', 'name']
class FrontPortSerializer(ValidatedModelSerializer):
class FrontPortSerializer(TaggitSerializer, ValidatedModelSerializer):
device = NestedDeviceSerializer()
type = ChoiceField(choices=PORT_TYPE_CHOICES)
rear_port = FrontPortRearPortSerializer()
@@ -475,7 +521,7 @@ class DeviceBaySerializer(TaggitSerializer, ValidatedModelSerializer):
class Meta:
model = DeviceBay
fields = ['id', 'device', 'name', 'installed_device', 'tags']
fields = ['id', 'device', 'name', 'description', 'installed_device', 'tags']
#
@@ -502,12 +548,16 @@ class InventoryItemSerializer(TaggitSerializer, ValidatedModelSerializer):
#
class CableSerializer(ValidatedModelSerializer):
termination_a_type = ContentTypeField()
termination_b_type = ContentTypeField()
termination_a_type = ContentTypeField(
queryset=ContentType.objects.filter(model__in=CABLE_TERMINATION_TYPES)
)
termination_b_type = ContentTypeField(
queryset=ContentType.objects.filter(model__in=CABLE_TERMINATION_TYPES)
)
termination_a = serializers.SerializerMethodField(read_only=True)
termination_b = serializers.SerializerMethodField(read_only=True)
status = ChoiceField(choices=CONNECTION_STATUS_CHOICES, required=False)
length_unit = ChoiceField(choices=CABLE_LENGTH_UNIT_CHOICES, required=False)
length_unit = ChoiceField(choices=CABLE_LENGTH_UNIT_CHOICES, required=False, allow_null=True)
class Meta:
model = Cable
@@ -531,9 +581,11 @@ class CableSerializer(ValidatedModelSerializer):
return data
@swagger_serializer_method(serializer_or_field=serializers.DictField)
def get_termination_a(self, obj):
return self._get_termination(obj, 'a')
@swagger_serializer_method(serializer_or_field=serializers.DictField)
def get_termination_b(self, obj):
return self._get_termination(obj, 'b')
@@ -564,6 +616,7 @@ class InterfaceConnectionSerializer(ValidatedModelSerializer):
model = Interface
fields = ['interface_a', 'interface_b', 'connection_status']
@swagger_serializer_method(serializer_or_field=NestedInterfaceSerializer)
def get_interface_a(self, obj):
context = {'request': self.context['request']}
return NestedInterfaceSerializer(instance=obj, context=context).data
@@ -576,7 +629,61 @@ class InterfaceConnectionSerializer(ValidatedModelSerializer):
class VirtualChassisSerializer(TaggitSerializer, ValidatedModelSerializer):
master = NestedDeviceSerializer()
tags = TagListSerializerField(required=False)
member_count = serializers.IntegerField(read_only=True)
class Meta:
model = VirtualChassis
fields = ['id', 'master', 'domain', 'tags']
fields = ['id', 'master', 'domain', 'tags', 'member_count']
#
# Power panels
#
class PowerPanelSerializer(ValidatedModelSerializer):
site = NestedSiteSerializer()
rack_group = NestedRackGroupSerializer(
required=False,
allow_null=True,
default=None
)
powerfeed_count = serializers.IntegerField(read_only=True)
class Meta:
model = PowerPanel
fields = ['id', 'site', 'rack_group', 'name', 'powerfeed_count']
class PowerFeedSerializer(TaggitSerializer, CustomFieldModelSerializer):
power_panel = NestedPowerPanelSerializer()
rack = NestedRackSerializer(
required=False,
allow_null=True,
default=None
)
type = ChoiceField(
choices=POWERFEED_TYPE_CHOICES,
default=POWERFEED_TYPE_PRIMARY
)
status = ChoiceField(
choices=POWERFEED_STATUS_CHOICES,
default=POWERFEED_STATUS_ACTIVE
)
supply = ChoiceField(
choices=POWERFEED_SUPPLY_CHOICES,
default=POWERFEED_SUPPLY_AC
)
phase = ChoiceField(
choices=POWERFEED_PHASE_CHOICES,
default=POWERFEED_PHASE_SINGLE
)
tags = TagListSerializerField(
required=False
)
class Meta:
model = PowerFeed
fields = [
'id', 'power_panel', 'rack', 'name', 'status', 'type', 'supply', 'phase', 'voltage', 'amperage',
'max_utilization', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]

View File

@@ -68,6 +68,10 @@ router.register(r'cables', views.CableViewSet)
# Virtual chassis
router.register(r'virtual-chassis', views.VirtualChassisViewSet)
# Power
router.register(r'power-panels', views.PowerPanelViewSet)
router.register(r'power-feeds', views.PowerFeedViewSet)
# Miscellaneous
router.register(r'connected-device', views.ConnectedDeviceViewSet, basename='connected-device')

View File

@@ -1,7 +1,7 @@
from collections import OrderedDict
from django.conf import settings
from django.db.models import F, Q
from django.db.models import Count, F
from django.http import HttpResponseForbidden
from django.shortcuts import get_object_or_404
from drf_yasg import openapi
@@ -12,19 +12,25 @@ from rest_framework.mixins import ListModelMixin
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet, ViewSet
from circuits.models import Circuit
from dcim import filters
from dcim.models import (
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
Manufacturer, InventoryItem, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack,
RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site, VirtualChassis,
Manufacturer, InventoryItem, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate, PowerPanel, PowerPort,
PowerPortTemplate, Rack, RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site,
VirtualChassis,
)
from extras.api.serializers import RenderedGraphSerializer
from extras.api.views import CustomFieldModelViewSet
from extras.models import Graph, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE
from extras.constants import GRAPH_TYPE_DEVICE, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE
from extras.models import Graph
from ipam.models import Prefix, VLAN
from utilities.api import (
get_serializer_for_model, IsAuthenticatedOrLoginNotRequired, FieldChoicesViewSet, ModelViewSet, ServiceUnavailable,
)
from utilities.utils import get_subquery
from virtualization.models import VirtualMachine
from . import serializers
from .exceptions import MissingFilterException
@@ -35,14 +41,16 @@ from .exceptions import MissingFilterException
class DCIMFieldChoicesViewSet(FieldChoicesViewSet):
fields = (
(Cable, ['length_unit', 'status', 'type']),
(Cable, ['length_unit', 'status', 'termination_a_type', 'termination_b_type', 'type']),
(ConsolePort, ['connection_status']),
(Device, ['face', 'status']),
(DeviceType, ['subdevice_role']),
(FrontPort, ['type']),
(FrontPortTemplate, ['type']),
(Interface, ['form_factor', 'mode']),
(InterfaceTemplate, ['form_factor']),
(Interface, ['type', 'mode']),
(InterfaceTemplate, ['type']),
(PowerOutlet, ['feed_leg']),
(PowerOutletTemplate, ['feed_leg']),
(PowerPort, ['connection_status']),
(Rack, ['outer_unit', 'status', 'type', 'width']),
(RearPort, ['type']),
@@ -90,7 +98,9 @@ class CableTraceMixin(object):
#
class RegionViewSet(ModelViewSet):
queryset = Region.objects.all()
queryset = Region.objects.annotate(
site_count=Count('sites')
)
serializer_class = serializers.RegionSerializer
filterset_class = filters.RegionFilter
@@ -100,12 +110,21 @@ class RegionViewSet(ModelViewSet):
#
class SiteViewSet(CustomFieldModelViewSet):
queryset = Site.objects.select_related('region', 'tenant').prefetch_related('tags')
queryset = Site.objects.prefetch_related(
'region', 'tenant', 'tags'
).annotate(
device_count=get_subquery(Device, 'site'),
rack_count=get_subquery(Rack, 'site'),
prefix_count=get_subquery(Prefix, 'site'),
vlan_count=get_subquery(VLAN, 'site'),
circuit_count=get_subquery(Circuit, 'terminations__site'),
virtualmachine_count=get_subquery(VirtualMachine, 'cluster__site'),
)
serializer_class = serializers.SiteSerializer
filterset_class = filters.SiteFilter
@action(detail=True)
def graphs(self, request, pk=None):
def graphs(self, request, pk):
"""
A convenience method for rendering graphs for a particular site.
"""
@@ -120,7 +139,9 @@ class SiteViewSet(CustomFieldModelViewSet):
#
class RackGroupViewSet(ModelViewSet):
queryset = RackGroup.objects.select_related('site')
queryset = RackGroup.objects.prefetch_related('site').annotate(
rack_count=Count('racks')
)
serializer_class = serializers.RackGroupSerializer
filterset_class = filters.RackGroupFilter
@@ -130,7 +151,9 @@ class RackGroupViewSet(ModelViewSet):
#
class RackRoleViewSet(ModelViewSet):
queryset = RackRole.objects.all()
queryset = RackRole.objects.annotate(
rack_count=Count('racks')
)
serializer_class = serializers.RackRoleSerializer
filterset_class = filters.RackRoleFilter
@@ -140,7 +163,12 @@ class RackRoleViewSet(ModelViewSet):
#
class RackViewSet(CustomFieldModelViewSet):
queryset = Rack.objects.select_related('site', 'group__site', 'tenant').prefetch_related('tags')
queryset = Rack.objects.prefetch_related(
'site', 'group__site', 'role', 'tenant', 'tags'
).annotate(
device_count=get_subquery(Device, 'rack'),
powerfeed_count=get_subquery(PowerFeed, 'rack')
)
serializer_class = serializers.RackSerializer
filterset_class = filters.RackFilter
@@ -175,7 +203,7 @@ class RackViewSet(CustomFieldModelViewSet):
#
class RackReservationViewSet(ModelViewSet):
queryset = RackReservation.objects.select_related('rack', 'user', 'tenant')
queryset = RackReservation.objects.prefetch_related('rack', 'user', 'tenant')
serializer_class = serializers.RackReservationSerializer
filterset_class = filters.RackReservationFilter
@@ -189,7 +217,11 @@ class RackReservationViewSet(ModelViewSet):
#
class ManufacturerViewSet(ModelViewSet):
queryset = Manufacturer.objects.all()
queryset = Manufacturer.objects.annotate(
devicetype_count=get_subquery(DeviceType, 'manufacturer'),
inventoryitem_count=get_subquery(InventoryItem, 'manufacturer'),
platform_count=get_subquery(Platform, 'manufacturer')
)
serializer_class = serializers.ManufacturerSerializer
filterset_class = filters.ManufacturerFilter
@@ -199,7 +231,9 @@ class ManufacturerViewSet(ModelViewSet):
#
class DeviceTypeViewSet(CustomFieldModelViewSet):
queryset = DeviceType.objects.select_related('manufacturer').prefetch_related('tags')
queryset = DeviceType.objects.prefetch_related('manufacturer').prefetch_related('tags').annotate(
device_count=Count('instances')
)
serializer_class = serializers.DeviceTypeSerializer
filterset_class = filters.DeviceTypeFilter
@@ -209,49 +243,49 @@ class DeviceTypeViewSet(CustomFieldModelViewSet):
#
class ConsolePortTemplateViewSet(ModelViewSet):
queryset = ConsolePortTemplate.objects.select_related('device_type__manufacturer')
queryset = ConsolePortTemplate.objects.prefetch_related('device_type__manufacturer')
serializer_class = serializers.ConsolePortTemplateSerializer
filterset_class = filters.ConsolePortTemplateFilter
class ConsoleServerPortTemplateViewSet(ModelViewSet):
queryset = ConsoleServerPortTemplate.objects.select_related('device_type__manufacturer')
queryset = ConsoleServerPortTemplate.objects.prefetch_related('device_type__manufacturer')
serializer_class = serializers.ConsoleServerPortTemplateSerializer
filterset_class = filters.ConsoleServerPortTemplateFilter
class PowerPortTemplateViewSet(ModelViewSet):
queryset = PowerPortTemplate.objects.select_related('device_type__manufacturer')
queryset = PowerPortTemplate.objects.prefetch_related('device_type__manufacturer')
serializer_class = serializers.PowerPortTemplateSerializer
filterset_class = filters.PowerPortTemplateFilter
class PowerOutletTemplateViewSet(ModelViewSet):
queryset = PowerOutletTemplate.objects.select_related('device_type__manufacturer')
queryset = PowerOutletTemplate.objects.prefetch_related('device_type__manufacturer')
serializer_class = serializers.PowerOutletTemplateSerializer
filterset_class = filters.PowerOutletTemplateFilter
class InterfaceTemplateViewSet(ModelViewSet):
queryset = InterfaceTemplate.objects.select_related('device_type__manufacturer')
queryset = InterfaceTemplate.objects.prefetch_related('device_type__manufacturer')
serializer_class = serializers.InterfaceTemplateSerializer
filterset_class = filters.InterfaceTemplateFilter
class FrontPortTemplateViewSet(ModelViewSet):
queryset = FrontPortTemplate.objects.select_related('device_type__manufacturer')
queryset = FrontPortTemplate.objects.prefetch_related('device_type__manufacturer')
serializer_class = serializers.FrontPortTemplateSerializer
filterset_class = filters.FrontPortTemplateFilter
class RearPortTemplateViewSet(ModelViewSet):
queryset = RearPortTemplate.objects.select_related('device_type__manufacturer')
queryset = RearPortTemplate.objects.prefetch_related('device_type__manufacturer')
serializer_class = serializers.RearPortTemplateSerializer
filterset_class = filters.RearPortTemplateFilter
class DeviceBayTemplateViewSet(ModelViewSet):
queryset = DeviceBayTemplate.objects.select_related('device_type__manufacturer')
queryset = DeviceBayTemplate.objects.prefetch_related('device_type__manufacturer')
serializer_class = serializers.DeviceBayTemplateSerializer
filterset_class = filters.DeviceBayTemplateFilter
@@ -261,7 +295,10 @@ class DeviceBayTemplateViewSet(ModelViewSet):
#
class DeviceRoleViewSet(ModelViewSet):
queryset = DeviceRole.objects.all()
queryset = DeviceRole.objects.annotate(
device_count=get_subquery(Device, 'device_role'),
virtualmachine_count=get_subquery(VirtualMachine, 'role')
)
serializer_class = serializers.DeviceRoleSerializer
filterset_class = filters.DeviceRoleFilter
@@ -271,7 +308,10 @@ class DeviceRoleViewSet(ModelViewSet):
#
class PlatformViewSet(ModelViewSet):
queryset = Platform.objects.all()
queryset = Platform.objects.annotate(
device_count=get_subquery(Device, 'platform'),
virtualmachine_count=get_subquery(VirtualMachine, 'platform')
)
serializer_class = serializers.PlatformSerializer
filterset_class = filters.PlatformFilter
@@ -281,26 +321,42 @@ class PlatformViewSet(ModelViewSet):
#
class DeviceViewSet(CustomFieldModelViewSet):
queryset = Device.objects.select_related(
queryset = Device.objects.prefetch_related(
'device_type__manufacturer', 'device_role', 'tenant', 'platform', 'site', 'rack', 'parent_bay',
'virtual_chassis__master',
).prefetch_related(
'primary_ip4__nat_outside', 'primary_ip6__nat_outside', 'tags',
'virtual_chassis__master', 'primary_ip4__nat_outside', 'primary_ip6__nat_outside', 'tags',
)
filterset_class = filters.DeviceFilter
def get_serializer_class(self):
"""
Include rendered config context when retrieving a single Device.
Select the specific serializer based on the request context.
If the `brief` query param equates to True, return the NestedDeviceSerializer
If the `exclude` query param includes `config_context` as a value, return the DeviceSerializer
Else, return the DeviceWithConfigContextSerializer
"""
if self.action == 'retrieve':
return serializers.DeviceWithConfigContextSerializer
request = self.get_serializer_context()['request']
if request.query_params.get('brief', False):
return serializers.NestedDeviceSerializer
return serializers.DeviceSerializer
elif 'config_context' in request.query_params.get('exclude', []):
return serializers.DeviceSerializer
return serializers.DeviceWithConfigContextSerializer
@action(detail=True)
def graphs(self, request, pk):
"""
A convenience method for rendering graphs for a particular Device.
"""
device = get_object_or_404(Device, pk=pk)
queryset = Graph.objects.filter(type=GRAPH_TYPE_DEVICE)
serializer = RenderedGraphSerializer(queryset, many=True, context={'graphed_object': device})
return Response(serializer.data)
@action(detail=True, url_path='napalm')
def napalm(self, request, pk):
@@ -379,56 +435,42 @@ class DeviceViewSet(CustomFieldModelViewSet):
#
class ConsolePortViewSet(CableTraceMixin, ModelViewSet):
queryset = ConsolePort.objects.select_related(
'device', 'connected_endpoint__device', 'cable'
).prefetch_related(
'tags'
)
queryset = ConsolePort.objects.prefetch_related('device', 'connected_endpoint__device', 'cable', 'tags')
serializer_class = serializers.ConsolePortSerializer
filterset_class = filters.ConsolePortFilter
class ConsoleServerPortViewSet(CableTraceMixin, ModelViewSet):
queryset = ConsoleServerPort.objects.select_related(
'device', 'connected_endpoint__device', 'cable'
).prefetch_related(
'tags'
)
queryset = ConsoleServerPort.objects.prefetch_related('device', 'connected_endpoint__device', 'cable', 'tags')
serializer_class = serializers.ConsoleServerPortSerializer
filterset_class = filters.ConsoleServerPortFilter
class PowerPortViewSet(CableTraceMixin, ModelViewSet):
queryset = PowerPort.objects.select_related(
'device', 'connected_endpoint__device', 'cable'
).prefetch_related(
'tags'
queryset = PowerPort.objects.prefetch_related(
'device', '_connected_poweroutlet__device', '_connected_powerfeed', 'cable', 'tags'
)
serializer_class = serializers.PowerPortSerializer
filterset_class = filters.PowerPortFilter
class PowerOutletViewSet(CableTraceMixin, ModelViewSet):
queryset = PowerOutlet.objects.select_related(
'device', 'connected_endpoint__device', 'cable'
).prefetch_related(
'tags'
)
queryset = PowerOutlet.objects.prefetch_related('device', 'connected_endpoint__device', 'cable', 'tags')
serializer_class = serializers.PowerOutletSerializer
filterset_class = filters.PowerOutletFilter
class InterfaceViewSet(CableTraceMixin, ModelViewSet):
queryset = Interface.objects.select_related(
'device', '_connected_interface', '_connected_circuittermination', 'cable'
).prefetch_related(
'ip_addresses', 'tags'
queryset = Interface.objects.prefetch_related(
'device', '_connected_interface', '_connected_circuittermination', 'cable', 'ip_addresses', 'tags'
).filter(
device__isnull=False
)
serializer_class = serializers.InterfaceSerializer
filterset_class = filters.InterfaceFilter
@action(detail=True)
def graphs(self, request, pk=None):
def graphs(self, request, pk):
"""
A convenience method for rendering graphs for a particular interface.
"""
@@ -439,33 +481,25 @@ class InterfaceViewSet(CableTraceMixin, ModelViewSet):
class FrontPortViewSet(ModelViewSet):
queryset = FrontPort.objects.select_related(
'device__device_type__manufacturer', 'rear_port', 'cable'
).prefetch_related(
'tags'
)
queryset = FrontPort.objects.prefetch_related('device__device_type__manufacturer', 'rear_port', 'cable', 'tags')
serializer_class = serializers.FrontPortSerializer
filterset_class = filters.FrontPortFilter
class RearPortViewSet(ModelViewSet):
queryset = RearPort.objects.select_related(
'device__device_type__manufacturer', 'cable'
).prefetch_related(
'tags'
)
queryset = RearPort.objects.prefetch_related('device__device_type__manufacturer', 'cable', 'tags')
serializer_class = serializers.RearPortSerializer
filterset_class = filters.RearPortFilter
class DeviceBayViewSet(ModelViewSet):
queryset = DeviceBay.objects.select_related('installed_device').prefetch_related('tags')
queryset = DeviceBay.objects.prefetch_related('installed_device').prefetch_related('tags')
serializer_class = serializers.DeviceBaySerializer
filterset_class = filters.DeviceBayFilter
class InventoryItemViewSet(ModelViewSet):
queryset = InventoryItem.objects.select_related('device', 'manufacturer').prefetch_related('tags')
queryset = InventoryItem.objects.prefetch_related('device', 'manufacturer').prefetch_related('tags')
serializer_class = serializers.InventoryItemSerializer
filterset_class = filters.InventoryItemFilter
@@ -475,7 +509,7 @@ class InventoryItemViewSet(ModelViewSet):
#
class ConsoleConnectionViewSet(ListModelMixin, GenericViewSet):
queryset = ConsolePort.objects.select_related(
queryset = ConsolePort.objects.prefetch_related(
'device', 'connected_endpoint__device'
).filter(
connected_endpoint__isnull=False
@@ -485,22 +519,22 @@ class ConsoleConnectionViewSet(ListModelMixin, GenericViewSet):
class PowerConnectionViewSet(ListModelMixin, GenericViewSet):
queryset = PowerPort.objects.select_related(
queryset = PowerPort.objects.prefetch_related(
'device', 'connected_endpoint__device'
).filter(
connected_endpoint__isnull=False
_connected_poweroutlet__isnull=False
)
serializer_class = serializers.PowerPortSerializer
filterset_class = filters.PowerConnectionFilter
class InterfaceConnectionViewSet(ListModelMixin, GenericViewSet):
queryset = Interface.objects.select_related(
'device', '_connected_interface', '_connected_circuittermination'
queryset = Interface.objects.prefetch_related(
'device', '_connected_interface__device'
).filter(
# Avoid duplicate connections by only selecting the lower PK in a connected pair
Q(_connected_interface__isnull=False, pk__lt=F('_connected_interface')) |
Q(_connected_circuittermination__isnull=False)
_connected_interface__isnull=False,
pk__lt=F('_connected_interface')
)
serializer_class = serializers.InterfaceConnectionSerializer
filterset_class = filters.InterfaceConnectionFilter
@@ -523,8 +557,35 @@ class CableViewSet(ModelViewSet):
#
class VirtualChassisViewSet(ModelViewSet):
queryset = VirtualChassis.objects.prefetch_related('tags')
queryset = VirtualChassis.objects.prefetch_related('tags').annotate(
member_count=Count('members')
)
serializer_class = serializers.VirtualChassisSerializer
filterset_class = filters.VirtualChassisFilter
#
# Power panels
#
class PowerPanelViewSet(ModelViewSet):
queryset = PowerPanel.objects.prefetch_related(
'site', 'rack_group'
).annotate(
powerfeed_count=Count('powerfeeds')
)
serializer_class = serializers.PowerPanelSerializer
filterset_class = filters.PowerPanelFilter
#
# Power feeds
#
class PowerFeedViewSet(CustomFieldModelViewSet):
queryset = PowerFeed.objects.prefetch_related('power_panel', 'rack', 'tags')
serializer_class = serializers.PowerFeedSerializer
filterset_class = filters.PowerFeedFilter
#

View File

@@ -58,206 +58,230 @@ SUBDEVICE_ROLE_CHOICES = (
(SUBDEVICE_ROLE_CHILD, 'Child'),
)
# Interface ordering schemes (for device types)
IFACE_ORDERING_POSITION = 1
IFACE_ORDERING_NAME = 2
IFACE_ORDERING_CHOICES = [
[IFACE_ORDERING_POSITION, 'Slot/position'],
[IFACE_ORDERING_NAME, 'Name (alphabetically)']
]
# Interface form factors
# Interface types
# Virtual
IFACE_FF_VIRTUAL = 0
IFACE_FF_LAG = 200
IFACE_TYPE_VIRTUAL = 0
IFACE_TYPE_LAG = 200
# Ethernet
IFACE_FF_100ME_FIXED = 800
IFACE_FF_1GE_FIXED = 1000
IFACE_FF_1GE_GBIC = 1050
IFACE_FF_1GE_SFP = 1100
IFACE_FF_10GE_FIXED = 1150
IFACE_FF_10GE_CX4 = 1170
IFACE_FF_10GE_SFP_PLUS = 1200
IFACE_FF_10GE_XFP = 1300
IFACE_FF_10GE_XENPAK = 1310
IFACE_FF_10GE_X2 = 1320
IFACE_FF_25GE_SFP28 = 1350
IFACE_FF_40GE_QSFP_PLUS = 1400
IFACE_FF_100GE_CFP = 1500
IFACE_FF_100GE_CFP2 = 1510
IFACE_FF_100GE_CFP4 = 1520
IFACE_FF_100GE_CPAK = 1550
IFACE_FF_100GE_QSFP28 = 1600
IFACE_FF_200GE_CFP2 = 1650
IFACE_FF_200GE_QSFP56 = 1700
IFACE_FF_400GE_QSFP_DD = 1750
IFACE_TYPE_100ME_FIXED = 800
IFACE_TYPE_1GE_FIXED = 1000
IFACE_TYPE_1GE_GBIC = 1050
IFACE_TYPE_1GE_SFP = 1100
IFACE_TYPE_2GE_FIXED = 1120
IFACE_TYPE_5GE_FIXED = 1130
IFACE_TYPE_10GE_FIXED = 1150
IFACE_TYPE_10GE_CX4 = 1170
IFACE_TYPE_10GE_SFP_PLUS = 1200
IFACE_TYPE_10GE_XFP = 1300
IFACE_TYPE_10GE_XENPAK = 1310
IFACE_TYPE_10GE_X2 = 1320
IFACE_TYPE_25GE_SFP28 = 1350
IFACE_TYPE_40GE_QSFP_PLUS = 1400
IFACE_TYPE_50GE_QSFP28 = 1420
IFACE_TYPE_100GE_CFP = 1500
IFACE_TYPE_100GE_CFP2 = 1510
IFACE_TYPE_100GE_CFP4 = 1520
IFACE_TYPE_100GE_CPAK = 1550
IFACE_TYPE_100GE_QSFP28 = 1600
IFACE_TYPE_200GE_CFP2 = 1650
IFACE_TYPE_200GE_QSFP56 = 1700
IFACE_TYPE_400GE_QSFP_DD = 1750
IFACE_TYPE_400GE_OSFP = 1800
# Wireless
IFACE_FF_80211A = 2600
IFACE_FF_80211G = 2610
IFACE_FF_80211N = 2620
IFACE_FF_80211AC = 2630
IFACE_FF_80211AD = 2640
IFACE_TYPE_80211A = 2600
IFACE_TYPE_80211G = 2610
IFACE_TYPE_80211N = 2620
IFACE_TYPE_80211AC = 2630
IFACE_TYPE_80211AD = 2640
# Cellular
IFACE_FF_GSM = 2810
IFACE_FF_CDMA = 2820
IFACE_FF_LTE = 2830
IFACE_TYPE_GSM = 2810
IFACE_TYPE_CDMA = 2820
IFACE_TYPE_LTE = 2830
# SONET
IFACE_FF_SONET_OC3 = 6100
IFACE_FF_SONET_OC12 = 6200
IFACE_FF_SONET_OC48 = 6300
IFACE_FF_SONET_OC192 = 6400
IFACE_FF_SONET_OC768 = 6500
IFACE_FF_SONET_OC1920 = 6600
IFACE_FF_SONET_OC3840 = 6700
IFACE_TYPE_SONET_OC3 = 6100
IFACE_TYPE_SONET_OC12 = 6200
IFACE_TYPE_SONET_OC48 = 6300
IFACE_TYPE_SONET_OC192 = 6400
IFACE_TYPE_SONET_OC768 = 6500
IFACE_TYPE_SONET_OC1920 = 6600
IFACE_TYPE_SONET_OC3840 = 6700
# Fibrechannel
IFACE_FF_1GFC_SFP = 3010
IFACE_FF_2GFC_SFP = 3020
IFACE_FF_4GFC_SFP = 3040
IFACE_FF_8GFC_SFP_PLUS = 3080
IFACE_FF_16GFC_SFP_PLUS = 3160
IFACE_FF_32GFC_SFP28 = 3320
IFACE_FF_128GFC_QSFP28 = 3400
IFACE_TYPE_1GFC_SFP = 3010
IFACE_TYPE_2GFC_SFP = 3020
IFACE_TYPE_4GFC_SFP = 3040
IFACE_TYPE_8GFC_SFP_PLUS = 3080
IFACE_TYPE_16GFC_SFP_PLUS = 3160
IFACE_TYPE_32GFC_SFP28 = 3320
IFACE_TYPE_128GFC_QSFP28 = 3400
# InfiniBand
IFACE_FF_INFINIBAND_SDR = 7010
IFACE_FF_INFINIBAND_DDR = 7020
IFACE_FF_INFINIBAND_QDR = 7030
IFACE_FF_INFINIBAND_FDR10 = 7040
IFACE_FF_INFINIBAND_FDR = 7050
IFACE_FF_INFINIBAND_EDR = 7060
IFACE_FF_INFINIBAND_HDR = 7070
IFACE_FF_INFINIBAND_NDR = 7080
IFACE_FF_INFINIBAND_XDR = 7090
# Serial
IFACE_FF_T1 = 4000
IFACE_FF_E1 = 4010
IFACE_FF_T3 = 4040
IFACE_FF_E3 = 4050
IFACE_TYPE_T1 = 4000
IFACE_TYPE_E1 = 4010
IFACE_TYPE_T3 = 4040
IFACE_TYPE_E3 = 4050
# Stacking
IFACE_FF_STACKWISE = 5000
IFACE_FF_STACKWISE_PLUS = 5050
IFACE_FF_FLEXSTACK = 5100
IFACE_FF_FLEXSTACK_PLUS = 5150
IFACE_FF_JUNIPER_VCP = 5200
IFACE_FF_SUMMITSTACK = 5300
IFACE_FF_SUMMITSTACK128 = 5310
IFACE_FF_SUMMITSTACK256 = 5320
IFACE_FF_SUMMITSTACK512 = 5330
IFACE_TYPE_STACKWISE = 5000
IFACE_TYPE_STACKWISE_PLUS = 5050
IFACE_TYPE_FLEXSTACK = 5100
IFACE_TYPE_FLEXSTACK_PLUS = 5150
IFACE_TYPE_JUNIPER_VCP = 5200
IFACE_TYPE_SUMMITSTACK = 5300
IFACE_TYPE_SUMMITSTACK128 = 5310
IFACE_TYPE_SUMMITSTACK256 = 5320
IFACE_TYPE_SUMMITSTACK512 = 5330
# Other
IFACE_FF_OTHER = 32767
IFACE_TYPE_OTHER = 32767
IFACE_FF_CHOICES = [
IFACE_TYPE_CHOICES = [
[
'Virtual interfaces',
[
[IFACE_FF_VIRTUAL, 'Virtual'],
[IFACE_FF_LAG, 'Link Aggregation Group (LAG)'],
[IFACE_TYPE_VIRTUAL, 'Virtual'],
[IFACE_TYPE_LAG, 'Link Aggregation Group (LAG)'],
],
],
[
'Ethernet (fixed)',
[
[IFACE_FF_100ME_FIXED, '100BASE-TX (10/100ME)'],
[IFACE_FF_1GE_FIXED, '1000BASE-T (1GE)'],
[IFACE_FF_10GE_FIXED, '10GBASE-T (10GE)'],
[IFACE_FF_10GE_CX4, '10GBASE-CX4 (10GE)'],
[IFACE_TYPE_100ME_FIXED, '100BASE-TX (10/100ME)'],
[IFACE_TYPE_1GE_FIXED, '1000BASE-T (1GE)'],
[IFACE_TYPE_2GE_FIXED, '2.5GBASE-T (2.5GE)'],
[IFACE_TYPE_5GE_FIXED, '5GBASE-T (5GE)'],
[IFACE_TYPE_10GE_FIXED, '10GBASE-T (10GE)'],
[IFACE_TYPE_10GE_CX4, '10GBASE-CX4 (10GE)'],
]
],
[
'Ethernet (modular)',
[
[IFACE_FF_1GE_GBIC, 'GBIC (1GE)'],
[IFACE_FF_1GE_SFP, 'SFP (1GE)'],
[IFACE_FF_10GE_SFP_PLUS, 'SFP+ (10GE)'],
[IFACE_FF_10GE_XFP, 'XFP (10GE)'],
[IFACE_FF_10GE_XENPAK, 'XENPAK (10GE)'],
[IFACE_FF_10GE_X2, 'X2 (10GE)'],
[IFACE_FF_25GE_SFP28, 'SFP28 (25GE)'],
[IFACE_FF_40GE_QSFP_PLUS, 'QSFP+ (40GE)'],
[IFACE_FF_100GE_CFP, 'CFP (100GE)'],
[IFACE_FF_100GE_CFP2, 'CFP2 (100GE)'],
[IFACE_FF_200GE_CFP2, 'CFP2 (200GE)'],
[IFACE_FF_100GE_CFP4, 'CFP4 (100GE)'],
[IFACE_FF_100GE_CPAK, 'Cisco CPAK (100GE)'],
[IFACE_FF_100GE_QSFP28, 'QSFP28 (100GE)'],
[IFACE_FF_200GE_QSFP56, 'QSFP56 (200GE)'],
[IFACE_FF_400GE_QSFP_DD, 'QSFP-DD (400GE)'],
[IFACE_TYPE_1GE_GBIC, 'GBIC (1GE)'],
[IFACE_TYPE_1GE_SFP, 'SFP (1GE)'],
[IFACE_TYPE_10GE_SFP_PLUS, 'SFP+ (10GE)'],
[IFACE_TYPE_10GE_XFP, 'XFP (10GE)'],
[IFACE_TYPE_10GE_XENPAK, 'XENPAK (10GE)'],
[IFACE_TYPE_10GE_X2, 'X2 (10GE)'],
[IFACE_TYPE_25GE_SFP28, 'SFP28 (25GE)'],
[IFACE_TYPE_40GE_QSFP_PLUS, 'QSFP+ (40GE)'],
[IFACE_TYPE_50GE_QSFP28, 'QSFP28 (50GE)'],
[IFACE_TYPE_100GE_CFP, 'CFP (100GE)'],
[IFACE_TYPE_100GE_CFP2, 'CFP2 (100GE)'],
[IFACE_TYPE_200GE_CFP2, 'CFP2 (200GE)'],
[IFACE_TYPE_100GE_CFP4, 'CFP4 (100GE)'],
[IFACE_TYPE_100GE_CPAK, 'Cisco CPAK (100GE)'],
[IFACE_TYPE_100GE_QSFP28, 'QSFP28 (100GE)'],
[IFACE_TYPE_200GE_QSFP56, 'QSFP56 (200GE)'],
[IFACE_TYPE_400GE_QSFP_DD, 'QSFP-DD (400GE)'],
[IFACE_TYPE_400GE_OSFP, 'OSFP (400GE)'],
]
],
[
'Wireless',
[
[IFACE_FF_80211A, 'IEEE 802.11a'],
[IFACE_FF_80211G, 'IEEE 802.11b/g'],
[IFACE_FF_80211N, 'IEEE 802.11n'],
[IFACE_FF_80211AC, 'IEEE 802.11ac'],
[IFACE_FF_80211AD, 'IEEE 802.11ad'],
[IFACE_TYPE_80211A, 'IEEE 802.11a'],
[IFACE_TYPE_80211G, 'IEEE 802.11b/g'],
[IFACE_TYPE_80211N, 'IEEE 802.11n'],
[IFACE_TYPE_80211AC, 'IEEE 802.11ac'],
[IFACE_TYPE_80211AD, 'IEEE 802.11ad'],
]
],
[
'Cellular',
[
[IFACE_FF_GSM, 'GSM'],
[IFACE_FF_CDMA, 'CDMA'],
[IFACE_FF_LTE, 'LTE'],
[IFACE_TYPE_GSM, 'GSM'],
[IFACE_TYPE_CDMA, 'CDMA'],
[IFACE_TYPE_LTE, 'LTE'],
]
],
[
'SONET',
[
[IFACE_FF_SONET_OC3, 'OC-3/STM-1'],
[IFACE_FF_SONET_OC12, 'OC-12/STM-4'],
[IFACE_FF_SONET_OC48, 'OC-48/STM-16'],
[IFACE_FF_SONET_OC192, 'OC-192/STM-64'],
[IFACE_FF_SONET_OC768, 'OC-768/STM-256'],
[IFACE_FF_SONET_OC1920, 'OC-1920/STM-640'],
[IFACE_FF_SONET_OC3840, 'OC-3840/STM-1234'],
[IFACE_TYPE_SONET_OC3, 'OC-3/STM-1'],
[IFACE_TYPE_SONET_OC12, 'OC-12/STM-4'],
[IFACE_TYPE_SONET_OC48, 'OC-48/STM-16'],
[IFACE_TYPE_SONET_OC192, 'OC-192/STM-64'],
[IFACE_TYPE_SONET_OC768, 'OC-768/STM-256'],
[IFACE_TYPE_SONET_OC1920, 'OC-1920/STM-640'],
[IFACE_TYPE_SONET_OC3840, 'OC-3840/STM-1234'],
]
],
[
'FibreChannel',
[
[IFACE_FF_1GFC_SFP, 'SFP (1GFC)'],
[IFACE_FF_2GFC_SFP, 'SFP (2GFC)'],
[IFACE_FF_4GFC_SFP, 'SFP (4GFC)'],
[IFACE_FF_8GFC_SFP_PLUS, 'SFP+ (8GFC)'],
[IFACE_FF_16GFC_SFP_PLUS, 'SFP+ (16GFC)'],
[IFACE_FF_32GFC_SFP28, 'SFP28 (32GFC)'],
[IFACE_FF_128GFC_QSFP28, 'QSFP28 (128GFC)'],
[IFACE_TYPE_1GFC_SFP, 'SFP (1GFC)'],
[IFACE_TYPE_2GFC_SFP, 'SFP (2GFC)'],
[IFACE_TYPE_4GFC_SFP, 'SFP (4GFC)'],
[IFACE_TYPE_8GFC_SFP_PLUS, 'SFP+ (8GFC)'],
[IFACE_TYPE_16GFC_SFP_PLUS, 'SFP+ (16GFC)'],
[IFACE_TYPE_32GFC_SFP28, 'SFP28 (32GFC)'],
[IFACE_TYPE_128GFC_QSFP28, 'QSFP28 (128GFC)'],
]
],
[
'InfiniBand',
[
[IFACE_FF_INFINIBAND_SDR, 'SDR (2 Gbps)'],
[IFACE_FF_INFINIBAND_DDR, 'DDR (4 Gbps)'],
[IFACE_FF_INFINIBAND_QDR, 'QDR (8 Gbps)'],
[IFACE_FF_INFINIBAND_FDR10, 'FDR10 (10 Gbps)'],
[IFACE_FF_INFINIBAND_FDR, 'FDR (13.5 Gbps)'],
[IFACE_FF_INFINIBAND_EDR, 'EDR (25 Gbps)'],
[IFACE_FF_INFINIBAND_HDR, 'HDR (50 Gbps)'],
[IFACE_FF_INFINIBAND_NDR, 'NDR (100 Gbps)'],
[IFACE_FF_INFINIBAND_XDR, 'XDR (250 Gbps)'],
]
],
[
'Serial',
[
[IFACE_FF_T1, 'T1 (1.544 Mbps)'],
[IFACE_FF_E1, 'E1 (2.048 Mbps)'],
[IFACE_FF_T3, 'T3 (45 Mbps)'],
[IFACE_FF_E3, 'E3 (34 Mbps)'],
[IFACE_TYPE_T1, 'T1 (1.544 Mbps)'],
[IFACE_TYPE_E1, 'E1 (2.048 Mbps)'],
[IFACE_TYPE_T3, 'T3 (45 Mbps)'],
[IFACE_TYPE_E3, 'E3 (34 Mbps)'],
]
],
[
'Stacking',
[
[IFACE_FF_STACKWISE, 'Cisco StackWise'],
[IFACE_FF_STACKWISE_PLUS, 'Cisco StackWise Plus'],
[IFACE_FF_FLEXSTACK, 'Cisco FlexStack'],
[IFACE_FF_FLEXSTACK_PLUS, 'Cisco FlexStack Plus'],
[IFACE_FF_JUNIPER_VCP, 'Juniper VCP'],
[IFACE_FF_SUMMITSTACK, 'Extreme SummitStack'],
[IFACE_FF_SUMMITSTACK128, 'Extreme SummitStack-128'],
[IFACE_FF_SUMMITSTACK256, 'Extreme SummitStack-256'],
[IFACE_FF_SUMMITSTACK512, 'Extreme SummitStack-512'],
[IFACE_TYPE_STACKWISE, 'Cisco StackWise'],
[IFACE_TYPE_STACKWISE_PLUS, 'Cisco StackWise Plus'],
[IFACE_TYPE_FLEXSTACK, 'Cisco FlexStack'],
[IFACE_TYPE_FLEXSTACK_PLUS, 'Cisco FlexStack Plus'],
[IFACE_TYPE_JUNIPER_VCP, 'Juniper VCP'],
[IFACE_TYPE_SUMMITSTACK, 'Extreme SummitStack'],
[IFACE_TYPE_SUMMITSTACK128, 'Extreme SummitStack-128'],
[IFACE_TYPE_SUMMITSTACK256, 'Extreme SummitStack-256'],
[IFACE_TYPE_SUMMITSTACK512, 'Extreme SummitStack-512'],
]
],
[
'Other',
[
[IFACE_FF_OTHER, 'Other'],
[IFACE_TYPE_OTHER, 'Other'],
]
],
]
VIRTUAL_IFACE_TYPES = [
IFACE_FF_VIRTUAL,
IFACE_FF_LAG,
IFACE_TYPE_VIRTUAL,
IFACE_TYPE_LAG,
]
WIRELESS_IFACE_TYPES = [
IFACE_FF_80211A,
IFACE_FF_80211G,
IFACE_FF_80211N,
IFACE_FF_80211AC,
IFACE_FF_80211AD,
IFACE_TYPE_80211A,
IFACE_TYPE_80211G,
IFACE_TYPE_80211N,
IFACE_TYPE_80211AC,
IFACE_TYPE_80211AD,
]
NONCONNECTABLE_IFACE_TYPES = VIRTUAL_IFACE_TYPES + WIRELESS_IFACE_TYPES
@@ -274,6 +298,7 @@ IFACE_MODE_CHOICES = [
# Pass-through port types
PORT_TYPE_8P8C = 1000
PORT_TYPE_110_PUNCH = 1100
PORT_TYPE_BNC = 1200
PORT_TYPE_ST = 2000
PORT_TYPE_SC = 2100
PORT_TYPE_SC_APC = 2110
@@ -290,6 +315,7 @@ PORT_TYPE_CHOICES = [
[
[PORT_TYPE_8P8C, '8P8C'],
[PORT_TYPE_110_PUNCH, '110 Punch'],
[PORT_TYPE_BNC, 'BNC'],
],
],
[
@@ -316,6 +342,7 @@ DEVICE_STATUS_PLANNED = 2
DEVICE_STATUS_STAGED = 3
DEVICE_STATUS_FAILED = 4
DEVICE_STATUS_INVENTORY = 5
DEVICE_STATUS_DECOMMISSIONING = 6
DEVICE_STATUS_CHOICES = [
[DEVICE_STATUS_ACTIVE, 'Active'],
[DEVICE_STATUS_OFFLINE, 'Offline'],
@@ -323,6 +350,7 @@ DEVICE_STATUS_CHOICES = [
[DEVICE_STATUS_STAGED, 'Staged'],
[DEVICE_STATUS_FAILED, 'Failed'],
[DEVICE_STATUS_INVENTORY, 'Inventory'],
[DEVICE_STATUS_DECOMMISSIONING, 'Decommissioning'],
]
# Site statuses
@@ -343,6 +371,7 @@ STATUS_CLASSES = {
3: 'primary',
4: 'danger',
5: 'default',
6: 'warning',
}
# Console/power/interface connection statuses
@@ -355,7 +384,7 @@ CONNECTION_STATUS_CHOICES = [
# Cable endpoint types
CABLE_TERMINATION_TYPES = [
'consoleport', 'consoleserverport', 'interface', 'poweroutlet', 'powerport', 'frontport', 'rearport',
'consoleport', 'consoleserverport', 'interface', 'poweroutlet', 'powerport', 'frontport', 'rearport', 'circuittermination',
]
# Cable types
@@ -367,6 +396,7 @@ CABLE_TYPE_CAT6A = 1610
CABLE_TYPE_CAT7 = 1700
CABLE_TYPE_DAC_ACTIVE = 1800
CABLE_TYPE_DAC_PASSIVE = 1810
CABLE_TYPE_COAXIAL = 1900
CABLE_TYPE_MMF = 3000
CABLE_TYPE_MMF_OM1 = 3010
CABLE_TYPE_MMF_OM2 = 3020
@@ -388,6 +418,7 @@ CABLE_TYPE_CHOICES = (
(CABLE_TYPE_CAT7, 'CAT7'),
(CABLE_TYPE_DAC_ACTIVE, 'Direct Attach Copper (Active)'),
(CABLE_TYPE_DAC_PASSIVE, 'Direct Attach Copper (Passive)'),
(CABLE_TYPE_COAXIAL, 'Coaxial'),
),
),
(
@@ -420,7 +451,7 @@ CABLE_TERMINATION_TYPE_CHOICES = {
COMPATIBLE_TERMINATION_TYPES = {
'consoleport': ['consoleserverport', 'frontport', 'rearport'],
'consoleserverport': ['consoleport', 'frontport', 'rearport'],
'powerport': ['poweroutlet'],
'powerport': ['poweroutlet', 'powerfeed'],
'poweroutlet': ['powerport'],
'interface': ['interface', 'circuittermination', 'frontport', 'rearport'],
'frontport': ['consoleport', 'consoleserverport', 'interface', 'frontport', 'rearport', 'circuittermination'],
@@ -443,3 +474,41 @@ RACK_DIMENSION_UNIT_CHOICES = (
(LENGTH_UNIT_MILLIMETER, 'Millimeters'),
(LENGTH_UNIT_INCH, 'Inches'),
)
# Power feeds
POWERFEED_TYPE_PRIMARY = 1
POWERFEED_TYPE_REDUNDANT = 2
POWERFEED_TYPE_CHOICES = (
(POWERFEED_TYPE_PRIMARY, 'Primary'),
(POWERFEED_TYPE_REDUNDANT, 'Redundant'),
)
POWERFEED_SUPPLY_AC = 1
POWERFEED_SUPPLY_DC = 2
POWERFEED_SUPPLY_CHOICES = (
(POWERFEED_SUPPLY_AC, 'AC'),
(POWERFEED_SUPPLY_DC, 'DC'),
)
POWERFEED_PHASE_SINGLE = 1
POWERFEED_PHASE_3PHASE = 3
POWERFEED_PHASE_CHOICES = (
(POWERFEED_PHASE_SINGLE, 'Single phase'),
(POWERFEED_PHASE_3PHASE, 'Three-phase'),
)
POWERFEED_STATUS_OFFLINE = 0
POWERFEED_STATUS_ACTIVE = 1
POWERFEED_STATUS_PLANNED = 2
POWERFEED_STATUS_FAILED = 4
POWERFEED_STATUS_CHOICES = (
(POWERFEED_STATUS_ACTIVE, 'Active'),
(POWERFEED_STATUS_OFFLINE, 'Offline'),
(POWERFEED_STATUS_PLANNED, 'Planned'),
(POWERFEED_STATUS_FAILED, 'Failed'),
)
POWERFEED_LEG_A = 1
POWERFEED_LEG_B = 2
POWERFEED_LEG_C = 3
POWERFEED_LEG_CHOICES = (
(POWERFEED_LEG_A, 'A'),
(POWERFEED_LEG_B, 'B'),
(POWERFEED_LEG_C, 'C'),
)

View File

@@ -31,7 +31,7 @@ class MACAddressField(models.Field):
try:
return EUI(value, version=48, dialect=mac_unix_expanded_uppercase)
except AddrFormatError as e:
raise ValidationError(e)
raise ValidationError("Invalid MAC address format: {}".format(value))
def db_type(self, connection):
return 'macaddr'

View File

@@ -1,21 +1,23 @@
import django_filters
from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Q
from netaddr import EUI
from netaddr.core import AddrFormatError
from extras.filters import CustomFieldFilterSet
from extras.filters import CustomFieldFilterSet, LocalConfigContextFilter
from tenancy.filtersets import TenancyFilterSet
from tenancy.models import Tenant
from utilities.constants import COLOR_CHOICES
from utilities.filters import NameSlugSearchFilterSet, NullableCharFieldFilter, NumericInFilter, TagFilter
from utilities.filters import (
MultiValueMACAddressFilter, MultiValueNumberFilter, NameSlugSearchFilterSet, NumericInFilter, TagFilter,
TreeNodeMultipleChoiceFilter,
)
from virtualization.models import Cluster
from .constants import *
from .models import (
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
InventoryItem, Manufacturer, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack,
RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site, VirtualChassis,
InventoryItem, Manufacturer, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate, PowerPanel, PowerPort,
PowerPortTemplate, Rack, RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site,
VirtualChassis,
)
@@ -33,10 +35,10 @@ class RegionFilter(NameSlugSearchFilterSet):
class Meta:
model = Region
fields = ['name', 'slug']
fields = ['id', 'name', 'slug']
class SiteFilter(CustomFieldFilterSet, django_filters.FilterSet):
class SiteFilter(TenancyFilterSet, CustomFieldFilterSet):
id__in = NumericInFilter(
field_name='id',
lookup_expr='in'
@@ -49,31 +51,25 @@ class SiteFilter(CustomFieldFilterSet, django_filters.FilterSet):
choices=SITE_STATUS_CHOICES,
null_value=None
)
region_id = django_filters.NumberFilter(
method='filter_region',
field_name='pk',
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='region__in',
label='Region (ID)',
)
region = django_filters.CharFilter(
method='filter_region',
field_name='slug',
label='Region (slug)',
)
tenant_id = django_filters.ModelMultipleChoiceFilter(
queryset=Tenant.objects.all(),
label='Tenant (ID)',
)
tenant = django_filters.ModelMultipleChoiceFilter(
field_name='tenant__slug',
queryset=Tenant.objects.all(),
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='region__in',
to_field_name='slug',
label='Tenant (slug)',
label='Region (slug)',
)
tag = TagFilter()
class Meta:
model = Site
fields = ['q', 'name', 'slug', 'facility', 'asn', 'contact_name', 'contact_phone', 'contact_email']
fields = [
'id', 'name', 'slug', 'facility', 'asn', 'latitude', 'longitude', 'contact_name', 'contact_phone',
'contact_email',
]
def search(self, queryset, name, value):
if not value.strip():
@@ -95,16 +91,6 @@ class SiteFilter(CustomFieldFilterSet, django_filters.FilterSet):
pass
return queryset.filter(qs_filter)
def filter_region(self, queryset, name, value):
try:
region = Region.objects.get(**{name: value})
except ObjectDoesNotExist:
return queryset.none()
return queryset.filter(
Q(region=region) |
Q(region__in=region.get_descendants())
)
class RackGroupFilter(NameSlugSearchFilterSet):
site_id = django_filters.ModelMultipleChoiceFilter(
@@ -120,17 +106,17 @@ class RackGroupFilter(NameSlugSearchFilterSet):
class Meta:
model = RackGroup
fields = ['site_id', 'name', 'slug']
fields = ['id', 'name', 'slug']
class RackRoleFilter(NameSlugSearchFilterSet):
class Meta:
model = RackRole
fields = ['name', 'slug', 'color']
fields = ['id', 'name', 'slug', 'color']
class RackFilter(CustomFieldFilterSet, django_filters.FilterSet):
class RackFilter(TenancyFilterSet, CustomFieldFilterSet):
id__in = NumericInFilter(
field_name='id',
lookup_expr='in'
@@ -139,7 +125,6 @@ class RackFilter(CustomFieldFilterSet, django_filters.FilterSet):
method='search',
label='Search',
)
facility_id = NullableCharFieldFilter()
site_id = django_filters.ModelMultipleChoiceFilter(
queryset=Site.objects.all(),
label='Site (ID)',
@@ -160,16 +145,6 @@ class RackFilter(CustomFieldFilterSet, django_filters.FilterSet):
to_field_name='slug',
label='Group',
)
tenant_id = django_filters.ModelMultipleChoiceFilter(
queryset=Tenant.objects.all(),
label='Tenant (ID)',
)
tenant = django_filters.ModelMultipleChoiceFilter(
field_name='tenant__slug',
queryset=Tenant.objects.all(),
to_field_name='slug',
label='Tenant (slug)',
)
status = django_filters.MultipleChoiceFilter(
choices=RACK_STATUS_CHOICES,
null_value=None
@@ -184,14 +159,16 @@ class RackFilter(CustomFieldFilterSet, django_filters.FilterSet):
to_field_name='slug',
label='Role (slug)',
)
asset_tag = NullableCharFieldFilter()
serial = django_filters.CharFilter(
lookup_expr='iexact'
)
tag = TagFilter()
class Meta:
model = Rack
fields = [
'name', 'serial', 'asset_tag', 'type', 'width', 'u_height', 'desc_units', 'outer_width', 'outer_depth',
'outer_unit',
'id', 'name', 'facility_id', 'asset_tag', 'type', 'width', 'u_height', 'desc_units',
'outer_width', 'outer_depth', 'outer_unit',
]
def search(self, queryset, name, value):
@@ -206,7 +183,7 @@ class RackFilter(CustomFieldFilterSet, django_filters.FilterSet):
)
class RackReservationFilter(django_filters.FilterSet):
class RackReservationFilter(TenancyFilterSet):
id__in = NumericInFilter(
field_name='id',
lookup_expr='in'
@@ -241,16 +218,6 @@ class RackReservationFilter(django_filters.FilterSet):
to_field_name='slug',
label='Group',
)
tenant_id = django_filters.ModelMultipleChoiceFilter(
queryset=Tenant.objects.all(),
label='Tenant (ID)',
)
tenant = django_filters.ModelMultipleChoiceFilter(
field_name='tenant__slug',
queryset=Tenant.objects.all(),
to_field_name='slug',
label='Tenant (slug)',
)
user_id = django_filters.ModelMultipleChoiceFilter(
queryset=User.objects.all(),
label='User (ID)',
@@ -281,7 +248,7 @@ class ManufacturerFilter(NameSlugSearchFilterSet):
class Meta:
model = Manufacturer
fields = ['name', 'slug']
fields = ['id', 'name', 'slug']
class DeviceTypeFilter(CustomFieldFilterSet):
@@ -379,63 +346,63 @@ class ConsolePortTemplateFilter(DeviceTypeComponentFilterSet):
class Meta:
model = ConsolePortTemplate
fields = ['name']
fields = ['id', 'name']
class ConsoleServerPortTemplateFilter(DeviceTypeComponentFilterSet):
class Meta:
model = ConsoleServerPortTemplate
fields = ['name']
fields = ['id', 'name']
class PowerPortTemplateFilter(DeviceTypeComponentFilterSet):
class Meta:
model = PowerPortTemplate
fields = ['name']
fields = ['id', 'name', 'maximum_draw', 'allocated_draw']
class PowerOutletTemplateFilter(DeviceTypeComponentFilterSet):
class Meta:
model = PowerOutletTemplate
fields = ['name']
fields = ['id', 'name', 'feed_leg']
class InterfaceTemplateFilter(DeviceTypeComponentFilterSet):
class Meta:
model = InterfaceTemplate
fields = ['name', 'form_factor', 'mgmt_only']
fields = ['id', 'name', 'type', 'mgmt_only']
class FrontPortTemplateFilter(DeviceTypeComponentFilterSet):
class Meta:
model = FrontPortTemplate
fields = ['name', 'type']
fields = ['id', 'name', 'type']
class RearPortTemplateFilter(DeviceTypeComponentFilterSet):
class Meta:
model = RearPortTemplate
fields = ['name', 'type']
fields = ['id', 'name', 'type', 'positions']
class DeviceBayTemplateFilter(DeviceTypeComponentFilterSet):
class Meta:
model = DeviceBayTemplate
fields = ['name']
fields = ['id', 'name']
class DeviceRoleFilter(NameSlugSearchFilterSet):
class Meta:
model = DeviceRole
fields = ['name', 'slug', 'color', 'vm_role']
fields = ['id', 'name', 'slug', 'color', 'vm_role']
class PlatformFilter(NameSlugSearchFilterSet):
@@ -453,10 +420,10 @@ class PlatformFilter(NameSlugSearchFilterSet):
class Meta:
model = Platform
fields = ['name', 'slug']
fields = ['id', 'name', 'slug', 'napalm_driver']
class DeviceFilter(CustomFieldFilterSet):
class DeviceFilter(LocalConfigContextFilter, TenancyFilterSet, CustomFieldFilterSet):
id__in = NumericInFilter(
field_name='id',
lookup_expr='in'
@@ -491,16 +458,6 @@ class DeviceFilter(CustomFieldFilterSet):
to_field_name='slug',
label='Role (slug)',
)
tenant_id = django_filters.ModelMultipleChoiceFilter(
queryset=Tenant.objects.all(),
label='Tenant (ID)',
)
tenant = django_filters.ModelMultipleChoiceFilter(
field_name='tenant__slug',
queryset=Tenant.objects.all(),
to_field_name='slug',
label='Tenant (slug)',
)
platform_id = django_filters.ModelMultipleChoiceFilter(
queryset=Platform.objects.all(),
label='Platform (ID)',
@@ -511,16 +468,15 @@ class DeviceFilter(CustomFieldFilterSet):
to_field_name='slug',
label='Platform (slug)',
)
name = NullableCharFieldFilter()
asset_tag = NullableCharFieldFilter()
region_id = django_filters.NumberFilter(
method='filter_region',
field_name='pk',
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region__in',
label='Region (ID)',
)
region = django_filters.CharFilter(
method='filter_region',
field_name='slug',
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region__in',
to_field_name='slug',
label='Region (slug)',
)
site_id = django_filters.ModelMultipleChoiceFilter(
@@ -543,10 +499,6 @@ class DeviceFilter(CustomFieldFilterSet):
queryset=Rack.objects.all(),
label='Rack (ID)',
)
position = django_filters.ChoiceFilter(
choices=DEVICE_POSITION_CHOICES,
null_label='Non-racked'
)
cluster_id = django_filters.ModelMultipleChoiceFilter(
queryset=Cluster.objects.all(),
label='VM cluster (ID)',
@@ -565,10 +517,13 @@ class DeviceFilter(CustomFieldFilterSet):
field_name='device_type__is_full_depth',
label='Is full depth',
)
mac_address = django_filters.CharFilter(
method='_mac_address',
mac_address = MultiValueMACAddressFilter(
field_name='interfaces__mac_address',
label='MAC address',
)
serial = django_filters.CharFilter(
lookup_expr='iexact'
)
has_primary_ip = django_filters.BooleanFilter(
method='_has_primary_ip',
label='Has a primary IP',
@@ -578,6 +533,10 @@ class DeviceFilter(CustomFieldFilterSet):
queryset=VirtualChassis.objects.all(),
label='Virtual chassis (ID)',
)
virtual_chassis_member = django_filters.BooleanFilter(
method='_virtual_chassis_member',
label='Is a virtual chassis member'
)
console_ports = django_filters.BooleanFilter(
method='_console_ports',
label='Has console ports',
@@ -606,7 +565,7 @@ class DeviceFilter(CustomFieldFilterSet):
class Meta:
model = Device
fields = ['serial', 'face']
fields = ['id', 'name', 'asset_tag', 'face', 'position', 'vc_position', 'vc_priority']
def search(self, queryset, name, value):
if not value.strip():
@@ -619,26 +578,6 @@ class DeviceFilter(CustomFieldFilterSet):
Q(comments__icontains=value)
).distinct()
def filter_region(self, queryset, name, value):
try:
region = Region.objects.get(**{name: value})
except ObjectDoesNotExist:
return queryset.none()
return queryset.filter(
Q(site__region=region) |
Q(site__region__in=region.get_descendants())
)
def _mac_address(self, queryset, name, value):
value = value.strip()
if not value:
return queryset
try:
mac = EUI(value.strip())
return queryset.filter(interfaces__mac_address=mac).distinct()
except AddrFormatError:
return queryset.none()
def _has_primary_ip(self, queryset, name, value):
if value:
return queryset.filter(
@@ -651,6 +590,9 @@ class DeviceFilter(CustomFieldFilterSet):
Q(primary_ip6__isnull=False)
)
def _virtual_chassis_member(self, queryset, name, value):
return queryset.exclude(virtual_chassis__isnull=value)
def _console_ports(self, queryset, name, value):
return queryset.exclude(consoleports__isnull=value)
@@ -661,7 +603,7 @@ class DeviceFilter(CustomFieldFilterSet):
return queryset.exclude(powerports__isnull=value)
def _power_outlets(self, queryset, name, value):
return queryset.exclude(poweroutlets_isnull=value)
return queryset.exclude(poweroutlets__isnull=value)
def _interfaces(self, queryset, name, value):
return queryset.exclude(interfaces__isnull=value)
@@ -678,7 +620,7 @@ class DeviceComponentFilterSet(django_filters.FilterSet):
method='search',
label='Search',
)
device_id = django_filters.ModelChoiceFilter(
device_id = django_filters.ModelMultipleChoiceFilter(
queryset=Device.objects.all(),
label='Device (ID)',
)
@@ -693,7 +635,8 @@ class DeviceComponentFilterSet(django_filters.FilterSet):
if not value.strip():
return queryset
return queryset.filter(
Q(name__icontains=value)
Q(name__icontains=value) |
Q(description__icontains=value)
)
@@ -706,7 +649,7 @@ class ConsolePortFilter(DeviceComponentFilterSet):
class Meta:
model = ConsolePort
fields = ['name', 'connection_status']
fields = ['id', 'name', 'description', 'connection_status']
class ConsoleServerPortFilter(DeviceComponentFilterSet):
@@ -718,7 +661,7 @@ class ConsoleServerPortFilter(DeviceComponentFilterSet):
class Meta:
model = ConsoleServerPort
fields = ['name', 'connection_status']
fields = ['id', 'name', 'description', 'connection_status']
class PowerPortFilter(DeviceComponentFilterSet):
@@ -730,7 +673,7 @@ class PowerPortFilter(DeviceComponentFilterSet):
class Meta:
model = PowerPort
fields = ['name', 'connection_status']
fields = ['id', 'name', 'maximum_draw', 'allocated_draw', 'description', 'connection_status']
class PowerOutletFilter(DeviceComponentFilterSet):
@@ -742,7 +685,7 @@ class PowerOutletFilter(DeviceComponentFilterSet):
class Meta:
model = PowerOutlet
fields = ['name', 'connection_status']
fields = ['id', 'name', 'feed_leg', 'description', 'connection_status']
class InterfaceFilter(django_filters.FilterSet):
@@ -758,8 +701,8 @@ class InterfaceFilter(django_filters.FilterSet):
field_name='name',
label='Device',
)
device_id = django_filters.NumberFilter(
method='filter_device',
device_id = MultiValueNumberFilter(
method='filter_device_id',
field_name='pk',
label='Device (ID)',
)
@@ -768,19 +711,16 @@ class InterfaceFilter(django_filters.FilterSet):
lookup_expr='isnull',
exclude=True
)
type = django_filters.CharFilter(
method='filter_type',
label='Interface type',
kind = django_filters.CharFilter(
method='filter_kind',
label='Kind of interface',
)
lag_id = django_filters.ModelMultipleChoiceFilter(
field_name='lag',
queryset=Interface.objects.all(),
label='LAG interface (ID)',
)
mac_address = django_filters.CharFilter(
method='_mac_address',
label='MAC address',
)
mac_address = MultiValueMACAddressFilter()
tag = TagFilter()
vlan_id = django_filters.CharFilter(
method='filter_vlan_id',
@@ -790,20 +730,21 @@ class InterfaceFilter(django_filters.FilterSet):
method='filter_vlan',
label='Assigned VID'
)
form_factor = django_filters.MultipleChoiceFilter(
choices=IFACE_FF_CHOICES,
type = django_filters.MultipleChoiceFilter(
choices=IFACE_TYPE_CHOICES,
null_value=None
)
class Meta:
model = Interface
fields = ['name', 'connection_status', 'form_factor', 'enabled', 'mtu', 'mgmt_only']
fields = ['id', 'name', 'connection_status', 'type', 'enabled', 'mtu', 'mgmt_only', 'mode', 'description']
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(name__icontains=value)
Q(name__icontains=value) |
Q(description__icontains=value)
).distinct()
def filter_device(self, queryset, name, value):
@@ -814,6 +755,17 @@ class InterfaceFilter(django_filters.FilterSet):
except Device.DoesNotExist:
return queryset.none()
def filter_device_id(self, queryset, name, id_list):
# Include interfaces belonging to peer virtual chassis members
vc_interface_ids = []
try:
devices = Device.objects.filter(pk__in=id_list)
for device in devices:
vc_interface_ids += device.vc_interfaces.values_list('id', flat=True)
return queryset.filter(pk__in=vc_interface_ids)
except Device.DoesNotExist:
return queryset.none()
def filter_vlan_id(self, queryset, name, value):
value = value.strip()
if not value:
@@ -832,25 +784,14 @@ class InterfaceFilter(django_filters.FilterSet):
Q(tagged_vlans__vid=value)
)
def filter_type(self, queryset, name, value):
def filter_kind(self, queryset, name, value):
value = value.strip().lower()
return {
'physical': queryset.exclude(form_factor__in=NONCONNECTABLE_IFACE_TYPES),
'virtual': queryset.filter(form_factor__in=VIRTUAL_IFACE_TYPES),
'wireless': queryset.filter(form_factor__in=WIRELESS_IFACE_TYPES),
'lag': queryset.filter(form_factor=IFACE_FF_LAG),
'physical': queryset.exclude(type__in=NONCONNECTABLE_IFACE_TYPES),
'virtual': queryset.filter(type__in=VIRTUAL_IFACE_TYPES),
'wireless': queryset.filter(type__in=WIRELESS_IFACE_TYPES),
}.get(value, queryset.none())
def _mac_address(self, queryset, name, value):
value = value.strip()
if not value:
return queryset
try:
mac = EUI(value.strip())
return queryset.filter(mac_address=mac)
except AddrFormatError:
return queryset.none()
class FrontPortFilter(DeviceComponentFilterSet):
cabled = django_filters.BooleanFilter(
@@ -861,7 +802,7 @@ class FrontPortFilter(DeviceComponentFilterSet):
class Meta:
model = FrontPort
fields = ['name', 'type']
fields = ['id', 'name', 'type', 'description']
class RearPortFilter(DeviceComponentFilterSet):
@@ -873,14 +814,14 @@ class RearPortFilter(DeviceComponentFilterSet):
class Meta:
model = RearPort
fields = ['name', 'type']
fields = ['id', 'name', 'type', 'positions', 'description']
class DeviceBayFilter(DeviceComponentFilterSet):
class Meta:
model = DeviceBay
fields = ['name']
fields = ['id', 'name', 'description']
class InventoryItemFilter(DeviceComponentFilterSet):
@@ -911,11 +852,13 @@ class InventoryItemFilter(DeviceComponentFilterSet):
to_field_name='slug',
label='Manufacturer (slug)',
)
asset_tag = NullableCharFieldFilter()
serial = django_filters.CharFilter(
lookup_expr='iexact'
)
class Meta:
model = InventoryItem
fields = ['name', 'part_id', 'serial', 'asset_tag', 'discovered']
fields = ['id', 'name', 'part_id', 'asset_tag', 'discovered']
def search(self, queryset, name, value):
if not value.strip():
@@ -961,7 +904,7 @@ class VirtualChassisFilter(django_filters.FilterSet):
class Meta:
model = VirtualChassis
fields = ['domain']
fields = ['id', 'domain']
def search(self, queryset, name, value):
if not value.strip():
@@ -981,19 +924,52 @@ class CableFilter(django_filters.FilterSet):
type = django_filters.MultipleChoiceFilter(
choices=CABLE_TYPE_CHOICES
)
status = django_filters.MultipleChoiceFilter(
choices=CONNECTION_STATUS_CHOICES
)
color = django_filters.MultipleChoiceFilter(
choices=COLOR_CHOICES
)
device_id = MultiValueNumberFilter(
method='filter_device'
)
device = MultiValueNumberFilter(
method='filter_device',
field_name='device__name'
)
rack_id = MultiValueNumberFilter(
method='filter_device',
field_name='device__rack_id'
)
rack = MultiValueNumberFilter(
method='filter_device',
field_name='device__rack__name'
)
site_id = MultiValueNumberFilter(
method='filter_device',
field_name='device__site_id'
)
site = MultiValueNumberFilter(
method='filter_device',
field_name='device__site__slug'
)
class Meta:
model = Cable
fields = ['type', 'status', 'color', 'length', 'length_unit']
fields = ['id', 'label', 'length', 'length_unit']
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(label__icontains=value)
def filter_device(self, queryset, name, value):
queryset = queryset.filter(
Q(**{'_termination_a_{}__in'.format(name): value}) |
Q(**{'_termination_b_{}__in'.format(name): value})
)
return queryset
class ConsoleConnectionFilter(django_filters.FilterSet):
site = django_filters.CharFilter(
@@ -1040,14 +1016,14 @@ class PowerConnectionFilter(django_filters.FilterSet):
def filter_site(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(connected_endpoint__device__site__slug=value)
return queryset.filter(_connected_poweroutlet__device__site__slug=value)
def filter_device(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(device__name__icontains=value) |
Q(connected_endpoint__device__name__icontains=value)
Q(_connected_poweroutlet__device__name__icontains=value)
)
@@ -1080,3 +1056,86 @@ class InterfaceConnectionFilter(django_filters.FilterSet):
Q(device__name__icontains=value) |
Q(_connected_interface__device__name__icontains=value)
)
class PowerPanelFilter(django_filters.FilterSet):
id__in = NumericInFilter(
field_name='id',
lookup_expr='in'
)
q = django_filters.CharFilter(
method='search',
label='Search',
)
site_id = django_filters.ModelMultipleChoiceFilter(
queryset=Site.objects.all(),
label='Site (ID)',
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
label='Site name (slug)',
)
rack_group_id = django_filters.ModelMultipleChoiceFilter(
field_name='rack_group',
queryset=RackGroup.objects.all(),
label='Rack group (ID)',
)
class Meta:
model = PowerPanel
fields = ['name']
def search(self, queryset, name, value):
if not value.strip():
return queryset
qs_filter = (
Q(name__icontains=value)
)
return queryset.filter(qs_filter)
class PowerFeedFilter(CustomFieldFilterSet):
id__in = NumericInFilter(
field_name='id',
lookup_expr='in'
)
q = django_filters.CharFilter(
method='search',
label='Search',
)
site_id = django_filters.ModelMultipleChoiceFilter(
field_name='power_panel__site',
queryset=Site.objects.all(),
label='Site (ID)',
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='power_panel__site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
label='Site name (slug)',
)
power_panel_id = django_filters.ModelMultipleChoiceFilter(
queryset=PowerPanel.objects.all(),
label='Power panel (ID)',
)
rack_id = django_filters.ModelMultipleChoiceFilter(
field_name='rack',
queryset=Rack.objects.all(),
label='Rack (ID)',
)
tag = TagFilter()
class Meta:
model = PowerFeed
fields = ['name', 'status', 'type', 'supply', 'phase', 'voltage', 'amperage', 'max_utilization']
def search(self, queryset, name, value):
if not value.strip():
return queryset
qs_filter = (
Q(name__icontains=value) |
Q(comments__icontains=value)
)
return queryset.filter(qs_filter)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -14,22 +14,6 @@ CHANNEL_RE = r"COALESCE(CAST(SUBSTRING({} FROM '^.*:(\d{{1,9}})(\.\d{{1,9}})?$')
VC_RE = r"COALESCE(CAST(SUBSTRING({} FROM '^.*\.(\d{{1,9}})$') AS integer), 0)"
class DeviceComponentManager(Manager):
def get_queryset(self):
queryset = super().get_queryset()
table_name = self.model._meta.db_table
sql = r"CONCAT(REGEXP_REPLACE({}.name, '\d+$', ''), LPAD(SUBSTRING({}.name FROM '\d+$'), 8, '0'))"
# Pad any trailing digits to effect natural sorting
return queryset.extra(
select={
'name_padded': sql.format(table_name, table_name),
}
).order_by('name_padded')
class InterfaceQuerySet(QuerySet):
def connectable(self):
@@ -37,7 +21,7 @@ class InterfaceQuerySet(QuerySet):
Return only physical interfaces which are capable of being connected to other interfaces (i.e. not virtual or
wireless).
"""
return self.exclude(form_factor__in=NONCONNECTABLE_IFACE_TYPES)
return self.exclude(type__in=NONCONNECTABLE_IFACE_TYPES)
class InterfaceManager(Manager):
@@ -64,11 +48,15 @@ class InterfaceManager(Manager):
The original `name` field is considered in its entirety to serve as a fallback in the event interfaces do not
match any of the prescribed fields.
The `id` field is included to enforce deterministic ordering of interfaces in similar vein of other device
components.
"""
sql_col = '{}.name'.format(self.model._meta.db_table)
ordering = [
'_slot', '_subslot', '_position', '_subposition', '_type', '_id', '_channel', '_vc', 'name',
'_slot', '_subslot', '_position', '_subposition', '_type', '_id', '_channel', '_vc', 'name', 'pk'
]
fields = {

View File

@@ -174,8 +174,8 @@ class Migration(migrations.Migration):
('length', models.PositiveSmallIntegerField(blank=True, null=True)),
('length_unit', models.PositiveSmallIntegerField(blank=True, null=True)),
('_abs_length', models.DecimalField(blank=True, decimal_places=4, max_digits=10, null=True)),
('termination_a_type', models.ForeignKey(limit_choices_to={'model__in': ['consoleport', 'consoleserverport', 'interface', 'poweroutlet', 'powerport', 'frontport', 'rearport']}, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.ContentType')),
('termination_b_type', models.ForeignKey(limit_choices_to={'model__in': ['consoleport', 'consoleserverport', 'interface', 'poweroutlet', 'powerport', 'frontport', 'rearport']}, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.ContentType')),
('termination_a_type', models.ForeignKey(limit_choices_to={'model__in': ['consoleport', 'consoleserverport', 'interface', 'poweroutlet', 'powerport', 'frontport', 'rearport', 'circuittermination']}, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.ContentType')),
('termination_b_type', models.ForeignKey(limit_choices_to={'model__in': ['consoleport', 'consoleserverport', 'interface', 'poweroutlet', 'powerport', 'frontport', 'rearport', 'circuittermination']}, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.ContentType')),
],
),
migrations.AlterUniqueTogether(

View File

@@ -0,0 +1,85 @@
# Generated by Django 2.1.4 on 2019-02-20 06:56
from django.db import migrations
import taggit.managers
class Migration(migrations.Migration):
dependencies = [
('dcim', '0069_deprecate_nullablecharfield'),
('extras', '0019_tag_taggeditem'),
]
operations = [
migrations.AlterField(
model_name='consoleport',
name='tags',
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
),
migrations.AlterField(
model_name='consoleserverport',
name='tags',
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
),
migrations.AlterField(
model_name='device',
name='tags',
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
),
migrations.AlterField(
model_name='devicebay',
name='tags',
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
),
migrations.AlterField(
model_name='devicetype',
name='tags',
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
),
migrations.AlterField(
model_name='frontport',
name='tags',
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
),
migrations.AlterField(
model_name='interface',
name='tags',
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
),
migrations.AlterField(
model_name='inventoryitem',
name='tags',
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
),
migrations.AlterField(
model_name='poweroutlet',
name='tags',
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
),
migrations.AlterField(
model_name='powerport',
name='tags',
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
),
migrations.AlterField(
model_name='rack',
name='tags',
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
),
migrations.AlterField(
model_name='rearport',
name='tags',
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
),
migrations.AlterField(
model_name='site',
name='tags',
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
),
migrations.AlterField(
model_name='virtualchassis',
name='tags',
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
),
]

View File

@@ -0,0 +1,38 @@
# Generated by Django 2.1.7 on 2019-02-20 18:50
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dcim', '0070_custom_tag_models'),
]
operations = [
migrations.AddField(
model_name='consoleport',
name='description',
field=models.CharField(blank=True, max_length=100),
),
migrations.AddField(
model_name='consoleserverport',
name='description',
field=models.CharField(blank=True, max_length=100),
),
migrations.AddField(
model_name='devicebay',
name='description',
field=models.CharField(blank=True, max_length=100),
),
migrations.AddField(
model_name='poweroutlet',
name='description',
field=models.CharField(blank=True, max_length=100),
),
migrations.AddField(
model_name='powerport',
name='description',
field=models.CharField(blank=True, max_length=100),
),
]

View File

@@ -0,0 +1,134 @@
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
import taggit.managers
class Migration(migrations.Migration):
dependencies = [
('extras', '0021_add_color_comments_changelog_to_tag'),
('dcim', '0071_device_components_add_description'),
]
operations = [
migrations.CreateModel(
name='PowerFeed',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)),
('created', models.DateField(auto_now_add=True, null=True)),
('last_updated', models.DateTimeField(auto_now=True, null=True)),
('name', models.CharField(max_length=50)),
('status', models.PositiveSmallIntegerField(default=1)),
('type', models.PositiveSmallIntegerField(default=1)),
('supply', models.PositiveSmallIntegerField(default=1)),
('phase', models.PositiveSmallIntegerField(default=1)),
('voltage', models.PositiveSmallIntegerField(default=120, validators=[django.core.validators.MinValueValidator(1)])),
('amperage', models.PositiveSmallIntegerField(default=20, validators=[django.core.validators.MinValueValidator(1)])),
('max_utilization', models.PositiveSmallIntegerField(default=80, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(100)])),
('available_power', models.PositiveSmallIntegerField(default=0, editable=False)),
('comments', models.TextField(blank=True)),
('cable', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.Cable')),
],
options={
'ordering': ['power_panel', 'name'],
},
),
migrations.CreateModel(
name='PowerPanel',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)),
('created', models.DateField(auto_now_add=True, null=True)),
('last_updated', models.DateTimeField(auto_now=True, null=True)),
('name', models.CharField(max_length=50)),
('rack_group', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='dcim.RackGroup')),
('site', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='dcim.Site')),
],
options={
'ordering': ['site', 'name'],
},
),
migrations.AddField(
model_name='powerfeed',
name='power_panel',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='powerfeeds', to='dcim.PowerPanel'),
),
migrations.AddField(
model_name='powerfeed',
name='rack',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='dcim.Rack'),
),
migrations.AddField(
model_name='powerfeed',
name='tags',
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
),
migrations.AddField(
model_name='powerfeed',
name='connected_endpoint',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.PowerPort'),
),
migrations.AddField(
model_name='powerfeed',
name='connection_status',
field=models.NullBooleanField(),
),
migrations.RenameField(
model_name='powerport',
old_name='connected_endpoint',
new_name='_connected_poweroutlet',
),
migrations.AddField(
model_name='powerport',
name='_connected_powerfeed',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='dcim.PowerFeed'),
),
migrations.AddField(
model_name='powerport',
name='allocated_draw',
field=models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)]),
),
migrations.AddField(
model_name='powerport',
name='maximum_draw',
field=models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)]),
),
migrations.AddField(
model_name='powerporttemplate',
name='allocated_draw',
field=models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)]),
),
migrations.AddField(
model_name='powerporttemplate',
name='maximum_draw',
field=models.PositiveSmallIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(1)]),
),
migrations.AlterUniqueTogether(
name='powerpanel',
unique_together={('site', 'name')},
),
migrations.AlterUniqueTogether(
name='powerfeed',
unique_together={('power_panel', 'name')},
),
migrations.AddField(
model_name='poweroutlet',
name='feed_leg',
field=models.PositiveSmallIntegerField(blank=True, null=True),
),
migrations.AddField(
model_name='poweroutlet',
name='power_port',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='poweroutlets', to='dcim.PowerPort'),
),
migrations.AddField(
model_name='poweroutlettemplate',
name='feed_leg',
field=models.PositiveSmallIntegerField(blank=True, null=True),
),
migrations.AddField(
model_name='poweroutlettemplate',
name='power_port',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='poweroutlet_templates', to='dcim.PowerPortTemplate'),
),
]

View File

@@ -0,0 +1,23 @@
# Generated by Django 2.1.7 on 2019-04-12 17:27
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('dcim', '0072_powerfeeds'),
]
operations = [
migrations.RenameField(
model_name='interface',
old_name='form_factor',
new_name='type',
),
migrations.RenameField(
model_name='interfacetemplate',
old_name='form_factor',
new_name='type',
),
]

View File

@@ -0,0 +1,23 @@
# Generated by Django 2.2 on 2019-07-17 20:40
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dcim', '0073_interface_form_factor_to_type'),
]
operations = [
migrations.AlterField(
model_name='platform',
name='name',
field=models.CharField(max_length=100, unique=True),
),
migrations.AlterField(
model_name='platform',
name='slug',
field=models.SlugField(max_length=100, unique=True),
),
]

View File

@@ -0,0 +1,60 @@
import sys
from django.db import migrations, models
import django.db.models.deletion
def cache_cable_devices(apps, schema_editor):
Cable = apps.get_model('dcim', 'Cable')
if 'test' not in sys.argv:
print("\nUpdating cable device terminations...")
cable_count = Cable.objects.count()
# Cache A/B termination devices on all existing Cables. Note that the custom save() method on Cable is not
# available during a migration, so we replicate its logic here.
for i, cable in enumerate(Cable.objects.all(), start=1):
if not i % 1000 and 'test' not in sys.argv:
print("[{}/{}]".format(i, cable_count))
termination_a_model = apps.get_model(cable.termination_a_type.app_label, cable.termination_a_type.model)
termination_a_device = None
if hasattr(termination_a_model, 'device'):
termination_a = termination_a_model.objects.get(pk=cable.termination_a_id)
termination_a_device = termination_a.device
termination_b_model = apps.get_model(cable.termination_b_type.app_label, cable.termination_b_type.model)
termination_b_device = None
if hasattr(termination_b_model, 'device'):
termination_b = termination_b_model.objects.get(pk=cable.termination_b_id)
termination_b_device = termination_b.device
Cable.objects.filter(pk=cable.pk).update(
_termination_a_device=termination_a_device,
_termination_b_device=termination_b_device
)
class Migration(migrations.Migration):
dependencies = [
('dcim', '0074_increase_field_length_platform_name_slug'),
]
operations = [
migrations.AddField(
model_name='cable',
name='_termination_a_device',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='dcim.Device'),
),
migrations.AddField(
model_name='cable',
name='_termination_b_device',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='dcim.Device'),
),
migrations.RunPython(
code=cache_cable_devices,
reverse_code=migrations.RunPython.noop
),
]

File diff suppressed because it is too large Load Diff

View File

@@ -10,7 +10,11 @@ def assign_virtualchassis_master(instance, created, **kwargs):
When a VirtualChassis is created, automatically assign its master device to the VC.
"""
if created:
Device.objects.filter(pk=instance.master.pk).update(virtual_chassis=instance, vc_position=None)
devices = Device.objects.filter(pk=instance.master.pk)
for device in devices:
device.virtual_chassis = instance
device.vc_position = None
device.save()
@receiver(pre_delete, sender=VirtualChassis)
@@ -18,7 +22,11 @@ def clear_virtualchassis_members(instance, **kwargs):
"""
When a VirtualChassis is deleted, nullify the vc_position and vc_priority fields of its prior members.
"""
Device.objects.filter(virtual_chassis=instance.pk).update(vc_position=None, vc_priority=None)
devices = Device.objects.filter(virtual_chassis=instance.pk)
for device in devices:
device.vc_position = None
device.vc_priority = None
device.save()
@receiver(post_save, sender=Cable)

View File

@@ -6,8 +6,9 @@ from utilities.tables import BaseTable, BooleanColumn, ColorColumn, ToggleColumn
from .models import (
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
InventoryItem, Manufacturer, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack,
RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site, VirtualChassis,
InventoryItem, Manufacturer, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate, PowerPanel, PowerPort,
PowerPortTemplate, Rack, RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site,
VirtualChassis,
)
REGION_LINK = """
@@ -44,7 +45,7 @@ REGION_ACTIONS = """
<i class="fa fa-history"></i>
</a>
{% if perms.dcim.change_region %}
<a href="{% url 'dcim:region_edit' pk=record.pk %}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
<a href="{% url 'dcim:region_edit' pk=record.pk %}?return_url={{ request.path }}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
{% endif %}
"""
@@ -56,7 +57,7 @@ RACKGROUP_ACTIONS = """
<i class="fa fa-eye"></i>
</a>
{% if perms.dcim.change_rackgroup %}
<a href="{% url 'dcim:rackgroup_edit' pk=record.pk %}" class="btn btn-xs btn-warning" title="Edit">
<a href="{% url 'dcim:rackgroup_edit' pk=record.pk %}?return_url={{ request.path }}" class="btn btn-xs btn-warning" title="Edit">
<i class="glyphicon glyphicon-pencil"></i>
</a>
{% endif %}
@@ -67,13 +68,14 @@ RACKROLE_ACTIONS = """
<i class="fa fa-history"></i>
</a>
{% if perms.dcim.change_rackrole %}
<a href="{% url 'dcim:rackrole_edit' pk=record.pk %}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
<a href="{% url 'dcim:rackrole_edit' pk=record.pk %}?return_url={{ request.path }}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
{% endif %}
"""
RACK_ROLE = """
{% if record.role %}
<label class="label" style="background-color: #{{ record.role.color }}">{{ value }}</label>
{% load helpers %}
<label class="label" style="color: {{ record.role.color|fgcolor }}; background-color: #{{ record.role.color }}">{{ value }}</label>
{% else %}
&mdash;
{% endif %}
@@ -88,7 +90,7 @@ RACKRESERVATION_ACTIONS = """
<i class="fa fa-history"></i>
</a>
{% if perms.dcim.change_rackreservation %}
<a href="{% url 'dcim:rackreservation_edit' pk=record.pk %}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
<a href="{% url 'dcim:rackreservation_edit' pk=record.pk %}?return_url={{ request.path }}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
{% endif %}
"""
@@ -97,7 +99,7 @@ MANUFACTURER_ACTIONS = """
<i class="fa fa-history"></i>
</a>
{% if perms.dcim.change_manufacturer %}
<a href="{% url 'dcim:manufacturer_edit' slug=record.slug %}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
<a href="{% url 'dcim:manufacturer_edit' slug=record.slug %}?return_url={{ request.path }}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
{% endif %}
"""
@@ -106,7 +108,7 @@ DEVICEROLE_ACTIONS = """
<i class="fa fa-history"></i>
</a>
{% if perms.dcim.change_devicerole %}
<a href="{% url 'dcim:devicerole_edit' slug=record.slug %}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
<a href="{% url 'dcim:devicerole_edit' slug=record.slug %}?return_url={{ request.path }}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
{% endif %}
"""
@@ -131,7 +133,7 @@ PLATFORM_ACTIONS = """
<i class="fa fa-history"></i>
</a>
{% if perms.dcim.change_platform %}
<a href="{% url 'dcim:platform_edit' slug=record.slug %}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
<a href="{% url 'dcim:platform_edit' slug=record.slug %}?return_url={{ request.path }}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
{% endif %}
"""
@@ -144,6 +146,10 @@ STATUS_LABEL = """
<span class="label label-{{ record.get_status_class }}">{{ record.get_status_display }}</span>
"""
TYPE_LABEL = """
<span class="label label-{{ record.get_type_class }}">{{ record.get_type_display }}</span>
"""
DEVICE_PRIMARY_IP = """
{{ record.primary_ip6.address.ip|default:"" }}
{% if record.primary_ip6 and record.primary_ip4 %}<br />{% endif %}
@@ -168,7 +174,7 @@ VIRTUALCHASSIS_ACTIONS = """
<i class="fa fa-history"></i>
</a>
{% if perms.dcim.change_virtualchassis %}
<a href="{% url 'dcim:virtualchassis_edit' pk=record.pk %}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
<a href="{% url 'dcim:virtualchassis_edit' pk=record.pk %}?return_url={{ request.path }}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
{% endif %}
"""
@@ -184,6 +190,20 @@ CABLE_LENGTH = """
{% if record.length %}{{ record.length }} {{ record.get_length_unit_display }}{% else %}&mdash;{% endif %}
"""
POWERPANEL_POWERFEED_COUNT = """
<a href="{% url 'dcim:powerfeed_list' %}?power_panel_id={{ record.pk }}">{{ value }}</a>
"""
def get_component_template_actions(model_name):
return """
{{% if perms.dcim.change_{model_name} %}}
<a href="{{% url 'dcim:{model_name}_edit' pk=record.pk %}}?return_url={{{{ request.path }}}}" class="btn btn-xs btn-warning">
<i class="glyphicon glyphicon-pencil" aria-hidden="true"></i>
</a>
{{% endif %}}
""".format(model_name=model_name).strip()
#
# Regions
@@ -196,7 +216,7 @@ class RegionTable(BaseTable):
slug = tables.Column(verbose_name='Slug')
actions = tables.TemplateColumn(
template_code=REGION_ACTIONS,
attrs={'td': {'class': 'text-right'}},
attrs={'td': {'class': 'text-right noprint'}},
verbose_name=''
)
@@ -239,7 +259,7 @@ class RackGroupTable(BaseTable):
slug = tables.Column()
actions = tables.TemplateColumn(
template_code=RACKGROUP_ACTIONS,
attrs={'td': {'class': 'text-right'}},
attrs={'td': {'class': 'text-right noprint'}},
verbose_name=''
)
@@ -258,7 +278,7 @@ class RackRoleTable(BaseTable):
rack_count = tables.Column(verbose_name='Racks')
color = tables.TemplateColumn(COLOR_LABEL, verbose_name='Color')
slug = tables.Column(verbose_name='Slug')
actions = tables.TemplateColumn(template_code=RACKROLE_ACTIONS, attrs={'td': {'class': 'text-right'}},
actions = tables.TemplateColumn(template_code=RACKROLE_ACTIONS, attrs={'td': {'class': 'text-right noprint'}},
verbose_name='')
class Meta(BaseTable.Meta):
@@ -290,12 +310,21 @@ class RackDetailTable(RackTable):
template_code=RACK_DEVICE_COUNT,
verbose_name='Devices'
)
get_utilization = tables.TemplateColumn(UTILIZATION_GRAPH, orderable=False, verbose_name='Utilization')
get_utilization = tables.TemplateColumn(
template_code=UTILIZATION_GRAPH,
orderable=False,
verbose_name='Space'
)
get_power_utilization = tables.TemplateColumn(
template_code=UTILIZATION_GRAPH,
orderable=False,
verbose_name='Power'
)
class Meta(RackTable.Meta):
fields = (
'pk', 'name', 'site', 'group', 'status', 'facility_id', 'tenant', 'role', 'u_height', 'device_count',
'get_utilization',
'get_utilization', 'get_power_utilization',
)
@@ -305,16 +334,21 @@ class RackDetailTable(RackTable):
class RackReservationTable(BaseTable):
pk = ToggleColumn()
tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')])
site = tables.LinkColumn(
viewname='dcim:site',
accessor=Accessor('rack.site'),
args=[Accessor('rack.site.slug')],
)
tenant = tables.TemplateColumn(template_code=COL_TENANT)
rack = tables.LinkColumn('dcim:rack', args=[Accessor('rack.pk')])
unit_list = tables.Column(orderable=False, verbose_name='Units')
actions = tables.TemplateColumn(
template_code=RACKRESERVATION_ACTIONS, attrs={'td': {'class': 'text-right'}}, verbose_name=''
template_code=RACKRESERVATION_ACTIONS, attrs={'td': {'class': 'text-right noprint'}}, verbose_name=''
)
class Meta(BaseTable.Meta):
model = RackReservation
fields = ('pk', 'rack', 'unit_list', 'user', 'created', 'tenant', 'description', 'actions')
fields = ('pk', 'site', 'rack', 'unit_list', 'user', 'created', 'tenant', 'description', 'actions')
#
@@ -323,16 +357,26 @@ class RackReservationTable(BaseTable):
class ManufacturerTable(BaseTable):
pk = ToggleColumn()
name = tables.LinkColumn(verbose_name='Name')
devicetype_count = tables.Column(verbose_name='Device Types')
platform_count = tables.Column(verbose_name='Platforms')
slug = tables.Column(verbose_name='Slug')
actions = tables.TemplateColumn(template_code=MANUFACTURER_ACTIONS, attrs={'td': {'class': 'text-right'}},
verbose_name='')
name = tables.LinkColumn()
devicetype_count = tables.Column(
verbose_name='Device Types'
)
inventoryitem_count = tables.Column(
verbose_name='Inventory Items'
)
platform_count = tables.Column(
verbose_name='Platforms'
)
slug = tables.Column()
actions = tables.TemplateColumn(
template_code=MANUFACTURER_ACTIONS,
attrs={'td': {'class': 'text-right noprint'}},
verbose_name=''
)
class Meta(BaseTable.Meta):
model = Manufacturer
fields = ('pk', 'name', 'devicetype_count', 'platform_count', 'slug', 'actions')
fields = ('pk', 'name', 'devicetype_count', 'inventoryitem_count', 'platform_count', 'slug', 'actions')
#
@@ -370,74 +414,117 @@ class DeviceTypeTable(BaseTable):
class ConsolePortTemplateTable(BaseTable):
pk = ToggleColumn()
actions = tables.TemplateColumn(
template_code=get_component_template_actions('consoleporttemplate'),
attrs={'td': {'class': 'text-right noprint'}},
verbose_name=''
)
class Meta(BaseTable.Meta):
model = ConsolePortTemplate
fields = ('pk', 'name')
fields = ('pk', 'name', 'actions')
empty_text = "None"
class ConsoleServerPortTemplateTable(BaseTable):
pk = ToggleColumn()
actions = tables.TemplateColumn(
template_code=get_component_template_actions('consoleserverporttemplate'),
attrs={'td': {'class': 'text-right noprint'}},
verbose_name=''
)
class Meta(BaseTable.Meta):
model = ConsoleServerPortTemplate
fields = ('pk', 'name')
fields = ('pk', 'name', 'actions')
empty_text = "None"
class PowerPortTemplateTable(BaseTable):
pk = ToggleColumn()
actions = tables.TemplateColumn(
template_code=get_component_template_actions('powerporttemplate'),
attrs={'td': {'class': 'text-right noprint'}},
verbose_name=''
)
class Meta(BaseTable.Meta):
model = PowerPortTemplate
fields = ('pk', 'name')
fields = ('pk', 'name', 'maximum_draw', 'allocated_draw', 'actions')
empty_text = "None"
class PowerOutletTemplateTable(BaseTable):
pk = ToggleColumn()
actions = tables.TemplateColumn(
template_code=get_component_template_actions('poweroutlettemplate'),
attrs={'td': {'class': 'text-right noprint'}},
verbose_name=''
)
class Meta(BaseTable.Meta):
model = PowerOutletTemplate
fields = ('pk', 'name')
fields = ('pk', 'name', 'power_port', 'feed_leg', 'actions')
empty_text = "None"
class InterfaceTemplateTable(BaseTable):
pk = ToggleColumn()
mgmt_only = tables.TemplateColumn("{% if value %}OOB Management{% endif %}")
actions = tables.TemplateColumn(
template_code=get_component_template_actions('interfacetemplate'),
attrs={'td': {'class': 'text-right noprint'}},
verbose_name=''
)
class Meta(BaseTable.Meta):
model = InterfaceTemplate
fields = ('pk', 'name', 'mgmt_only', 'form_factor')
fields = ('pk', 'name', 'mgmt_only', 'type', 'actions')
empty_text = "None"
class FrontPortTemplateTable(BaseTable):
pk = ToggleColumn()
rear_port_position = tables.Column(
verbose_name='Position'
)
actions = tables.TemplateColumn(
template_code=get_component_template_actions('frontporttemplate'),
attrs={'td': {'class': 'text-right noprint'}},
verbose_name=''
)
class Meta(BaseTable.Meta):
model = FrontPortTemplate
fields = ('pk', 'name', 'type', 'rear_port', 'rear_port_position')
fields = ('pk', 'name', 'type', 'rear_port', 'rear_port_position', 'actions')
empty_text = "None"
class RearPortTemplateTable(BaseTable):
pk = ToggleColumn()
actions = tables.TemplateColumn(
template_code=get_component_template_actions('rearporttemplate'),
attrs={'td': {'class': 'text-right noprint'}},
verbose_name=''
)
class Meta(BaseTable.Meta):
model = RearPortTemplate
fields = ('pk', 'name', 'type', 'positions')
fields = ('pk', 'name', 'type', 'positions', 'actions')
empty_text = "None"
class DeviceBayTemplateTable(BaseTable):
pk = ToggleColumn()
actions = tables.TemplateColumn(
template_code=get_component_template_actions('devicebaytemplate'),
attrs={'td': {'class': 'text-right noprint'}},
verbose_name=''
)
class Meta(BaseTable.Meta):
model = DeviceBayTemplate
fields = ('pk', 'name')
fields = ('pk', 'name', 'actions')
empty_text = "None"
@@ -463,7 +550,7 @@ class DeviceRoleTable(BaseTable):
slug = tables.Column(verbose_name='Slug')
actions = tables.TemplateColumn(
template_code=DEVICEROLE_ACTIONS,
attrs={'td': {'class': 'text-right'}},
attrs={'td': {'class': 'text-right noprint'}},
verbose_name=''
)
@@ -492,7 +579,7 @@ class PlatformTable(BaseTable):
)
actions = tables.TemplateColumn(
template_code=PLATFORM_ACTIONS,
attrs={'td': {'class': 'text-right'}},
attrs={'td': {'class': 'text-right noprint'}},
verbose_name=''
)
@@ -567,7 +654,7 @@ class ConsoleServerPortTable(BaseTable):
class Meta(BaseTable.Meta):
model = ConsoleServerPort
fields = ('name',)
fields = ('name', 'description')
class PowerPortTable(BaseTable):
@@ -581,14 +668,14 @@ class PowerOutletTable(BaseTable):
class Meta(BaseTable.Meta):
model = PowerOutlet
fields = ('name',)
fields = ('name', 'description')
class InterfaceTable(BaseTable):
class Meta(BaseTable.Meta):
model = Interface
fields = ('name', 'form_factor', 'lag', 'enabled', 'mgmt_only', 'description')
fields = ('name', 'type', 'lag', 'enabled', 'mgmt_only', 'description')
class FrontPortTable(BaseTable):
@@ -696,9 +783,11 @@ class PowerConnectionTable(BaseTable):
viewname='dcim:device',
accessor=Accessor('connected_endpoint.device'),
args=[Accessor('connected_endpoint.device.pk')],
order_by='_connected_poweroutlet__device',
verbose_name='PDU'
)
connected_endpoint = tables.Column(
outlet = tables.Column(
accessor=Accessor('_connected_poweroutlet'),
verbose_name='Outlet'
)
device = tables.LinkColumn(
@@ -711,7 +800,7 @@ class PowerConnectionTable(BaseTable):
class Meta(BaseTable.Meta):
model = PowerPort
fields = ('pdu', 'connected_endpoint', 'device', 'name', 'connection_status')
fields = ('pdu', 'outlet', 'device', 'name', 'connection_status')
class InterfaceConnectionTable(BaseTable):
@@ -733,18 +822,18 @@ class InterfaceConnectionTable(BaseTable):
)
device_b = tables.LinkColumn(
viewname='dcim:device',
accessor=Accessor('connected_endpoint.device'),
args=[Accessor('connected_endpoint.device.pk')],
accessor=Accessor('_connected_interface.device'),
args=[Accessor('_connected_interface.device.pk')],
verbose_name='Device B'
)
interface_b = tables.LinkColumn(
viewname='dcim:interface',
accessor=Accessor('connected_endpoint.name'),
args=[Accessor('connected_endpoint.pk')],
accessor=Accessor('_connected_interface'),
args=[Accessor('_connected_interface.pk')],
verbose_name='Interface B'
)
description_b = tables.Column(
accessor=Accessor('connected_endpoint.description'),
accessor=Accessor('_connected_interface.description'),
verbose_name='Description'
)
@@ -779,10 +868,58 @@ class VirtualChassisTable(BaseTable):
member_count = tables.Column(verbose_name='Members')
actions = tables.TemplateColumn(
template_code=VIRTUALCHASSIS_ACTIONS,
attrs={'td': {'class': 'text-right'}},
attrs={'td': {'class': 'text-right noprint'}},
verbose_name=''
)
class Meta(BaseTable.Meta):
model = VirtualChassis
fields = ('pk', 'master', 'domain', 'member_count', 'actions')
#
# Power panels
#
class PowerPanelTable(BaseTable):
pk = ToggleColumn()
name = tables.LinkColumn()
site = tables.LinkColumn(
viewname='dcim:site',
args=[Accessor('site.slug')]
)
powerfeed_count = tables.TemplateColumn(
template_code=POWERPANEL_POWERFEED_COUNT,
verbose_name='Feeds'
)
class Meta(BaseTable.Meta):
model = PowerPanel
fields = ('pk', 'name', 'site', 'rack_group', 'powerfeed_count')
#
# Power feeds
#
class PowerFeedTable(BaseTable):
pk = ToggleColumn()
name = tables.LinkColumn()
power_panel = tables.LinkColumn(
viewname='dcim:powerpanel',
args=[Accessor('power_panel.pk')],
)
rack = tables.LinkColumn(
viewname='dcim:rack',
args=[Accessor('rack.pk')]
)
status = tables.TemplateColumn(
template_code=STATUS_LABEL
)
type = tables.TemplateColumn(
template_code=TYPE_LABEL
)
class Meta(BaseTable.Meta):
model = PowerFeed
fields = ('pk', 'name', 'power_panel', 'rack', 'status', 'type', 'supply', 'voltage', 'amperage', 'phase')

View File

@@ -7,8 +7,8 @@ from dcim.constants import *
from dcim.models import (
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, Interface, InterfaceTemplate, Manufacturer,
InventoryItem, Platform, PowerPort, PowerPortTemplate, PowerOutlet, PowerOutletTemplate, Rack, RackGroup,
RackReservation, RackRole, RearPort, Region, Site, VirtualChassis,
InventoryItem, Platform, PowerFeed, PowerPort, PowerPortTemplate, PowerOutlet, PowerOutletTemplate, PowerPanel,
Rack, RackGroup, RackReservation, RackRole, RearPort, Region, Site, VirtualChassis,
)
from ipam.models import IPAddress, VLAN
from extras.models import Graph, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE
@@ -47,7 +47,7 @@ class RegionTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
['id', 'name', 'site_count', 'slug', 'url']
)
def test_create_region(self):
@@ -285,7 +285,7 @@ class RackGroupTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
['id', 'name', 'rack_count', 'slug', 'url']
)
def test_create_rackgroup(self):
@@ -393,7 +393,7 @@ class RackRoleTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
['id', 'name', 'rack_count', 'slug', 'url']
)
def test_create_rackrole(self):
@@ -520,7 +520,7 @@ class RackTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['display_name', 'id', 'name', 'url']
['device_count', 'display_name', 'id', 'name', 'url']
)
def test_create_rack(self):
@@ -746,7 +746,7 @@ class ManufacturerTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
['devicetype_count', 'id', 'name', 'slug', 'url']
)
def test_create_manufacturer(self):
@@ -855,7 +855,7 @@ class DeviceTypeTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['display_name', 'id', 'manufacturer', 'model', 'slug', 'url']
['device_count', 'display_name', 'id', 'manufacturer', 'model', 'slug', 'url']
)
def test_create_devicetype(self):
@@ -1569,7 +1569,7 @@ class DeviceRoleTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
['device_count', 'id', 'name', 'slug', 'url', 'virtualmachine_count']
)
def test_create_devicerole(self):
@@ -1677,7 +1677,7 @@ class PlatformTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'slug', 'url']
['device_count', 'id', 'name', 'slug', 'url', 'virtualmachine_count']
)
def test_create_platform(self):
@@ -1791,6 +1791,16 @@ class DeviceTest(APITestCase):
site=self.site1,
cluster=self.cluster1
)
self.device_with_context_data = Device.objects.create(
device_type=self.devicetype1,
device_role=self.devicerole1,
name='Device with context data',
site=self.site1,
local_context_data={
'A': 1,
'B': 2
}
)
def test_get_device(self):
@@ -1806,7 +1816,7 @@ class DeviceTest(APITestCase):
url = reverse('dcim-api:device-list')
response = self.client.get(url, **self.header)
self.assertEqual(response.data['count'], 3)
self.assertEqual(response.data['count'], 4)
def test_list_devices_brief(self):
@@ -1832,7 +1842,7 @@ class DeviceTest(APITestCase):
response = self.client.post(url, data, format='json', **self.header)
self.assertHttpStatus(response, status.HTTP_201_CREATED)
self.assertEqual(Device.objects.count(), 4)
self.assertEqual(Device.objects.count(), 5)
device4 = Device.objects.get(pk=response.data['id'])
self.assertEqual(device4.device_type_id, data['device_type'])
self.assertEqual(device4.device_role_id, data['device_role'])
@@ -1867,7 +1877,7 @@ class DeviceTest(APITestCase):
response = self.client.post(url, data, format='json', **self.header)
self.assertHttpStatus(response, status.HTTP_201_CREATED)
self.assertEqual(Device.objects.count(), 6)
self.assertEqual(Device.objects.count(), 7)
self.assertEqual(response.data[0]['name'], data[0]['name'])
self.assertEqual(response.data[1]['name'], data[1]['name'])
self.assertEqual(response.data[2]['name'], data[2]['name'])
@@ -1891,7 +1901,7 @@ class DeviceTest(APITestCase):
response = self.client.put(url, data, format='json', **self.header)
self.assertHttpStatus(response, status.HTTP_200_OK)
self.assertEqual(Device.objects.count(), 3)
self.assertEqual(Device.objects.count(), 4)
device1 = Device.objects.get(pk=response.data['id'])
self.assertEqual(device1.device_type_id, data['device_type'])
self.assertEqual(device1.device_role_id, data['device_role'])
@@ -1906,7 +1916,21 @@ class DeviceTest(APITestCase):
response = self.client.delete(url, **self.header)
self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
self.assertEqual(Device.objects.count(), 2)
self.assertEqual(Device.objects.count(), 3)
def test_config_context_included_by_default_in_list_view(self):
url = reverse('dcim-api:device-list') + '?slug=device-with-context-data'
response = self.client.get(url, **self.header)
self.assertEqual(response.data['results'][0].get('config_context', {}).get('A'), 1)
def test_config_context_excluded(self):
url = reverse('dcim-api:device-list') + '?exclude=config_context'
response = self.client.get(url, **self.header)
self.assertFalse('config_context' in response.data['results'][0])
class ConsolePortTest(APITestCase):
@@ -2529,7 +2553,7 @@ class InterfaceTest(APITestCase):
def test_update_interface(self):
lag_interface = Interface.objects.create(
device=self.device, name='Test LAG Interface', form_factor=IFACE_FF_LAG
device=self.device, name='Test LAG Interface', type=IFACE_TYPE_LAG
)
data = {
@@ -2817,7 +2841,7 @@ class CableTest(APITestCase):
)
for device in [self.device1, self.device2]:
for i in range(0, 10):
Interface(device=device, form_factor=IFACE_FF_1GE_FIXED, name='eth{}'.format(i)).save()
Interface(device=device, type=IFACE_TYPE_1GE_FIXED, name='eth{}'.format(i)).save()
self.cable1 = Cable(
termination_a=self.device1.interfaces.get(name='eth0'),
@@ -3386,23 +3410,23 @@ class VirtualChassisTest(APITestCase):
device_type=device_type, device_role=device_role, name='StackSwitch9', site=site
)
for i in range(0, 13):
Interface.objects.create(device=self.device1, name='1/{}'.format(i), form_factor=IFACE_FF_1GE_FIXED)
Interface.objects.create(device=self.device1, name='1/{}'.format(i), type=IFACE_TYPE_1GE_FIXED)
for i in range(0, 13):
Interface.objects.create(device=self.device2, name='2/{}'.format(i), form_factor=IFACE_FF_1GE_FIXED)
Interface.objects.create(device=self.device2, name='2/{}'.format(i), type=IFACE_TYPE_1GE_FIXED)
for i in range(0, 13):
Interface.objects.create(device=self.device3, name='3/{}'.format(i), form_factor=IFACE_FF_1GE_FIXED)
Interface.objects.create(device=self.device3, name='3/{}'.format(i), type=IFACE_TYPE_1GE_FIXED)
for i in range(0, 13):
Interface.objects.create(device=self.device4, name='1/{}'.format(i), form_factor=IFACE_FF_1GE_FIXED)
Interface.objects.create(device=self.device4, name='1/{}'.format(i), type=IFACE_TYPE_1GE_FIXED)
for i in range(0, 13):
Interface.objects.create(device=self.device5, name='2/{}'.format(i), form_factor=IFACE_FF_1GE_FIXED)
Interface.objects.create(device=self.device5, name='2/{}'.format(i), type=IFACE_TYPE_1GE_FIXED)
for i in range(0, 13):
Interface.objects.create(device=self.device6, name='3/{}'.format(i), form_factor=IFACE_FF_1GE_FIXED)
Interface.objects.create(device=self.device6, name='3/{}'.format(i), type=IFACE_TYPE_1GE_FIXED)
for i in range(0, 13):
Interface.objects.create(device=self.device7, name='1/{}'.format(i), form_factor=IFACE_FF_1GE_FIXED)
Interface.objects.create(device=self.device7, name='1/{}'.format(i), type=IFACE_TYPE_1GE_FIXED)
for i in range(0, 13):
Interface.objects.create(device=self.device8, name='2/{}'.format(i), form_factor=IFACE_FF_1GE_FIXED)
Interface.objects.create(device=self.device8, name='2/{}'.format(i), type=IFACE_TYPE_1GE_FIXED)
for i in range(0, 13):
Interface.objects.create(device=self.device9, name='3/{}'.format(i), form_factor=IFACE_FF_1GE_FIXED)
Interface.objects.create(device=self.device9, name='3/{}'.format(i), type=IFACE_TYPE_1GE_FIXED)
# Create two VirtualChassis with three members each
self.vc1 = VirtualChassis.objects.create(master=self.device1, domain='test-domain-1')
@@ -3433,7 +3457,7 @@ class VirtualChassisTest(APITestCase):
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'master', 'url']
['id', 'master', 'member_count', 'url']
)
def test_create_virtualchassis(self):
@@ -3508,3 +3532,260 @@ class VirtualChassisTest(APITestCase):
self.assertTrue(
Device.objects.filter(pk=d.pk, virtual_chassis=None, vc_position=None, vc_priority=None)
)
class PowerPanelTest(APITestCase):
def setUp(self):
super().setUp()
self.site1 = Site.objects.create(name='Test Site 1', slug='test-site-1')
self.rackgroup1 = RackGroup.objects.create(site=self.site1, name='Test Rack Group 1', slug='test-rack-group-1')
self.rackgroup2 = RackGroup.objects.create(site=self.site1, name='Test Rack Group 2', slug='test-rack-group-2')
self.rackgroup3 = RackGroup.objects.create(site=self.site1, name='Test Rack Group 3', slug='test-rack-group-3')
self.powerpanel1 = PowerPanel.objects.create(
site=self.site1, rack_group=self.rackgroup1, name='Test Power Panel 1'
)
self.powerpanel2 = PowerPanel.objects.create(
site=self.site1, rack_group=self.rackgroup2, name='Test Power Panel 2'
)
self.powerpanel3 = PowerPanel.objects.create(
site=self.site1, rack_group=self.rackgroup3, name='Test Power Panel 3'
)
def test_get_powerpanel(self):
url = reverse('dcim-api:powerpanel-detail', kwargs={'pk': self.powerpanel1.pk})
response = self.client.get(url, **self.header)
self.assertEqual(response.data['name'], self.powerpanel1.name)
def test_list_powerpanels(self):
url = reverse('dcim-api:powerpanel-list')
response = self.client.get(url, **self.header)
self.assertEqual(response.data['count'], 3)
def test_list_powerpanels_brief(self):
url = reverse('dcim-api:powerpanel-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'powerfeed_count', 'url']
)
def test_create_powerpanel(self):
data = {
'name': 'Test Power Panel 4',
'site': self.site1.pk,
'rack_group': self.rackgroup1.pk,
}
url = reverse('dcim-api:powerpanel-list')
response = self.client.post(url, data, format='json', **self.header)
self.assertHttpStatus(response, status.HTTP_201_CREATED)
self.assertEqual(PowerPanel.objects.count(), 4)
powerpanel4 = PowerPanel.objects.get(pk=response.data['id'])
self.assertEqual(powerpanel4.name, data['name'])
self.assertEqual(powerpanel4.site_id, data['site'])
self.assertEqual(powerpanel4.rack_group_id, data['rack_group'])
def test_create_powerpanel_bulk(self):
data = [
{
'name': 'Test Power Panel 4',
'site': self.site1.pk,
'rack_group': self.rackgroup1.pk,
},
{
'name': 'Test Power Panel 5',
'site': self.site1.pk,
'rack_group': self.rackgroup2.pk,
},
{
'name': 'Test Power Panel 6',
'site': self.site1.pk,
'rack_group': self.rackgroup3.pk,
},
]
url = reverse('dcim-api:powerpanel-list')
response = self.client.post(url, data, format='json', **self.header)
self.assertHttpStatus(response, status.HTTP_201_CREATED)
self.assertEqual(PowerPanel.objects.count(), 6)
self.assertEqual(response.data[0]['name'], data[0]['name'])
self.assertEqual(response.data[1]['name'], data[1]['name'])
self.assertEqual(response.data[2]['name'], data[2]['name'])
def test_update_powerpanel(self):
data = {
'name': 'Test Power Panel X',
'rack_group': self.rackgroup2.pk,
}
url = reverse('dcim-api:powerpanel-detail', kwargs={'pk': self.powerpanel1.pk})
response = self.client.patch(url, data, format='json', **self.header)
self.assertHttpStatus(response, status.HTTP_200_OK)
self.assertEqual(PowerPanel.objects.count(), 3)
powerpanel1 = PowerPanel.objects.get(pk=response.data['id'])
self.assertEqual(powerpanel1.name, data['name'])
self.assertEqual(powerpanel1.rack_group_id, data['rack_group'])
def test_delete_powerpanel(self):
url = reverse('dcim-api:powerpanel-detail', kwargs={'pk': self.powerpanel1.pk})
response = self.client.delete(url, **self.header)
self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
self.assertEqual(PowerPanel.objects.count(), 2)
class PowerFeedTest(APITestCase):
def setUp(self):
super().setUp()
self.site1 = Site.objects.create(name='Test Site 1', slug='test-site-1')
self.rackgroup1 = RackGroup.objects.create(site=self.site1, name='Test Rack Group 1', slug='test-rack-group-1')
self.rackrole1 = RackRole.objects.create(name='Test Rack Role 1', slug='test-rack-role-1', color='ff0000')
self.rack1 = Rack.objects.create(
site=self.site1, group=self.rackgroup1, role=self.rackrole1, name='Test Rack 1', u_height=42,
)
self.rack2 = Rack.objects.create(
site=self.site1, group=self.rackgroup1, role=self.rackrole1, name='Test Rack 2', u_height=42,
)
self.rack3 = Rack.objects.create(
site=self.site1, group=self.rackgroup1, role=self.rackrole1, name='Test Rack 3', u_height=42,
)
self.rack4 = Rack.objects.create(
site=self.site1, group=self.rackgroup1, role=self.rackrole1, name='Test Rack 4', u_height=42,
)
self.powerpanel1 = PowerPanel.objects.create(
site=self.site1, rack_group=self.rackgroup1, name='Test Power Panel 1'
)
self.powerpanel2 = PowerPanel.objects.create(
site=self.site1, rack_group=self.rackgroup1, name='Test Power Panel 2'
)
self.powerfeed1 = PowerFeed.objects.create(
power_panel=self.powerpanel1, rack=self.rack1, name='Test Power Feed 1A', type=POWERFEED_TYPE_PRIMARY
)
self.powerfeed2 = PowerFeed.objects.create(
power_panel=self.powerpanel2, rack=self.rack1, name='Test Power Feed 1B', type=POWERFEED_TYPE_REDUNDANT
)
self.powerfeed3 = PowerFeed.objects.create(
power_panel=self.powerpanel1, rack=self.rack2, name='Test Power Feed 2A', type=POWERFEED_TYPE_PRIMARY
)
self.powerfeed4 = PowerFeed.objects.create(
power_panel=self.powerpanel2, rack=self.rack2, name='Test Power Feed 2B', type=POWERFEED_TYPE_REDUNDANT
)
self.powerfeed5 = PowerFeed.objects.create(
power_panel=self.powerpanel1, rack=self.rack3, name='Test Power Feed 3A', type=POWERFEED_TYPE_PRIMARY
)
self.powerfeed6 = PowerFeed.objects.create(
power_panel=self.powerpanel2, rack=self.rack3, name='Test Power Feed 3B', type=POWERFEED_TYPE_REDUNDANT
)
def test_get_powerfeed(self):
url = reverse('dcim-api:powerfeed-detail', kwargs={'pk': self.powerfeed1.pk})
response = self.client.get(url, **self.header)
self.assertEqual(response.data['name'], self.powerfeed1.name)
def test_list_powerfeeds(self):
url = reverse('dcim-api:powerfeed-list')
response = self.client.get(url, **self.header)
self.assertEqual(response.data['count'], 6)
def test_list_powerfeeds_brief(self):
url = reverse('dcim-api:powerfeed-list')
response = self.client.get('{}?brief=1'.format(url), **self.header)
self.assertEqual(
sorted(response.data['results'][0]),
['id', 'name', 'url']
)
def test_create_powerfeed(self):
data = {
'name': 'Test Power Feed 4A',
'power_panel': self.powerpanel1.pk,
'rack': self.rack4.pk,
'type': POWERFEED_TYPE_PRIMARY,
}
url = reverse('dcim-api:powerfeed-list')
response = self.client.post(url, data, format='json', **self.header)
self.assertHttpStatus(response, status.HTTP_201_CREATED)
self.assertEqual(PowerFeed.objects.count(), 7)
powerfeed4 = PowerFeed.objects.get(pk=response.data['id'])
self.assertEqual(powerfeed4.name, data['name'])
self.assertEqual(powerfeed4.power_panel_id, data['power_panel'])
self.assertEqual(powerfeed4.rack_id, data['rack'])
def test_create_powerfeed_bulk(self):
data = [
{
'name': 'Test Power Feed 4A',
'power_panel': self.powerpanel1.pk,
'rack': self.rack4.pk,
'type': POWERFEED_TYPE_PRIMARY,
},
{
'name': 'Test Power Feed 4B',
'power_panel': self.powerpanel1.pk,
'rack': self.rack4.pk,
'type': POWERFEED_TYPE_REDUNDANT,
},
]
url = reverse('dcim-api:powerfeed-list')
response = self.client.post(url, data, format='json', **self.header)
self.assertHttpStatus(response, status.HTTP_201_CREATED)
self.assertEqual(PowerFeed.objects.count(), 8)
self.assertEqual(response.data[0]['name'], data[0]['name'])
self.assertEqual(response.data[1]['name'], data[1]['name'])
def test_update_powerfeed(self):
data = {
'name': 'Test Power Feed X',
'rack': self.rack4.pk,
'type': POWERFEED_TYPE_REDUNDANT,
}
url = reverse('dcim-api:powerfeed-detail', kwargs={'pk': self.powerfeed1.pk})
response = self.client.patch(url, data, format='json', **self.header)
self.assertHttpStatus(response, status.HTTP_200_OK)
self.assertEqual(PowerFeed.objects.count(), 6)
powerfeed1 = PowerFeed.objects.get(pk=response.data['id'])
self.assertEqual(powerfeed1.name, data['name'])
self.assertEqual(powerfeed1.rack_id, data['rack'])
self.assertEqual(powerfeed1.type, data['type'])
def test_delete_powerfeed(self):
url = reverse('dcim-api:powerfeed-detail', kwargs={'pk': self.powerfeed1.pk})
response = self.client.delete(url, **self.header)
self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
self.assertEqual(PowerFeed.objects.count(), 5)

View File

@@ -1,6 +1,5 @@
from django.test import TestCase
from dcim.constants import *
from dcim.models import *
@@ -152,6 +151,137 @@ class RackTestCase(TestCase):
self.assertTrue(pdu)
class DeviceTestCase(TestCase):
def setUp(self):
self.site = Site.objects.create(name='Test Site 1', slug='test-site-1')
manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
self.device_type = DeviceType.objects.create(
manufacturer=manufacturer, model='Test Device Type 1', slug='test-device-type-1'
)
self.device_role = DeviceRole.objects.create(
name='Test Device Role 1', slug='test-device-role-1', color='ff0000'
)
# Create DeviceType components
ConsolePortTemplate(
device_type=self.device_type,
name='Console Port 1'
).save()
ConsoleServerPortTemplate(
device_type=self.device_type,
name='Console Server Port 1'
).save()
ppt = PowerPortTemplate(
device_type=self.device_type,
name='Power Port 1',
maximum_draw=1000,
allocated_draw=500
)
ppt.save()
PowerOutletTemplate(
device_type=self.device_type,
name='Power Outlet 1',
power_port=ppt,
feed_leg=POWERFEED_LEG_A
).save()
InterfaceTemplate(
device_type=self.device_type,
name='Interface 1',
type=IFACE_TYPE_1GE_FIXED,
mgmt_only=True
).save()
rpt = RearPortTemplate(
device_type=self.device_type,
name='Rear Port 1',
type=PORT_TYPE_8P8C,
positions=8
)
rpt.save()
FrontPortTemplate(
device_type=self.device_type,
name='Front Port 1',
type=PORT_TYPE_8P8C,
rear_port=rpt,
rear_port_position=2
).save()
DeviceBayTemplate(
device_type=self.device_type,
name='Device Bay 1'
).save()
def test_device_creation(self):
"""
Ensure that all Device components are copied automatically from the DeviceType.
"""
d = Device(
site=self.site,
device_type=self.device_type,
device_role=self.device_role,
name='Test Device 1'
)
d.save()
ConsolePort.objects.get(
device=d,
name='Console Port 1'
)
ConsoleServerPort.objects.get(
device=d,
name='Console Server Port 1'
)
pp = PowerPort.objects.get(
device=d,
name='Power Port 1',
maximum_draw=1000,
allocated_draw=500
)
PowerOutlet.objects.get(
device=d,
name='Power Outlet 1',
power_port=pp,
feed_leg=POWERFEED_LEG_A
)
Interface.objects.get(
device=d,
name='Interface 1',
type=IFACE_TYPE_1GE_FIXED,
mgmt_only=True
)
rp = RearPort.objects.get(
device=d,
name='Rear Port 1',
type=PORT_TYPE_8P8C,
positions=8
)
FrontPort.objects.get(
device=d,
name='Front Port 1',
type=PORT_TYPE_8P8C,
rear_port=rp,
rear_port_position=2
)
DeviceBay.objects.get(
device=d,
name='Device Bay 1'
)
class CableTestCase(TestCase):
def setUp(self):
@@ -213,7 +343,7 @@ class CableTestCase(TestCase):
def test_cable_validates_compatibale_types(self):
"""
The clean method should have a check to ensure only compatiable port types can be connected by a cable
The clean method should have a check to ensure only compatible port types can be connected by a cable
"""
# An interface cannot be connected to a power port
cable = Cable(termination_a=self.interface1, termination_b=self.power_port1)
@@ -230,30 +360,39 @@ class CableTestCase(TestCase):
def test_cable_front_port_cannot_connect_to_corresponding_rear_port(self):
"""
A cable cannot connect a front port to its sorresponding rear port
A cable cannot connect a front port to its corresponding rear port
"""
cable = Cable(termination_a=self.front_port, termination_b=self.rear_port)
with self.assertRaises(ValidationError):
cable.clean()
def test_cable_cannot_be_connected_to_an_existing_connection(self):
def test_cable_cannot_terminate_to_an_existing_connection(self):
"""
Either side of a cable cannot be terminated when that side aready has a connection
Either side of a cable cannot be terminated when that side already has a connection
"""
# Try to create a cable with the same interface terminations
cable = Cable(termination_a=self.interface2, termination_b=self.interface1)
with self.assertRaises(ValidationError):
cable.clean()
def test_cable_cannot_connect_to_a_virtual_inteface(self):
def test_cable_cannot_terminate_to_a_virtual_inteface(self):
"""
A cable connection cannot include a virtual interface
A cable cannot terminate to a virtual interface
"""
virtual_interface = Interface(device=self.device1, name="V1", form_factor=0)
virtual_interface = Interface(device=self.device1, name="V1", type=IFACE_TYPE_VIRTUAL)
cable = Cable(termination_a=self.interface2, termination_b=virtual_interface)
with self.assertRaises(ValidationError):
cable.clean()
def test_cable_cannot_terminate_to_a_wireless_inteface(self):
"""
A cable cannot terminate to a wireless interface
"""
wireless_interface = Interface(device=self.device1, name="W1", type=IFACE_TYPE_80211A)
cable = Cable(termination_a=self.interface2, termination_b=wireless_interface)
with self.assertRaises(ValidationError):
cable.clean()
class CablePathTestCase(TestCase):

View File

@@ -1,21 +1,22 @@
import urllib.parse
from django.contrib.auth import get_user_model
from django.test import Client, TestCase
from django.urls import reverse
from dcim.constants import CABLE_TYPE_CAT6, IFACE_FF_1GE_FIXED
from dcim.constants import CABLE_TYPE_CAT6, IFACE_TYPE_1GE_FIXED
from dcim.models import (
Cable, Device, DeviceRole, DeviceType, Interface, InventoryItem, Manufacturer, Platform, Rack, RackGroup,
RackReservation, RackRole, Site, Region, VirtualChassis,
)
from utilities.testing import create_test_user
class RegionTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['dcim.view_region'])
self.client = Client()
self.client.force_login(user)
# Create three Regions
for i in range(1, 4):
@@ -32,8 +33,9 @@ class RegionTestCase(TestCase):
class SiteTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['dcim.view_site'])
self.client = Client()
self.client.force_login(user)
region = Region(name='Region 1', slug='region-1')
region.save()
@@ -64,8 +66,9 @@ class SiteTestCase(TestCase):
class RackGroupTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['dcim.view_rackgroup'])
self.client = Client()
self.client.force_login(user)
site = Site(name='Site 1', slug='site-1')
site.save()
@@ -84,11 +87,12 @@ class RackGroupTestCase(TestCase):
self.assertEqual(response.status_code, 200)
class RackTypeTestCase(TestCase):
class RackRoleTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['dcim.view_rackrole'])
self.client = Client()
self.client.force_login(user)
RackRole.objects.bulk_create([
RackRole(name='Rack Role 1', slug='rack-role-1'),
@@ -107,12 +111,9 @@ class RackTypeTestCase(TestCase):
class RackReservationTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['dcim.view_rackreservation'])
self.client = Client()
User = get_user_model()
user = User(username='testuser', email='testuser@example.com')
user.save()
self.client.force_login(user)
site = Site(name='Site 1', slug='site-1')
site.save()
@@ -137,8 +138,9 @@ class RackReservationTestCase(TestCase):
class RackTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['dcim.view_rack'])
self.client = Client()
self.client.force_login(user)
site = Site(name='Site 1', slug='site-1')
site.save()
@@ -169,8 +171,9 @@ class RackTestCase(TestCase):
class ManufacturerTypeTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['dcim.view_manufacturer'])
self.client = Client()
self.client.force_login(user)
Manufacturer.objects.bulk_create([
Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
@@ -189,8 +192,9 @@ class ManufacturerTypeTestCase(TestCase):
class DeviceTypeTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['dcim.view_devicetype'])
self.client = Client()
self.client.force_login(user)
manufacturer = Manufacturer(name='Manufacturer 1', slug='manufacturer-1')
manufacturer.save()
@@ -221,8 +225,9 @@ class DeviceTypeTestCase(TestCase):
class DeviceRoleTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['dcim.view_devicerole'])
self.client = Client()
self.client.force_login(user)
DeviceRole.objects.bulk_create([
DeviceRole(name='Device Role 1', slug='device-role-1'),
@@ -241,8 +246,9 @@ class DeviceRoleTestCase(TestCase):
class PlatformTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['dcim.view_platform'])
self.client = Client()
self.client.force_login(user)
Platform.objects.bulk_create([
Platform(name='Platform 1', slug='platform-1'),
@@ -261,8 +267,9 @@ class PlatformTestCase(TestCase):
class DeviceTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['dcim.view_device'])
self.client = Client()
self.client.force_login(user)
site = Site(name='Site 1', slug='site-1')
site.save()
@@ -303,8 +310,9 @@ class DeviceTestCase(TestCase):
class InventoryItemTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['dcim.view_inventoryitem'])
self.client = Client()
self.client.force_login(user)
site = Site(name='Site 1', slug='site-1')
site.save()
@@ -337,18 +345,13 @@ class InventoryItemTestCase(TestCase):
response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params)))
self.assertEqual(response.status_code, 200)
def test_inventoryitem(self):
inventoryitem = InventoryItem.objects.first()
response = self.client.get(inventoryitem.get_absolute_url())
self.assertEqual(response.status_code, 200)
class CableTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['dcim.view_cable'])
self.client = Client()
self.client.force_login(user)
site = Site(name='Site 1', slug='site-1')
site.save()
@@ -367,17 +370,17 @@ class CableTestCase(TestCase):
device2 = Device(name='Device 2', site=site, device_type=devicetype, device_role=devicerole)
device2.save()
iface1 = Interface(device=device1, name='Interface 1', form_factor=IFACE_FF_1GE_FIXED)
iface1 = Interface(device=device1, name='Interface 1', type=IFACE_TYPE_1GE_FIXED)
iface1.save()
iface2 = Interface(device=device1, name='Interface 2', form_factor=IFACE_FF_1GE_FIXED)
iface2 = Interface(device=device1, name='Interface 2', type=IFACE_TYPE_1GE_FIXED)
iface2.save()
iface3 = Interface(device=device1, name='Interface 3', form_factor=IFACE_FF_1GE_FIXED)
iface3 = Interface(device=device1, name='Interface 3', type=IFACE_TYPE_1GE_FIXED)
iface3.save()
iface4 = Interface(device=device2, name='Interface 1', form_factor=IFACE_FF_1GE_FIXED)
iface4 = Interface(device=device2, name='Interface 1', type=IFACE_TYPE_1GE_FIXED)
iface4.save()
iface5 = Interface(device=device2, name='Interface 2', form_factor=IFACE_FF_1GE_FIXED)
iface5 = Interface(device=device2, name='Interface 2', type=IFACE_TYPE_1GE_FIXED)
iface5.save()
iface6 = Interface(device=device2, name='Interface 3', form_factor=IFACE_FF_1GE_FIXED)
iface6 = Interface(device=device2, name='Interface 3', type=IFACE_TYPE_1GE_FIXED)
iface6.save()
Cable(termination_a=iface1, termination_b=iface4, type=CABLE_TYPE_CAT6).save()
@@ -401,11 +404,12 @@ class CableTestCase(TestCase):
self.assertEqual(response.status_code, 200)
class VirtualMachineTestCase(TestCase):
class VirtualChassisTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['dcim.view_virtualchassis'])
self.client = Client()
self.client.force_login(user)
site = Site.objects.create(name='Site 1', slug='site-1')
manufacturer = Manufacturer.objects.create(name='Manufacturer', slug='manufacturer-1')
@@ -450,9 +454,3 @@ class VirtualMachineTestCase(TestCase):
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
def test_virtualchassis(self):
virtualchassis = VirtualChassis.objects.first()
response = self.client.get(virtualchassis.get_absolute_url())
self.assertEqual(response.status_code, 200)

View File

@@ -1,4 +1,4 @@
from django.conf.urls import url
from django.urls import path
from extras.views import ObjectChangeLogView, ImageAttachmentEditView
from ipam.views import ServiceCreateView
@@ -6,277 +6,309 @@ from secrets.views import secret_add
from . import views
from .models import (
Cable, ConsolePort, ConsoleServerPort, Device, DeviceRole, DeviceType, FrontPort, Interface, Manufacturer, Platform,
PowerPort, PowerOutlet, Rack, RackGroup, RackReservation, RackRole, RearPort, Region, Site, VirtualChassis,
PowerFeed, PowerPanel, PowerPort, PowerOutlet, Rack, RackGroup, RackReservation, RackRole, RearPort, Region, Site,
VirtualChassis,
)
app_name = 'dcim'
urlpatterns = [
# Regions
url(r'^regions/$', views.RegionListView.as_view(), name='region_list'),
url(r'^regions/add/$', views.RegionCreateView.as_view(), name='region_add'),
url(r'^regions/import/$', views.RegionBulkImportView.as_view(), name='region_import'),
url(r'^regions/delete/$', views.RegionBulkDeleteView.as_view(), name='region_bulk_delete'),
url(r'^regions/(?P<pk>\d+)/edit/$', views.RegionEditView.as_view(), name='region_edit'),
url(r'^regions/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='region_changelog', kwargs={'model': Region}),
path(r'regions/', views.RegionListView.as_view(), name='region_list'),
path(r'regions/add/', views.RegionCreateView.as_view(), name='region_add'),
path(r'regions/import/', views.RegionBulkImportView.as_view(), name='region_import'),
path(r'regions/delete/', views.RegionBulkDeleteView.as_view(), name='region_bulk_delete'),
path(r'regions/<int:pk>/edit/', views.RegionEditView.as_view(), name='region_edit'),
path(r'regions/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='region_changelog', kwargs={'model': Region}),
# Sites
url(r'^sites/$', views.SiteListView.as_view(), name='site_list'),
url(r'^sites/add/$', views.SiteCreateView.as_view(), name='site_add'),
url(r'^sites/import/$', views.SiteBulkImportView.as_view(), name='site_import'),
url(r'^sites/edit/$', views.SiteBulkEditView.as_view(), name='site_bulk_edit'),
url(r'^sites/(?P<slug>[\w-]+)/$', views.SiteView.as_view(), name='site'),
url(r'^sites/(?P<slug>[\w-]+)/edit/$', views.SiteEditView.as_view(), name='site_edit'),
url(r'^sites/(?P<slug>[\w-]+)/delete/$', views.SiteDeleteView.as_view(), name='site_delete'),
url(r'^sites/(?P<slug>[\w-]+)/changelog/$', ObjectChangeLogView.as_view(), name='site_changelog', kwargs={'model': Site}),
url(r'^sites/(?P<object_id>\d+)/images/add/$', ImageAttachmentEditView.as_view(), name='site_add_image', kwargs={'model': Site}),
path(r'sites/', views.SiteListView.as_view(), name='site_list'),
path(r'sites/add/', views.SiteCreateView.as_view(), name='site_add'),
path(r'sites/import/', views.SiteBulkImportView.as_view(), name='site_import'),
path(r'sites/edit/', views.SiteBulkEditView.as_view(), name='site_bulk_edit'),
path(r'sites/delete/', views.SiteBulkDeleteView.as_view(), name='site_bulk_delete'),
path(r'sites/<slug:slug>/', views.SiteView.as_view(), name='site'),
path(r'sites/<slug:slug>/edit/', views.SiteEditView.as_view(), name='site_edit'),
path(r'sites/<slug:slug>/delete/', views.SiteDeleteView.as_view(), name='site_delete'),
path(r'sites/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='site_changelog', kwargs={'model': Site}),
path(r'sites/<int:object_id>/images/add/', ImageAttachmentEditView.as_view(), name='site_add_image', kwargs={'model': Site}),
# Rack groups
url(r'^rack-groups/$', views.RackGroupListView.as_view(), name='rackgroup_list'),
url(r'^rack-groups/add/$', views.RackGroupCreateView.as_view(), name='rackgroup_add'),
url(r'^rack-groups/import/$', views.RackGroupBulkImportView.as_view(), name='rackgroup_import'),
url(r'^rack-groups/delete/$', views.RackGroupBulkDeleteView.as_view(), name='rackgroup_bulk_delete'),
url(r'^rack-groups/(?P<pk>\d+)/edit/$', views.RackGroupEditView.as_view(), name='rackgroup_edit'),
url(r'^rack-groups/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='rackgroup_changelog', kwargs={'model': RackGroup}),
path(r'rack-groups/', views.RackGroupListView.as_view(), name='rackgroup_list'),
path(r'rack-groups/add/', views.RackGroupCreateView.as_view(), name='rackgroup_add'),
path(r'rack-groups/import/', views.RackGroupBulkImportView.as_view(), name='rackgroup_import'),
path(r'rack-groups/delete/', views.RackGroupBulkDeleteView.as_view(), name='rackgroup_bulk_delete'),
path(r'rack-groups/<int:pk>/edit/', views.RackGroupEditView.as_view(), name='rackgroup_edit'),
path(r'rack-groups/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='rackgroup_changelog', kwargs={'model': RackGroup}),
# Rack roles
url(r'^rack-roles/$', views.RackRoleListView.as_view(), name='rackrole_list'),
url(r'^rack-roles/add/$', views.RackRoleCreateView.as_view(), name='rackrole_add'),
url(r'^rack-roles/import/$', views.RackRoleBulkImportView.as_view(), name='rackrole_import'),
url(r'^rack-roles/delete/$', views.RackRoleBulkDeleteView.as_view(), name='rackrole_bulk_delete'),
url(r'^rack-roles/(?P<pk>\d+)/edit/$', views.RackRoleEditView.as_view(), name='rackrole_edit'),
url(r'^rack-roles/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='rackrole_changelog', kwargs={'model': RackRole}),
path(r'rack-roles/', views.RackRoleListView.as_view(), name='rackrole_list'),
path(r'rack-roles/add/', views.RackRoleCreateView.as_view(), name='rackrole_add'),
path(r'rack-roles/import/', views.RackRoleBulkImportView.as_view(), name='rackrole_import'),
path(r'rack-roles/delete/', views.RackRoleBulkDeleteView.as_view(), name='rackrole_bulk_delete'),
path(r'rack-roles/<int:pk>/edit/', views.RackRoleEditView.as_view(), name='rackrole_edit'),
path(r'rack-roles/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='rackrole_changelog', kwargs={'model': RackRole}),
# Rack reservations
url(r'^rack-reservations/$', views.RackReservationListView.as_view(), name='rackreservation_list'),
url(r'^rack-reservations/edit/$', views.RackReservationBulkEditView.as_view(), name='rackreservation_bulk_edit'),
url(r'^rack-reservations/delete/$', views.RackReservationBulkDeleteView.as_view(), name='rackreservation_bulk_delete'),
url(r'^rack-reservations/(?P<pk>\d+)/edit/$', views.RackReservationEditView.as_view(), name='rackreservation_edit'),
url(r'^rack-reservations/(?P<pk>\d+)/delete/$', views.RackReservationDeleteView.as_view(), name='rackreservation_delete'),
url(r'^rack-reservations/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='rackreservation_changelog', kwargs={'model': RackReservation}),
path(r'rack-reservations/', views.RackReservationListView.as_view(), name='rackreservation_list'),
path(r'rack-reservations/edit/', views.RackReservationBulkEditView.as_view(), name='rackreservation_bulk_edit'),
path(r'rack-reservations/delete/', views.RackReservationBulkDeleteView.as_view(), name='rackreservation_bulk_delete'),
path(r'rack-reservations/<int:pk>/edit/', views.RackReservationEditView.as_view(), name='rackreservation_edit'),
path(r'rack-reservations/<int:pk>/delete/', views.RackReservationDeleteView.as_view(), name='rackreservation_delete'),
path(r'rack-reservations/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='rackreservation_changelog', kwargs={'model': RackReservation}),
# Racks
url(r'^racks/$', views.RackListView.as_view(), name='rack_list'),
url(r'^rack-elevations/$', views.RackElevationListView.as_view(), name='rack_elevation_list'),
url(r'^racks/add/$', views.RackEditView.as_view(), name='rack_add'),
url(r'^racks/import/$', views.RackBulkImportView.as_view(), name='rack_import'),
url(r'^racks/edit/$', views.RackBulkEditView.as_view(), name='rack_bulk_edit'),
url(r'^racks/delete/$', views.RackBulkDeleteView.as_view(), name='rack_bulk_delete'),
url(r'^racks/(?P<pk>\d+)/$', views.RackView.as_view(), name='rack'),
url(r'^racks/(?P<pk>\d+)/edit/$', views.RackEditView.as_view(), name='rack_edit'),
url(r'^racks/(?P<pk>\d+)/delete/$', views.RackDeleteView.as_view(), name='rack_delete'),
url(r'^racks/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='rack_changelog', kwargs={'model': Rack}),
url(r'^racks/(?P<rack>\d+)/reservations/add/$', views.RackReservationCreateView.as_view(), name='rack_add_reservation'),
url(r'^racks/(?P<object_id>\d+)/images/add/$', ImageAttachmentEditView.as_view(), name='rack_add_image', kwargs={'model': Rack}),
path(r'racks/', views.RackListView.as_view(), name='rack_list'),
path(r'rack-elevations/', views.RackElevationListView.as_view(), name='rack_elevation_list'),
path(r'racks/add/', views.RackEditView.as_view(), name='rack_add'),
path(r'racks/import/', views.RackBulkImportView.as_view(), name='rack_import'),
path(r'racks/edit/', views.RackBulkEditView.as_view(), name='rack_bulk_edit'),
path(r'racks/delete/', views.RackBulkDeleteView.as_view(), name='rack_bulk_delete'),
path(r'racks/<int:pk>/', views.RackView.as_view(), name='rack'),
path(r'racks/<int:pk>/edit/', views.RackEditView.as_view(), name='rack_edit'),
path(r'racks/<int:pk>/delete/', views.RackDeleteView.as_view(), name='rack_delete'),
path(r'racks/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='rack_changelog', kwargs={'model': Rack}),
path(r'racks/<int:rack>/reservations/add/', views.RackReservationCreateView.as_view(), name='rack_add_reservation'),
path(r'racks/<int:object_id>/images/add/', ImageAttachmentEditView.as_view(), name='rack_add_image', kwargs={'model': Rack}),
# Manufacturers
url(r'^manufacturers/$', views.ManufacturerListView.as_view(), name='manufacturer_list'),
url(r'^manufacturers/add/$', views.ManufacturerCreateView.as_view(), name='manufacturer_add'),
url(r'^manufacturers/import/$', views.ManufacturerBulkImportView.as_view(), name='manufacturer_import'),
url(r'^manufacturers/delete/$', views.ManufacturerBulkDeleteView.as_view(), name='manufacturer_bulk_delete'),
url(r'^manufacturers/(?P<slug>[\w-]+)/edit/$', views.ManufacturerEditView.as_view(), name='manufacturer_edit'),
url(r'^manufacturers/(?P<slug>[\w-]+)/changelog/$', ObjectChangeLogView.as_view(), name='manufacturer_changelog', kwargs={'model': Manufacturer}),
path(r'manufacturers/', views.ManufacturerListView.as_view(), name='manufacturer_list'),
path(r'manufacturers/add/', views.ManufacturerCreateView.as_view(), name='manufacturer_add'),
path(r'manufacturers/import/', views.ManufacturerBulkImportView.as_view(), name='manufacturer_import'),
path(r'manufacturers/delete/', views.ManufacturerBulkDeleteView.as_view(), name='manufacturer_bulk_delete'),
path(r'manufacturers/<slug:slug>/edit/', views.ManufacturerEditView.as_view(), name='manufacturer_edit'),
path(r'manufacturers/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='manufacturer_changelog', kwargs={'model': Manufacturer}),
# Device types
url(r'^device-types/$', views.DeviceTypeListView.as_view(), name='devicetype_list'),
url(r'^device-types/add/$', views.DeviceTypeCreateView.as_view(), name='devicetype_add'),
url(r'^device-types/import/$', views.DeviceTypeBulkImportView.as_view(), name='devicetype_import'),
url(r'^device-types/edit/$', views.DeviceTypeBulkEditView.as_view(), name='devicetype_bulk_edit'),
url(r'^device-types/delete/$', views.DeviceTypeBulkDeleteView.as_view(), name='devicetype_bulk_delete'),
url(r'^device-types/(?P<pk>\d+)/$', views.DeviceTypeView.as_view(), name='devicetype'),
url(r'^device-types/(?P<pk>\d+)/edit/$', views.DeviceTypeEditView.as_view(), name='devicetype_edit'),
url(r'^device-types/(?P<pk>\d+)/delete/$', views.DeviceTypeDeleteView.as_view(), name='devicetype_delete'),
url(r'^device-types/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='devicetype_changelog', kwargs={'model': DeviceType}),
path(r'device-types/', views.DeviceTypeListView.as_view(), name='devicetype_list'),
path(r'device-types/add/', views.DeviceTypeCreateView.as_view(), name='devicetype_add'),
path(r'device-types/import/', views.DeviceTypeBulkImportView.as_view(), name='devicetype_import'),
path(r'device-types/edit/', views.DeviceTypeBulkEditView.as_view(), name='devicetype_bulk_edit'),
path(r'device-types/delete/', views.DeviceTypeBulkDeleteView.as_view(), name='devicetype_bulk_delete'),
path(r'device-types/<int:pk>/', views.DeviceTypeView.as_view(), name='devicetype'),
path(r'device-types/<int:pk>/edit/', views.DeviceTypeEditView.as_view(), name='devicetype_edit'),
path(r'device-types/<int:pk>/delete/', views.DeviceTypeDeleteView.as_view(), name='devicetype_delete'),
path(r'device-types/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='devicetype_changelog', kwargs={'model': DeviceType}),
# Console port templates
url(r'^device-types/(?P<pk>\d+)/console-ports/add/$', views.ConsolePortTemplateCreateView.as_view(), name='devicetype_add_consoleport'),
url(r'^device-types/(?P<pk>\d+)/console-ports/delete/$', views.ConsolePortTemplateBulkDeleteView.as_view(), name='devicetype_delete_consoleport'),
path(r'device-types/<int:pk>/console-ports/add/', views.ConsolePortTemplateCreateView.as_view(), name='devicetype_add_consoleport'),
path(r'device-types/<int:pk>/console-ports/delete/', views.ConsolePortTemplateBulkDeleteView.as_view(), name='devicetype_delete_consoleport'),
path(r'console-port-templates/<int:pk>/edit/', views.ConsolePortTemplateEditView.as_view(), name='consoleporttemplate_edit'),
# Console server port templates
url(r'^device-types/(?P<pk>\d+)/console-server-ports/add/$', views.ConsoleServerPortTemplateCreateView.as_view(), name='devicetype_add_consoleserverport'),
url(r'^device-types/(?P<pk>\d+)/console-server-ports/delete/$', views.ConsoleServerPortTemplateBulkDeleteView.as_view(), name='devicetype_delete_consoleserverport'),
path(r'device-types/<int:pk>/console-server-ports/add/', views.ConsoleServerPortTemplateCreateView.as_view(), name='devicetype_add_consoleserverport'),
path(r'device-types/<int:pk>/console-server-ports/delete/', views.ConsoleServerPortTemplateBulkDeleteView.as_view(), name='devicetype_delete_consoleserverport'),
path(r'console-server-port-templates/<int:pk>/edit/', views.ConsoleServerPortTemplateEditView.as_view(), name='consoleserverporttemplate_edit'),
# Power port templates
url(r'^device-types/(?P<pk>\d+)/power-ports/add/$', views.PowerPortTemplateCreateView.as_view(), name='devicetype_add_powerport'),
url(r'^device-types/(?P<pk>\d+)/power-ports/delete/$', views.PowerPortTemplateBulkDeleteView.as_view(), name='devicetype_delete_powerport'),
path(r'device-types/<int:pk>/power-ports/add/', views.PowerPortTemplateCreateView.as_view(), name='devicetype_add_powerport'),
path(r'device-types/<int:pk>/power-ports/delete/', views.PowerPortTemplateBulkDeleteView.as_view(), name='devicetype_delete_powerport'),
path(r'power-port-templates/<int:pk>/edit/', views.PowerPortTemplateEditView.as_view(), name='powerporttemplate_edit'),
# Power outlet templates
url(r'^device-types/(?P<pk>\d+)/power-outlets/add/$', views.PowerOutletTemplateCreateView.as_view(), name='devicetype_add_poweroutlet'),
url(r'^device-types/(?P<pk>\d+)/power-outlets/delete/$', views.PowerOutletTemplateBulkDeleteView.as_view(), name='devicetype_delete_poweroutlet'),
path(r'device-types/<int:pk>/power-outlets/add/', views.PowerOutletTemplateCreateView.as_view(), name='devicetype_add_poweroutlet'),
path(r'device-types/<int:pk>/power-outlets/delete/', views.PowerOutletTemplateBulkDeleteView.as_view(), name='devicetype_delete_poweroutlet'),
path(r'power-outlet-templates/<int:pk>/edit/', views.PowerOutletTemplateEditView.as_view(), name='poweroutlettemplate_edit'),
# Interface templates
url(r'^device-types/(?P<pk>\d+)/interfaces/add/$', views.InterfaceTemplateCreateView.as_view(), name='devicetype_add_interface'),
url(r'^device-types/(?P<pk>\d+)/interfaces/edit/$', views.InterfaceTemplateBulkEditView.as_view(), name='devicetype_bulkedit_interface'),
url(r'^device-types/(?P<pk>\d+)/interfaces/delete/$', views.InterfaceTemplateBulkDeleteView.as_view(), name='devicetype_delete_interface'),
path(r'device-types/<int:pk>/interfaces/add/', views.InterfaceTemplateCreateView.as_view(), name='devicetype_add_interface'),
path(r'device-types/<int:pk>/interfaces/edit/', views.InterfaceTemplateBulkEditView.as_view(), name='devicetype_bulkedit_interface'),
path(r'device-types/<int:pk>/interfaces/delete/', views.InterfaceTemplateBulkDeleteView.as_view(), name='devicetype_delete_interface'),
path(r'interface-templates/<int:pk>/edit/', views.InterfaceTemplateEditView.as_view(), name='interfacetemplate_edit'),
# Front port templates
url(r'^device-types/(?P<pk>\d+)/front-ports/add/$', views.FrontPortTemplateCreateView.as_view(), name='devicetype_add_frontport'),
url(r'^device-types/(?P<pk>\d+)/front-ports/delete/$', views.FrontPortTemplateBulkDeleteView.as_view(), name='devicetype_delete_frontport'),
path(r'device-types/<int:pk>/front-ports/add/', views.FrontPortTemplateCreateView.as_view(), name='devicetype_add_frontport'),
path(r'device-types/<int:pk>/front-ports/delete/', views.FrontPortTemplateBulkDeleteView.as_view(), name='devicetype_delete_frontport'),
path(r'front-port-templates/<int:pk>/edit/', views.FrontPortTemplateEditView.as_view(), name='frontporttemplate_edit'),
# Rear port templates
url(r'^device-types/(?P<pk>\d+)/rear-ports/add/$', views.RearPortTemplateCreateView.as_view(), name='devicetype_add_rearport'),
url(r'^device-types/(?P<pk>\d+)/rear-ports/delete/$', views.RearPortTemplateBulkDeleteView.as_view(), name='devicetype_delete_rearport'),
path(r'device-types/<int:pk>/rear-ports/add/', views.RearPortTemplateCreateView.as_view(), name='devicetype_add_rearport'),
path(r'device-types/<int:pk>/rear-ports/delete/', views.RearPortTemplateBulkDeleteView.as_view(), name='devicetype_delete_rearport'),
path(r'rear-port-templates/<int:pk>/edit/', views.RearPortTemplateEditView.as_view(), name='rearporttemplate_edit'),
# Device bay templates
url(r'^device-types/(?P<pk>\d+)/device-bays/add/$', views.DeviceBayTemplateCreateView.as_view(), name='devicetype_add_devicebay'),
url(r'^device-types/(?P<pk>\d+)/device-bays/delete/$', views.DeviceBayTemplateBulkDeleteView.as_view(), name='devicetype_delete_devicebay'),
path(r'device-types/<int:pk>/device-bays/add/', views.DeviceBayTemplateCreateView.as_view(), name='devicetype_add_devicebay'),
path(r'device-types/<int:pk>/device-bays/delete/', views.DeviceBayTemplateBulkDeleteView.as_view(), name='devicetype_delete_devicebay'),
path(r'device-bay-templates/<int:pk>/edit/', views.DeviceBayTemplateEditView.as_view(), name='devicebaytemplate_edit'),
# Device roles
url(r'^device-roles/$', views.DeviceRoleListView.as_view(), name='devicerole_list'),
url(r'^device-roles/add/$', views.DeviceRoleCreateView.as_view(), name='devicerole_add'),
url(r'^device-roles/import/$', views.DeviceRoleBulkImportView.as_view(), name='devicerole_import'),
url(r'^device-roles/delete/$', views.DeviceRoleBulkDeleteView.as_view(), name='devicerole_bulk_delete'),
url(r'^device-roles/(?P<slug>[\w-]+)/edit/$', views.DeviceRoleEditView.as_view(), name='devicerole_edit'),
url(r'^device-roles/(?P<slug>[\w-]+)/changelog/$', ObjectChangeLogView.as_view(), name='devicerole_changelog', kwargs={'model': DeviceRole}),
path(r'device-roles/', views.DeviceRoleListView.as_view(), name='devicerole_list'),
path(r'device-roles/add/', views.DeviceRoleCreateView.as_view(), name='devicerole_add'),
path(r'device-roles/import/', views.DeviceRoleBulkImportView.as_view(), name='devicerole_import'),
path(r'device-roles/delete/', views.DeviceRoleBulkDeleteView.as_view(), name='devicerole_bulk_delete'),
path(r'device-roles/<slug:slug>/edit/', views.DeviceRoleEditView.as_view(), name='devicerole_edit'),
path(r'device-roles/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='devicerole_changelog', kwargs={'model': DeviceRole}),
# Platforms
url(r'^platforms/$', views.PlatformListView.as_view(), name='platform_list'),
url(r'^platforms/add/$', views.PlatformCreateView.as_view(), name='platform_add'),
url(r'^platforms/import/$', views.PlatformBulkImportView.as_view(), name='platform_import'),
url(r'^platforms/delete/$', views.PlatformBulkDeleteView.as_view(), name='platform_bulk_delete'),
url(r'^platforms/(?P<slug>[\w-]+)/edit/$', views.PlatformEditView.as_view(), name='platform_edit'),
url(r'^platforms/(?P<slug>[\w-]+)/changelog/$', ObjectChangeLogView.as_view(), name='platform_changelog', kwargs={'model': Platform}),
path(r'platforms/', views.PlatformListView.as_view(), name='platform_list'),
path(r'platforms/add/', views.PlatformCreateView.as_view(), name='platform_add'),
path(r'platforms/import/', views.PlatformBulkImportView.as_view(), name='platform_import'),
path(r'platforms/delete/', views.PlatformBulkDeleteView.as_view(), name='platform_bulk_delete'),
path(r'platforms/<slug:slug>/edit/', views.PlatformEditView.as_view(), name='platform_edit'),
path(r'platforms/<slug:slug>/changelog/', ObjectChangeLogView.as_view(), name='platform_changelog', kwargs={'model': Platform}),
# Devices
url(r'^devices/$', views.DeviceListView.as_view(), name='device_list'),
url(r'^devices/add/$', views.DeviceCreateView.as_view(), name='device_add'),
url(r'^devices/import/$', views.DeviceBulkImportView.as_view(), name='device_import'),
url(r'^devices/import/child-devices/$', views.ChildDeviceBulkImportView.as_view(), name='device_import_child'),
url(r'^devices/edit/$', views.DeviceBulkEditView.as_view(), name='device_bulk_edit'),
url(r'^devices/delete/$', views.DeviceBulkDeleteView.as_view(), name='device_bulk_delete'),
url(r'^devices/(?P<pk>\d+)/$', views.DeviceView.as_view(), name='device'),
url(r'^devices/(?P<pk>\d+)/edit/$', views.DeviceEditView.as_view(), name='device_edit'),
url(r'^devices/(?P<pk>\d+)/delete/$', views.DeviceDeleteView.as_view(), name='device_delete'),
url(r'^devices/(?P<pk>\d+)/config-context/$', views.DeviceConfigContextView.as_view(), name='device_configcontext'),
url(r'^devices/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='device_changelog', kwargs={'model': Device}),
url(r'^devices/(?P<pk>\d+)/inventory/$', views.DeviceInventoryView.as_view(), name='device_inventory'),
url(r'^devices/(?P<pk>\d+)/status/$', views.DeviceStatusView.as_view(), name='device_status'),
url(r'^devices/(?P<pk>\d+)/lldp-neighbors/$', views.DeviceLLDPNeighborsView.as_view(), name='device_lldp_neighbors'),
url(r'^devices/(?P<pk>\d+)/config/$', views.DeviceConfigView.as_view(), name='device_config'),
url(r'^devices/(?P<pk>\d+)/add-secret/$', secret_add, name='device_addsecret'),
url(r'^devices/(?P<device>\d+)/services/assign/$', ServiceCreateView.as_view(), name='device_service_assign'),
url(r'^devices/(?P<object_id>\d+)/images/add/$', ImageAttachmentEditView.as_view(), name='device_add_image', kwargs={'model': Device}),
path(r'devices/', views.DeviceListView.as_view(), name='device_list'),
path(r'devices/add/', views.DeviceCreateView.as_view(), name='device_add'),
path(r'devices/import/', views.DeviceBulkImportView.as_view(), name='device_import'),
path(r'devices/import/child-devices/', views.ChildDeviceBulkImportView.as_view(), name='device_import_child'),
path(r'devices/edit/', views.DeviceBulkEditView.as_view(), name='device_bulk_edit'),
path(r'devices/delete/', views.DeviceBulkDeleteView.as_view(), name='device_bulk_delete'),
path(r'devices/<int:pk>/', views.DeviceView.as_view(), name='device'),
path(r'devices/<int:pk>/edit/', views.DeviceEditView.as_view(), name='device_edit'),
path(r'devices/<int:pk>/delete/', views.DeviceDeleteView.as_view(), name='device_delete'),
path(r'devices/<int:pk>/config-context/', views.DeviceConfigContextView.as_view(), name='device_configcontext'),
path(r'devices/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='device_changelog', kwargs={'model': Device}),
path(r'devices/<int:pk>/inventory/', views.DeviceInventoryView.as_view(), name='device_inventory'),
path(r'devices/<int:pk>/status/', views.DeviceStatusView.as_view(), name='device_status'),
path(r'devices/<int:pk>/lldp-neighbors/', views.DeviceLLDPNeighborsView.as_view(), name='device_lldp_neighbors'),
path(r'devices/<int:pk>/config/', views.DeviceConfigView.as_view(), name='device_config'),
path(r'devices/<int:pk>/add-secret/', secret_add, name='device_addsecret'),
path(r'devices/<int:device>/services/assign/', ServiceCreateView.as_view(), name='device_service_assign'),
path(r'devices/<int:object_id>/images/add/', ImageAttachmentEditView.as_view(), name='device_add_image', kwargs={'model': Device}),
# Console ports
url(r'^devices/console-ports/add/$', views.DeviceBulkAddConsolePortView.as_view(), name='device_bulk_add_consoleport'),
url(r'^devices/(?P<pk>\d+)/console-ports/add/$', views.ConsolePortCreateView.as_view(), name='consoleport_add'),
url(r'^devices/(?P<pk>\d+)/console-ports/delete/$', views.ConsolePortBulkDeleteView.as_view(), name='consoleport_bulk_delete'),
url(r'^console-ports/(?P<termination_a_id>\d+)/connect/$', views.CableCreateView.as_view(), name='consoleport_connect', kwargs={'termination_a_type': ConsolePort}),
url(r'^console-ports/(?P<pk>\d+)/edit/$', views.ConsolePortEditView.as_view(), name='consoleport_edit'),
url(r'^console-ports/(?P<pk>\d+)/delete/$', views.ConsolePortDeleteView.as_view(), name='consoleport_delete'),
url(r'^console-ports/(?P<pk>\d+)/trace/$', views.CableTraceView.as_view(), name='consoleport_trace', kwargs={'model': ConsolePort}),
path(r'devices/console-ports/add/', views.DeviceBulkAddConsolePortView.as_view(), name='device_bulk_add_consoleport'),
path(r'devices/<int:pk>/console-ports/add/', views.ConsolePortCreateView.as_view(), name='consoleport_add'),
path(r'devices/<int:pk>/console-ports/delete/', views.ConsolePortBulkDeleteView.as_view(), name='consoleport_bulk_delete'),
path(r'console-ports/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='consoleport_connect', kwargs={'termination_a_type': ConsolePort}),
path(r'console-ports/<int:pk>/edit/', views.ConsolePortEditView.as_view(), name='consoleport_edit'),
path(r'console-ports/<int:pk>/delete/', views.ConsolePortDeleteView.as_view(), name='consoleport_delete'),
path(r'console-ports/<int:pk>/trace/', views.CableTraceView.as_view(), name='consoleport_trace', kwargs={'model': ConsolePort}),
# Console server ports
url(r'^devices/console-server-ports/add/$', views.DeviceBulkAddConsoleServerPortView.as_view(), name='device_bulk_add_consoleserverport'),
url(r'^devices/(?P<pk>\d+)/console-server-ports/add/$', views.ConsoleServerPortCreateView.as_view(), name='consoleserverport_add'),
url(r'^devices/(?P<pk>\d+)/console-server-ports/delete/$', views.ConsoleServerPortBulkDeleteView.as_view(), name='consoleserverport_bulk_delete'),
url(r'^console-server-ports/(?P<termination_a_id>\d+)/connect/$', views.CableCreateView.as_view(), name='consoleserverport_connect', kwargs={'termination_a_type': ConsoleServerPort}),
url(r'^console-server-ports/(?P<pk>\d+)/edit/$', views.ConsoleServerPortEditView.as_view(), name='consoleserverport_edit'),
url(r'^console-server-ports/(?P<pk>\d+)/delete/$', views.ConsoleServerPortDeleteView.as_view(), name='consoleserverport_delete'),
url(r'^console-server-ports/(?P<pk>\d+)/trace/$', views.CableTraceView.as_view(), name='consoleserverport_trace', kwargs={'model': ConsoleServerPort}),
url(r'^console-server-ports/rename/$', views.ConsoleServerPortBulkRenameView.as_view(), name='consoleserverport_bulk_rename'),
url(r'^console-server-ports/disconnect/$', views.ConsoleServerPortBulkDisconnectView.as_view(), name='consoleserverport_bulk_disconnect'),
path(r'devices/console-server-ports/add/', views.DeviceBulkAddConsoleServerPortView.as_view(), name='device_bulk_add_consoleserverport'),
path(r'devices/<int:pk>/console-server-ports/add/', views.ConsoleServerPortCreateView.as_view(), name='consoleserverport_add'),
path(r'devices/<int:pk>/console-server-ports/edit/', views.ConsoleServerPortBulkEditView.as_view(), name='consoleserverport_bulk_edit'),
path(r'devices/<int:pk>/console-server-ports/delete/', views.ConsoleServerPortBulkDeleteView.as_view(), name='consoleserverport_bulk_delete'),
path(r'console-server-ports/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='consoleserverport_connect', kwargs={'termination_a_type': ConsoleServerPort}),
path(r'console-server-ports/<int:pk>/edit/', views.ConsoleServerPortEditView.as_view(), name='consoleserverport_edit'),
path(r'console-server-ports/<int:pk>/delete/', views.ConsoleServerPortDeleteView.as_view(), name='consoleserverport_delete'),
path(r'console-server-ports/<int:pk>/trace/', views.CableTraceView.as_view(), name='consoleserverport_trace', kwargs={'model': ConsoleServerPort}),
path(r'console-server-ports/rename/', views.ConsoleServerPortBulkRenameView.as_view(), name='consoleserverport_bulk_rename'),
path(r'console-server-ports/disconnect/', views.ConsoleServerPortBulkDisconnectView.as_view(), name='consoleserverport_bulk_disconnect'),
# Power ports
url(r'^devices/power-ports/add/$', views.DeviceBulkAddPowerPortView.as_view(), name='device_bulk_add_powerport'),
url(r'^devices/(?P<pk>\d+)/power-ports/add/$', views.PowerPortCreateView.as_view(), name='powerport_add'),
url(r'^devices/(?P<pk>\d+)/power-ports/delete/$', views.PowerPortBulkDeleteView.as_view(), name='powerport_bulk_delete'),
url(r'^power-ports/(?P<termination_a_id>\d+)/connect/$', views.CableCreateView.as_view(), name='powerport_connect', kwargs={'termination_a_type': PowerPort}),
url(r'^power-ports/(?P<pk>\d+)/edit/$', views.PowerPortEditView.as_view(), name='powerport_edit'),
url(r'^power-ports/(?P<pk>\d+)/delete/$', views.PowerPortDeleteView.as_view(), name='powerport_delete'),
url(r'^power-ports/(?P<pk>\d+)/trace/$', views.CableTraceView.as_view(), name='powerport_trace', kwargs={'model': PowerPort}),
path(r'devices/power-ports/add/', views.DeviceBulkAddPowerPortView.as_view(), name='device_bulk_add_powerport'),
path(r'devices/<int:pk>/power-ports/add/', views.PowerPortCreateView.as_view(), name='powerport_add'),
path(r'devices/<int:pk>/power-ports/delete/', views.PowerPortBulkDeleteView.as_view(), name='powerport_bulk_delete'),
path(r'power-ports/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='powerport_connect', kwargs={'termination_a_type': PowerPort}),
path(r'power-ports/<int:pk>/edit/', views.PowerPortEditView.as_view(), name='powerport_edit'),
path(r'power-ports/<int:pk>/delete/', views.PowerPortDeleteView.as_view(), name='powerport_delete'),
path(r'power-ports/<int:pk>/trace/', views.CableTraceView.as_view(), name='powerport_trace', kwargs={'model': PowerPort}),
# Power outlets
url(r'^devices/power-outlets/add/$', views.DeviceBulkAddPowerOutletView.as_view(), name='device_bulk_add_poweroutlet'),
url(r'^devices/(?P<pk>\d+)/power-outlets/add/$', views.PowerOutletCreateView.as_view(), name='poweroutlet_add'),
url(r'^devices/(?P<pk>\d+)/power-outlets/delete/$', views.PowerOutletBulkDeleteView.as_view(), name='poweroutlet_bulk_delete'),
url(r'^power-outlets/(?P<termination_a_id>\d+)/connect/$', views.CableCreateView.as_view(), name='poweroutlet_connect', kwargs={'termination_a_type': PowerOutlet}),
url(r'^power-outlets/(?P<pk>\d+)/edit/$', views.PowerOutletEditView.as_view(), name='poweroutlet_edit'),
url(r'^power-outlets/(?P<pk>\d+)/delete/$', views.PowerOutletDeleteView.as_view(), name='poweroutlet_delete'),
url(r'^power-outlets/(?P<pk>\d+)/trace/$', views.CableTraceView.as_view(), name='poweroutlet_trace', kwargs={'model': PowerOutlet}),
url(r'^power-outlets/rename/$', views.PowerOutletBulkRenameView.as_view(), name='poweroutlet_bulk_rename'),
url(r'^power-outlets/disconnect/$', views.PowerOutletBulkDisconnectView.as_view(), name='poweroutlet_bulk_disconnect'),
path(r'devices/power-outlets/add/', views.DeviceBulkAddPowerOutletView.as_view(), name='device_bulk_add_poweroutlet'),
path(r'devices/<int:pk>/power-outlets/add/', views.PowerOutletCreateView.as_view(), name='poweroutlet_add'),
path(r'devices/<int:pk>/power-outlets/edit/', views.PowerOutletBulkEditView.as_view(), name='poweroutlet_bulk_edit'),
path(r'devices/<int:pk>/power-outlets/delete/', views.PowerOutletBulkDeleteView.as_view(), name='poweroutlet_bulk_delete'),
path(r'power-outlets/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='poweroutlet_connect', kwargs={'termination_a_type': PowerOutlet}),
path(r'power-outlets/<int:pk>/edit/', views.PowerOutletEditView.as_view(), name='poweroutlet_edit'),
path(r'power-outlets/<int:pk>/delete/', views.PowerOutletDeleteView.as_view(), name='poweroutlet_delete'),
path(r'power-outlets/<int:pk>/trace/', views.CableTraceView.as_view(), name='poweroutlet_trace', kwargs={'model': PowerOutlet}),
path(r'power-outlets/rename/', views.PowerOutletBulkRenameView.as_view(), name='poweroutlet_bulk_rename'),
path(r'power-outlets/disconnect/', views.PowerOutletBulkDisconnectView.as_view(), name='poweroutlet_bulk_disconnect'),
# Interfaces
url(r'^devices/interfaces/add/$', views.DeviceBulkAddInterfaceView.as_view(), name='device_bulk_add_interface'),
url(r'^devices/(?P<pk>\d+)/interfaces/add/$', views.InterfaceCreateView.as_view(), name='interface_add'),
url(r'^devices/(?P<pk>\d+)/interfaces/edit/$', views.InterfaceBulkEditView.as_view(), name='interface_bulk_edit'),
url(r'^devices/(?P<pk>\d+)/interfaces/delete/$', views.InterfaceBulkDeleteView.as_view(), name='interface_bulk_delete'),
url(r'^interfaces/(?P<termination_a_id>\d+)/connect/$', views.CableCreateView.as_view(), name='interface_connect', kwargs={'termination_a_type': Interface}),
url(r'^interfaces/(?P<pk>\d+)/$', views.InterfaceView.as_view(), name='interface'),
url(r'^interfaces/(?P<pk>\d+)/edit/$', views.InterfaceEditView.as_view(), name='interface_edit'),
url(r'^interfaces/(?P<pk>\d+)/assign-vlans/$', views.InterfaceAssignVLANsView.as_view(), name='interface_assign_vlans'),
url(r'^interfaces/(?P<pk>\d+)/delete/$', views.InterfaceDeleteView.as_view(), name='interface_delete'),
url(r'^interfaces/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='interface_changelog', kwargs={'model': Interface}),
url(r'^interfaces/(?P<pk>\d+)/trace/$', views.CableTraceView.as_view(), name='interface_trace', kwargs={'model': Interface}),
url(r'^interfaces/rename/$', views.InterfaceBulkRenameView.as_view(), name='interface_bulk_rename'),
url(r'^interfaces/disconnect/$', views.InterfaceBulkDisconnectView.as_view(), name='interface_bulk_disconnect'),
path(r'devices/interfaces/add/', views.DeviceBulkAddInterfaceView.as_view(), name='device_bulk_add_interface'),
path(r'devices/<int:pk>/interfaces/add/', views.InterfaceCreateView.as_view(), name='interface_add'),
path(r'devices/<int:pk>/interfaces/edit/', views.InterfaceBulkEditView.as_view(), name='interface_bulk_edit'),
path(r'devices/<int:pk>/interfaces/delete/', views.InterfaceBulkDeleteView.as_view(), name='interface_bulk_delete'),
path(r'interfaces/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='interface_connect', kwargs={'termination_a_type': Interface}),
path(r'interfaces/<int:pk>/', views.InterfaceView.as_view(), name='interface'),
path(r'interfaces/<int:pk>/edit/', views.InterfaceEditView.as_view(), name='interface_edit'),
path(r'interfaces/<int:pk>/delete/', views.InterfaceDeleteView.as_view(), name='interface_delete'),
path(r'interfaces/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='interface_changelog', kwargs={'model': Interface}),
path(r'interfaces/<int:pk>/trace/', views.CableTraceView.as_view(), name='interface_trace', kwargs={'model': Interface}),
path(r'interfaces/rename/', views.InterfaceBulkRenameView.as_view(), name='interface_bulk_rename'),
path(r'interfaces/disconnect/', views.InterfaceBulkDisconnectView.as_view(), name='interface_bulk_disconnect'),
# Front ports
# url(r'^devices/front-ports/add/$', views.DeviceBulkAddFrontPortView.as_view(), name='device_bulk_add_frontport'),
url(r'^devices/(?P<pk>\d+)/front-ports/add/$', views.FrontPortCreateView.as_view(), name='frontport_add'),
url(r'^devices/(?P<pk>\d+)/front-ports/edit/$', views.FrontPortBulkEditView.as_view(), name='frontport_bulk_edit'),
url(r'^devices/(?P<pk>\d+)/front-ports/delete/$', views.FrontPortBulkDeleteView.as_view(), name='frontport_bulk_delete'),
url(r'^front-ports/(?P<termination_a_id>\d+)/connect/$', views.CableCreateView.as_view(), name='frontport_connect', kwargs={'termination_a_type': FrontPort}),
url(r'^front-ports/(?P<pk>\d+)/edit/$', views.FrontPortEditView.as_view(), name='frontport_edit'),
url(r'^front-ports/(?P<pk>\d+)/delete/$', views.FrontPortDeleteView.as_view(), name='frontport_delete'),
url(r'^front-ports/(?P<pk>\d+)/trace/$', views.CableTraceView.as_view(), name='frontport_trace', kwargs={'model': FrontPort}),
url(r'^front-ports/rename/$', views.FrontPortBulkRenameView.as_view(), name='frontport_bulk_rename'),
url(r'^front-ports/disconnect/$', views.FrontPortBulkDisconnectView.as_view(), name='frontport_bulk_disconnect'),
# path(r'devices/front-ports/add/', views.DeviceBulkAddFrontPortView.as_view(), name='device_bulk_add_frontport'),
path(r'devices/<int:pk>/front-ports/add/', views.FrontPortCreateView.as_view(), name='frontport_add'),
path(r'devices/<int:pk>/front-ports/edit/', views.FrontPortBulkEditView.as_view(), name='frontport_bulk_edit'),
path(r'devices/<int:pk>/front-ports/delete/', views.FrontPortBulkDeleteView.as_view(), name='frontport_bulk_delete'),
path(r'front-ports/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='frontport_connect', kwargs={'termination_a_type': FrontPort}),
path(r'front-ports/<int:pk>/edit/', views.FrontPortEditView.as_view(), name='frontport_edit'),
path(r'front-ports/<int:pk>/delete/', views.FrontPortDeleteView.as_view(), name='frontport_delete'),
path(r'front-ports/<int:pk>/trace/', views.CableTraceView.as_view(), name='frontport_trace', kwargs={'model': FrontPort}),
path(r'front-ports/rename/', views.FrontPortBulkRenameView.as_view(), name='frontport_bulk_rename'),
path(r'front-ports/disconnect/', views.FrontPortBulkDisconnectView.as_view(), name='frontport_bulk_disconnect'),
# Rear ports
# url(r'^devices/rear-ports/add/$', views.DeviceBulkAddRearPortView.as_view(), name='device_bulk_add_rearport'),
url(r'^devices/(?P<pk>\d+)/rear-ports/add/$', views.RearPortCreateView.as_view(), name='rearport_add'),
url(r'^devices/(?P<pk>\d+)/rear-ports/edit/$', views.RearPortBulkEditView.as_view(), name='rearport_bulk_edit'),
url(r'^devices/(?P<pk>\d+)/rear-ports/delete/$', views.RearPortBulkDeleteView.as_view(), name='rearport_bulk_delete'),
url(r'^rear-ports/(?P<termination_a_id>\d+)/connect/$', views.CableCreateView.as_view(), name='rearport_connect', kwargs={'termination_a_type': RearPort}),
url(r'^rear-ports/(?P<pk>\d+)/edit/$', views.RearPortEditView.as_view(), name='rearport_edit'),
url(r'^rear-ports/(?P<pk>\d+)/delete/$', views.RearPortDeleteView.as_view(), name='rearport_delete'),
url(r'^rear-ports/(?P<pk>\d+)/trace/$', views.CableTraceView.as_view(), name='rearport_trace', kwargs={'model': RearPort}),
url(r'^rear-ports/rename/$', views.RearPortBulkRenameView.as_view(), name='rearport_bulk_rename'),
url(r'^rear-ports/disconnect/$', views.RearPortBulkDisconnectView.as_view(), name='rearport_bulk_disconnect'),
# path(r'devices/rear-ports/add/', views.DeviceBulkAddRearPortView.as_view(), name='device_bulk_add_rearport'),
path(r'devices/<int:pk>/rear-ports/add/', views.RearPortCreateView.as_view(), name='rearport_add'),
path(r'devices/<int:pk>/rear-ports/edit/', views.RearPortBulkEditView.as_view(), name='rearport_bulk_edit'),
path(r'devices/<int:pk>/rear-ports/delete/', views.RearPortBulkDeleteView.as_view(), name='rearport_bulk_delete'),
path(r'rear-ports/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='rearport_connect', kwargs={'termination_a_type': RearPort}),
path(r'rear-ports/<int:pk>/edit/', views.RearPortEditView.as_view(), name='rearport_edit'),
path(r'rear-ports/<int:pk>/delete/', views.RearPortDeleteView.as_view(), name='rearport_delete'),
path(r'rear-ports/<int:pk>/trace/', views.CableTraceView.as_view(), name='rearport_trace', kwargs={'model': RearPort}),
path(r'rear-ports/rename/', views.RearPortBulkRenameView.as_view(), name='rearport_bulk_rename'),
path(r'rear-ports/disconnect/', views.RearPortBulkDisconnectView.as_view(), name='rearport_bulk_disconnect'),
# Device bays
url(r'^devices/device-bays/add/$', views.DeviceBulkAddDeviceBayView.as_view(), name='device_bulk_add_devicebay'),
url(r'^devices/(?P<pk>\d+)/bays/add/$', views.DeviceBayCreateView.as_view(), name='devicebay_add'),
url(r'^devices/(?P<pk>\d+)/bays/delete/$', views.DeviceBayBulkDeleteView.as_view(), name='devicebay_bulk_delete'),
url(r'^device-bays/(?P<pk>\d+)/edit/$', views.DeviceBayEditView.as_view(), name='devicebay_edit'),
url(r'^device-bays/(?P<pk>\d+)/delete/$', views.DeviceBayDeleteView.as_view(), name='devicebay_delete'),
url(r'^device-bays/(?P<pk>\d+)/populate/$', views.DeviceBayPopulateView.as_view(), name='devicebay_populate'),
url(r'^device-bays/(?P<pk>\d+)/depopulate/$', views.DeviceBayDepopulateView.as_view(), name='devicebay_depopulate'),
url(r'^device-bays/rename/$', views.DeviceBayBulkRenameView.as_view(), name='devicebay_bulk_rename'),
path(r'devices/device-bays/add/', views.DeviceBulkAddDeviceBayView.as_view(), name='device_bulk_add_devicebay'),
path(r'devices/<int:pk>/bays/add/', views.DeviceBayCreateView.as_view(), name='devicebay_add'),
path(r'devices/<int:pk>/bays/delete/', views.DeviceBayBulkDeleteView.as_view(), name='devicebay_bulk_delete'),
path(r'device-bays/<int:pk>/edit/', views.DeviceBayEditView.as_view(), name='devicebay_edit'),
path(r'device-bays/<int:pk>/delete/', views.DeviceBayDeleteView.as_view(), name='devicebay_delete'),
path(r'device-bays/<int:pk>/populate/', views.DeviceBayPopulateView.as_view(), name='devicebay_populate'),
path(r'device-bays/<int:pk>/depopulate/', views.DeviceBayDepopulateView.as_view(), name='devicebay_depopulate'),
path(r'device-bays/rename/', views.DeviceBayBulkRenameView.as_view(), name='devicebay_bulk_rename'),
# Inventory items
url(r'^inventory-items/$', views.InventoryItemListView.as_view(), name='inventoryitem_list'),
url(r'^inventory-items/import/$', views.InventoryItemBulkImportView.as_view(), name='inventoryitem_import'),
url(r'^inventory-items/edit/$', views.InventoryItemBulkEditView.as_view(), name='inventoryitem_bulk_edit'),
url(r'^inventory-items/delete/$', views.InventoryItemBulkDeleteView.as_view(), name='inventoryitem_bulk_delete'),
url(r'^inventory-items/(?P<pk>\d+)/edit/$', views.InventoryItemEditView.as_view(), name='inventoryitem_edit'),
url(r'^inventory-items/(?P<pk>\d+)/delete/$', views.InventoryItemDeleteView.as_view(), name='inventoryitem_delete'),
url(r'^devices/(?P<device>\d+)/inventory-items/add/$', views.InventoryItemEditView.as_view(), name='inventoryitem_add'),
path(r'inventory-items/', views.InventoryItemListView.as_view(), name='inventoryitem_list'),
path(r'inventory-items/import/', views.InventoryItemBulkImportView.as_view(), name='inventoryitem_import'),
path(r'inventory-items/edit/', views.InventoryItemBulkEditView.as_view(), name='inventoryitem_bulk_edit'),
path(r'inventory-items/delete/', views.InventoryItemBulkDeleteView.as_view(), name='inventoryitem_bulk_delete'),
path(r'inventory-items/<int:pk>/edit/', views.InventoryItemEditView.as_view(), name='inventoryitem_edit'),
path(r'inventory-items/<int:pk>/delete/', views.InventoryItemDeleteView.as_view(), name='inventoryitem_delete'),
path(r'devices/<int:device>/inventory-items/add/', views.InventoryItemEditView.as_view(), name='inventoryitem_add'),
# Cables
url(r'^cables/$', views.CableListView.as_view(), name='cable_list'),
url(r'^cables/import/$', views.CableBulkImportView.as_view(), name='cable_import'),
url(r'^cables/edit/$', views.CableBulkEditView.as_view(), name='cable_bulk_edit'),
url(r'^cables/delete/$', views.CableBulkDeleteView.as_view(), name='cable_bulk_delete'),
url(r'^cables/(?P<pk>\d+)/$', views.CableView.as_view(), name='cable'),
url(r'^cables/(?P<pk>\d+)/edit/$', views.CableEditView.as_view(), name='cable_edit'),
url(r'^cables/(?P<pk>\d+)/delete/$', views.CableDeleteView.as_view(), name='cable_delete'),
url(r'^cables/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='cable_changelog', kwargs={'model': Cable}),
path(r'cables/', views.CableListView.as_view(), name='cable_list'),
path(r'cables/import/', views.CableBulkImportView.as_view(), name='cable_import'),
path(r'cables/edit/', views.CableBulkEditView.as_view(), name='cable_bulk_edit'),
path(r'cables/delete/', views.CableBulkDeleteView.as_view(), name='cable_bulk_delete'),
path(r'cables/<int:pk>/', views.CableView.as_view(), name='cable'),
path(r'cables/<int:pk>/edit/', views.CableEditView.as_view(), name='cable_edit'),
path(r'cables/<int:pk>/delete/', views.CableDeleteView.as_view(), name='cable_delete'),
path(r'cables/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='cable_changelog', kwargs={'model': Cable}),
# Console/power/interface connections (read-only)
url(r'^console-connections/$', views.ConsoleConnectionsListView.as_view(), name='console_connections_list'),
url(r'^power-connections/$', views.PowerConnectionsListView.as_view(), name='power_connections_list'),
url(r'^interface-connections/$', views.InterfaceConnectionsListView.as_view(), name='interface_connections_list'),
path(r'console-connections/', views.ConsoleConnectionsListView.as_view(), name='console_connections_list'),
path(r'power-connections/', views.PowerConnectionsListView.as_view(), name='power_connections_list'),
path(r'interface-connections/', views.InterfaceConnectionsListView.as_view(), name='interface_connections_list'),
# Virtual chassis
url(r'^virtual-chassis/$', views.VirtualChassisListView.as_view(), name='virtualchassis_list'),
url(r'^virtual-chassis/add/$', views.VirtualChassisCreateView.as_view(), name='virtualchassis_add'),
url(r'^virtual-chassis/(?P<pk>\d+)/edit/$', views.VirtualChassisEditView.as_view(), name='virtualchassis_edit'),
url(r'^virtual-chassis/(?P<pk>\d+)/delete/$', views.VirtualChassisDeleteView.as_view(), name='virtualchassis_delete'),
url(r'^virtual-chassis/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='virtualchassis_changelog', kwargs={'model': VirtualChassis}),
url(r'^virtual-chassis/(?P<pk>\d+)/add-member/$', views.VirtualChassisAddMemberView.as_view(), name='virtualchassis_add_member'),
url(r'^virtual-chassis-members/(?P<pk>\d+)/delete/$', views.VirtualChassisRemoveMemberView.as_view(), name='virtualchassis_remove_member'),
path(r'virtual-chassis/', views.VirtualChassisListView.as_view(), name='virtualchassis_list'),
path(r'virtual-chassis/add/', views.VirtualChassisCreateView.as_view(), name='virtualchassis_add'),
path(r'virtual-chassis/<int:pk>/edit/', views.VirtualChassisEditView.as_view(), name='virtualchassis_edit'),
path(r'virtual-chassis/<int:pk>/delete/', views.VirtualChassisDeleteView.as_view(), name='virtualchassis_delete'),
path(r'virtual-chassis/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='virtualchassis_changelog', kwargs={'model': VirtualChassis}),
path(r'virtual-chassis/<int:pk>/add-member/', views.VirtualChassisAddMemberView.as_view(), name='virtualchassis_add_member'),
path(r'virtual-chassis-members/<int:pk>/delete/', views.VirtualChassisRemoveMemberView.as_view(), name='virtualchassis_remove_member'),
# Power panels
path(r'power-panels/', views.PowerPanelListView.as_view(), name='powerpanel_list'),
path(r'power-panels/add/', views.PowerPanelCreateView.as_view(), name='powerpanel_add'),
path(r'power-panels/import/', views.PowerPanelBulkImportView.as_view(), name='powerpanel_import'),
path(r'power-panels/delete/', views.PowerPanelBulkDeleteView.as_view(), name='powerpanel_bulk_delete'),
path(r'power-panels/<int:pk>/', views.PowerPanelView.as_view(), name='powerpanel'),
path(r'power-panels/<int:pk>/edit/', views.PowerPanelEditView.as_view(), name='powerpanel_edit'),
path(r'power-panels/<int:pk>/delete/', views.PowerPanelDeleteView.as_view(), name='powerpanel_delete'),
path(r'power-panels/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='powerpanel_changelog', kwargs={'model': PowerPanel}),
# Power feeds
path(r'power-feeds/', views.PowerFeedListView.as_view(), name='powerfeed_list'),
path(r'power-feeds/add/', views.PowerFeedEditView.as_view(), name='powerfeed_add'),
path(r'power-feeds/import/', views.PowerFeedBulkImportView.as_view(), name='powerfeed_import'),
path(r'power-feeds/edit/', views.PowerFeedBulkEditView.as_view(), name='powerfeed_bulk_edit'),
path(r'power-feeds/delete/', views.PowerFeedBulkDeleteView.as_view(), name='powerfeed_bulk_delete'),
path(r'power-feeds/<int:pk>/', views.PowerFeedView.as_view(), name='powerfeed'),
path(r'power-feeds/<int:pk>/edit/', views.PowerFeedEditView.as_view(), name='powerfeed_edit'),
path(r'power-feeds/<int:pk>/delete/', views.PowerFeedDeleteView.as_view(), name='powerfeed_delete'),
path(r'power-feeds/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='powerfeed_changelog', kwargs={'model': PowerFeed}),
]

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@ from django.contrib import admin
from netbox.admin import admin_site
from utilities.forms import LaxURLField
from .models import CustomField, CustomFieldChoice, Graph, ExportTemplate, TopologyMap, Webhook
from .models import CustomField, CustomFieldChoice, CustomLink, Graph, ExportTemplate, TopologyMap, Webhook
def order_content_types(field):
@@ -40,6 +40,9 @@ class WebhookAdmin(admin.ModelAdmin):
'name', 'models', 'payload_url', 'http_content_type', 'enabled', 'type_create', 'type_update',
'type_delete', 'ssl_verification',
]
list_filter = [
'enabled', 'type_create', 'type_update', 'type_delete', 'obj_type',
]
form = WebhookForm
def models(self, obj):
@@ -70,20 +73,68 @@ class CustomFieldChoiceAdmin(admin.TabularInline):
@admin.register(CustomField, site=admin_site)
class CustomFieldAdmin(admin.ModelAdmin):
inlines = [CustomFieldChoiceAdmin]
list_display = ['name', 'models', 'type', 'required', 'filter_logic', 'default', 'weight', 'description']
list_display = [
'name', 'models', 'type', 'required', 'filter_logic', 'default', 'weight', 'description',
]
list_filter = [
'type', 'required', 'obj_type',
]
form = CustomFieldForm
def models(self, obj):
return ', '.join([ct.name for ct in obj.obj_type.all()])
#
# Custom links
#
class CustomLinkForm(forms.ModelForm):
class Meta:
model = CustomLink
exclude = []
widgets = {
'text': forms.Textarea,
'url': forms.Textarea,
}
help_texts = {
'text': 'Jinja2 template code for the link text. Reference the object as <code>{{ obj }}</code>. Links '
'which render as empty text will not be displayed.',
'url': 'Jinja2 template code for the link URL. Reference the object as <code>{{ obj }}</code>.',
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Format ContentType choices
order_content_types(self.fields['content_type'])
self.fields['content_type'].choices.insert(0, ('', '---------'))
@admin.register(CustomLink, site=admin_site)
class CustomLinkAdmin(admin.ModelAdmin):
list_display = [
'name', 'content_type', 'group_name', 'weight',
]
list_filter = [
'content_type',
]
form = CustomLinkForm
#
# Graphs
#
@admin.register(Graph, site=admin_site)
class GraphAdmin(admin.ModelAdmin):
list_display = ['name', 'type', 'weight', 'source']
list_display = [
'name', 'type', 'weight', 'source',
]
list_filter = [
'type',
]
#
@@ -106,7 +157,12 @@ class ExportTemplateForm(forms.ModelForm):
@admin.register(ExportTemplate, site=admin_site)
class ExportTemplateAdmin(admin.ModelAdmin):
list_display = ['name', 'content_type', 'description', 'mime_type', 'file_extension']
list_display = [
'name', 'content_type', 'description', 'mime_type', 'file_extension',
]
list_filter = [
'content_type',
]
form = ExportTemplateForm

View File

@@ -5,7 +5,7 @@ from django.db import transaction
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from extras.constants import CF_TYPE_BOOLEAN, CF_TYPE_DATE, CF_TYPE_INTEGER, CF_TYPE_SELECT
from extras.constants import *
from extras.models import CustomField, CustomFieldChoice, CustomFieldValue
from utilities.api import ValidatedModelSerializer
@@ -97,13 +97,13 @@ class CustomFieldModelSerializer(ValidatedModelSerializer):
def __init__(self, *args, **kwargs):
def _populate_custom_fields(instance, fields):
custom_fields = {f.name: None for f in fields}
for cfv in instance.custom_field_values.all():
if cfv.field.type == CF_TYPE_SELECT:
custom_fields[cfv.field.name] = CustomFieldChoiceSerializer(cfv.value).data
instance.custom_fields = {}
for field in fields:
value = instance.cf.get(field.name)
if field.type == CF_TYPE_SELECT and value is not None:
instance.custom_fields[field.name] = CustomFieldChoiceSerializer(value).data
else:
custom_fields[cfv.field.name] = cfv.value
instance.custom_fields = custom_fields
instance.custom_fields[field.name] = value
super().__init__(*args, **kwargs)

View File

@@ -1,6 +1,7 @@
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from drf_yasg.utils import swagger_serializer_method
from rest_framework import serializers
from taggit.models import Tag
from dcim.api.nested_serializers import (
NestedDeviceSerializer, NestedDeviceRoleSerializer, NestedPlatformSerializer, NestedRackSerializer,
@@ -10,12 +11,14 @@ from dcim.models import Device, DeviceRole, Platform, Rack, Region, Site
from extras.constants import *
from extras.models import (
ConfigContext, ExportTemplate, Graph, ImageAttachment, ObjectChange, ReportResult, TopologyMap,
Tag
)
from tenancy.api.nested_serializers import NestedTenantSerializer, NestedTenantGroupSerializer
from tenancy.models import Tenant, TenantGroup
from users.api.nested_serializers import NestedUserSerializer
from utilities.api import (
ChoiceField, ContentTypeField, get_serializer_for_model, SerializedPKRelatedField, ValidatedModelSerializer,
ChoiceField, ContentTypeField, get_serializer_for_model, SerializerNotFound, SerializedPKRelatedField,
ValidatedModelSerializer,
)
from .nested_serializers import *
@@ -53,10 +56,17 @@ class RenderedGraphSerializer(serializers.ModelSerializer):
#
class ExportTemplateSerializer(ValidatedModelSerializer):
template_language = ChoiceField(
choices=TEMPLATE_LANGUAGE_CHOICES,
default=TEMPLATE_LANGUAGE_JINJA2
)
class Meta:
model = ExportTemplate
fields = ['id', 'content_type', 'name', 'description', 'template_code', 'mime_type', 'file_extension']
fields = [
'id', 'content_type', 'name', 'description', 'template_language', 'template_code', 'mime_type',
'file_extension',
]
#
@@ -80,7 +90,7 @@ class TagSerializer(ValidatedModelSerializer):
class Meta:
model = Tag
fields = ['id', 'name', 'slug', 'tagged_items']
fields = ['id', 'name', 'slug', 'color', 'comments', 'tagged_items']
#
@@ -88,7 +98,9 @@ class TagSerializer(ValidatedModelSerializer):
#
class ImageAttachmentSerializer(ValidatedModelSerializer):
content_type = ContentTypeField()
content_type = ContentTypeField(
queryset=ContentType.objects.all()
)
parent = serializers.SerializerMethodField(read_only=True)
class Meta:
@@ -112,6 +124,7 @@ class ImageAttachmentSerializer(ValidatedModelSerializer):
return data
@swagger_serializer_method(serializer_or_field=serializers.DictField)
def get_parent(self, obj):
# Static mapping of models to their nested serializers
@@ -205,25 +218,42 @@ class ReportDetailSerializer(ReportSerializer):
#
class ObjectChangeSerializer(serializers.ModelSerializer):
user = NestedUserSerializer(read_only=True)
content_type = ContentTypeField(read_only=True)
changed_object = serializers.SerializerMethodField(read_only=True)
user = NestedUserSerializer(
read_only=True
)
action = ChoiceField(
choices=OBJECTCHANGE_ACTION_CHOICES,
read_only=True
)
changed_object_type = ContentTypeField(
read_only=True
)
changed_object = serializers.SerializerMethodField(
read_only=True
)
class Meta:
model = ObjectChange
fields = [
'id', 'time', 'user', 'user_name', 'request_id', 'action', 'content_type', 'changed_object', 'object_data',
'id', 'time', 'user', 'user_name', 'request_id', 'action', 'changed_object_type', 'changed_object_id',
'changed_object', 'object_data',
]
@swagger_serializer_method(serializer_or_field=serializers.DictField)
def get_changed_object(self, obj):
"""
Serialize a nested representation of the changed object.
"""
if obj.changed_object is None:
return None
serializer = get_serializer_for_model(obj.changed_object, prefix='Nested')
if serializer is None:
try:
serializer = get_serializer_for_model(obj.changed_object, prefix='Nested')
except SerializerNotFound:
return obj.object_repr
context = {'request': self.context['request']}
context = {
'request': self.context['request']
}
data = serializer(obj.changed_object, context=context).data
return data

View File

@@ -17,6 +17,9 @@ router.APIRootView = ExtrasRootView
# Field choices
router.register(r'_choices', views.ExtrasFieldChoicesViewSet, basename='field-choice')
# Custom field choices
router.register(r'_custom_field_choices', views.CustomFieldChoicesViewSet, base_name='custom-field-choice')
# Graphs
router.register(r'graphs', views.GraphViewSet)

View File

@@ -1,3 +1,5 @@
from collections import OrderedDict
from django.contrib.contenttypes.models import ContentType
from django.db.models import Count
from django.http import Http404, HttpResponse
@@ -6,11 +8,11 @@ from rest_framework.decorators import action
from rest_framework.exceptions import PermissionDenied
from rest_framework.response import Response
from rest_framework.viewsets import ReadOnlyModelViewSet, ViewSet
from taggit.models import Tag
from extras import filters
from extras.models import (
ConfigContext, CustomField, ExportTemplate, Graph, ImageAttachment, ObjectChange, ReportResult, TopologyMap,
ConfigContext, CustomFieldChoice, ExportTemplate, Graph, ImageAttachment, ObjectChange, ReportResult, TopologyMap,
Tag,
)
from extras.reports import get_report, get_reports
from utilities.api import FieldChoicesViewSet, IsAuthenticatedOrLoginNotRequired, ModelViewSet
@@ -23,11 +25,42 @@ from . import serializers
class ExtrasFieldChoicesViewSet(FieldChoicesViewSet):
fields = (
(CustomField, ['type']),
(ExportTemplate, ['template_language']),
(Graph, ['type']),
(ObjectChange, ['action']),
)
#
# Custom field choices
#
class CustomFieldChoicesViewSet(ViewSet):
"""
"""
permission_classes = [IsAuthenticatedOrLoginNotRequired]
def __init__(self, *args, **kwargs):
super(CustomFieldChoicesViewSet, self).__init__(*args, **kwargs)
self._fields = OrderedDict()
for cfc in CustomFieldChoice.objects.all():
self._fields.setdefault(cfc.field.name, {})
self._fields[cfc.field.name][cfc.value] = cfc.pk
def list(self, request):
return Response(self._fields)
def retrieve(self, request, pk):
if pk not in self._fields:
raise Http404
return Response(self._fields[pk])
def get_view_name(self):
return "Custom Field choices"
#
# Custom fields
#
@@ -87,7 +120,7 @@ class ExportTemplateViewSet(ModelViewSet):
#
class TopologyMapViewSet(ModelViewSet):
queryset = TopologyMap.objects.select_related('site')
queryset = TopologyMap.objects.prefetch_related('site')
serializer_class = serializers.TopologyMapSerializer
filterset_class = filters.TopologyMapFilter
@@ -115,7 +148,9 @@ class TopologyMapViewSet(ModelViewSet):
#
class TagViewSet(ModelViewSet):
queryset = Tag.objects.annotate(tagged_items=Count('taggit_taggeditem_items'))
queryset = Tag.objects.annotate(
tagged_items=Count('extras_taggeditem_items', distinct=True)
)
serializer_class = serializers.TagSerializer
filterset_class = filters.TagFilter
@@ -225,6 +260,6 @@ class ObjectChangeViewSet(ReadOnlyModelViewSet):
"""
Retrieve a list of recent changes.
"""
queryset = ObjectChange.objects.select_related('user')
queryset = ObjectChange.objects.prefetch_related('user')
serializer_class = serializers.ObjectChangeSerializer
filterset_class = filters.ObjectChangeFilter

View File

@@ -7,6 +7,9 @@ class ExtrasConfig(AppConfig):
name = "extras"
def ready(self):
import extras.signals
# Check that we can connect to the configured Redis database if webhooks are enabled.
if settings.WEBHOOKS_ENABLED:
try:
@@ -22,6 +25,7 @@ class ExtrasConfig(AppConfig):
port=settings.REDIS_PORT,
db=settings.REDIS_DATABASE,
password=settings.REDIS_PASSWORD or None,
ssl=settings.REDIS_SSL,
)
rs.ping()
except redis.exceptions.ConnectionError:

View File

@@ -1,13 +1,23 @@
# Models which support custom fields
CUSTOMFIELD_MODELS = (
'provider', 'circuit', # Circuits
'site', 'rack', 'devicetype', 'device', # DCIM
'aggregate', 'prefix', 'ipaddress', 'vlan', 'vrf', 'service', # IPAM
'secret', # Secrets
'tenant', # Tenancy
'cluster', 'virtualmachine', # Virtualization
)
CUSTOMFIELD_MODELS = [
'circuits.circuit',
'circuits.provider',
'dcim.device',
'dcim.devicetype',
'dcim.powerfeed',
'dcim.rack',
'dcim.site',
'ipam.aggregate',
'ipam.ipaddress',
'ipam.prefix',
'ipam.service',
'ipam.vlan',
'ipam.vrf',
'secrets.secret',
'tenancy.tenant',
'virtualization.cluster',
'virtualization.virtualmachine',
]
# Custom field types
CF_TYPE_TEXT = 100
@@ -35,27 +45,97 @@ CF_FILTER_CHOICES = (
(CF_FILTER_EXACT, 'Exact'),
)
# Custom links
CUSTOMLINK_MODELS = [
'circuits.circuit',
'circuits.provider',
'dcim.cable',
'dcim.device',
'dcim.devicetype',
'dcim.powerpanel',
'dcim.powerfeed',
'dcim.rack',
'dcim.site',
'ipam.aggregate',
'ipam.ipaddress',
'ipam.prefix',
'ipam.service',
'ipam.vlan',
'ipam.vrf',
'secrets.secret',
'tenancy.tenant',
'virtualization.cluster',
'virtualization.virtualmachine',
]
BUTTON_CLASS_DEFAULT = 'default'
BUTTON_CLASS_PRIMARY = 'primary'
BUTTON_CLASS_SUCCESS = 'success'
BUTTON_CLASS_INFO = 'info'
BUTTON_CLASS_WARNING = 'warning'
BUTTON_CLASS_DANGER = 'danger'
BUTTON_CLASS_LINK = 'link'
BUTTON_CLASS_CHOICES = (
(BUTTON_CLASS_DEFAULT, 'Default'),
(BUTTON_CLASS_PRIMARY, 'Primary (blue)'),
(BUTTON_CLASS_SUCCESS, 'Success (green)'),
(BUTTON_CLASS_INFO, 'Info (aqua)'),
(BUTTON_CLASS_WARNING, 'Warning (orange)'),
(BUTTON_CLASS_DANGER, 'Danger (red)'),
(BUTTON_CLASS_LINK, 'None (link)'),
)
# Graph types
GRAPH_TYPE_INTERFACE = 100
GRAPH_TYPE_DEVICE = 150
GRAPH_TYPE_PROVIDER = 200
GRAPH_TYPE_SITE = 300
GRAPH_TYPE_CHOICES = (
(GRAPH_TYPE_INTERFACE, 'Interface'),
(GRAPH_TYPE_DEVICE, 'Device'),
(GRAPH_TYPE_PROVIDER, 'Provider'),
(GRAPH_TYPE_SITE, 'Site'),
)
# Models which support export templates
EXPORTTEMPLATE_MODELS = [
'provider', 'circuit', # Circuits
'site', 'region', 'rack', 'rackgroup', 'manufacturer', 'devicetype', 'device', # DCIM
'consoleport', 'powerport', 'interface', 'cable', 'virtualchassis', # DCIM
'aggregate', 'prefix', 'ipaddress', 'vlan', 'vrf', 'service', # IPAM
'secret', # Secrets
'tenant', # Tenancy
'cluster', 'virtualmachine', # Virtualization
'circuits.circuit',
'circuits.provider',
'dcim.cable',
'dcim.consoleport',
'dcim.device',
'dcim.devicetype',
'dcim.interface',
'dcim.inventoryitem',
'dcim.manufacturer',
'dcim.powerpanel',
'dcim.powerport',
'dcim.powerfeed',
'dcim.rack',
'dcim.rackgroup',
'dcim.region',
'dcim.site',
'dcim.virtualchassis',
'ipam.aggregate',
'ipam.ipaddress',
'ipam.prefix',
'ipam.service',
'ipam.vlan',
'ipam.vrf',
'secrets.secret',
'tenancy.tenant',
'virtualization.cluster',
'virtualization.virtualmachine',
]
# ExportTemplate language choices
TEMPLATE_LANGUAGE_DJANGO = 10
TEMPLATE_LANGUAGE_JINJA2 = 20
TEMPLATE_LANGUAGE_CHOICES = (
(TEMPLATE_LANGUAGE_DJANGO, 'Django'),
(TEMPLATE_LANGUAGE_JINJA2, 'Jinja2'),
)
# Topology map types
TOPOLOGYMAP_TYPE_NETWORK = 1
TOPOLOGYMAP_TYPE_CONSOLE = 2
@@ -117,13 +197,36 @@ WEBHOOK_CT_CHOICES = (
)
# Models which support registered webhooks
WEBHOOK_MODELS = (
'provider', 'circuit', # Circuits
'site', 'rack', 'devicetype', 'device', 'virtualchassis', # DCIM
'consoleport', 'consoleserverport', 'powerport', 'poweroutlet',
'interface', 'devicebay', 'inventoryitem',
'aggregate', 'prefix', 'ipaddress', 'vlan', 'vrf', 'service', # IPAM
'secret', # Secrets
'tenant', # Tenancy
'cluster', 'virtualmachine', # Virtualization
)
WEBHOOK_MODELS = [
'circuits.circuit',
'circuits.provider',
'dcim.cable',
'dcim.consoleport',
'dcim.consoleserverport',
'dcim.device',
'dcim.devicebay',
'dcim.devicetype',
'dcim.interface',
'dcim.inventoryitem',
'dcim.frontport',
'dcim.manufacturer',
'dcim.poweroutlet',
'dcim.powerpanel',
'dcim.powerport',
'dcim.powerfeed',
'dcim.rack',
'dcim.rearport',
'dcim.region',
'dcim.site',
'dcim.virtualchassis',
'ipam.aggregate',
'ipam.ipaddress',
'ipam.prefix',
'ipam.service',
'ipam.vlan',
'ipam.vrf',
'secrets.secret',
'tenancy.tenant',
'virtualization.cluster',
'virtualization.virtualmachine',
]

View File

@@ -1,12 +1,11 @@
import django_filters
from django.contrib.contenttypes.models import ContentType
from django.db.models import Q
from taggit.models import Tag
from dcim.models import DeviceRole, Platform, Region, Site
from tenancy.models import Tenant, TenantGroup
from .constants import CF_FILTER_DISABLED, CF_FILTER_EXACT, CF_TYPE_BOOLEAN, CF_TYPE_SELECT
from .models import ConfigContext, CustomField, Graph, ExportTemplate, ObjectChange, TopologyMap
from .constants import *
from .models import ConfigContext, CustomField, Graph, ExportTemplate, ObjectChange, Tag, TopologyMap
class CustomFieldFilter(django_filters.Filter):
@@ -82,7 +81,7 @@ class ExportTemplateFilter(django_filters.FilterSet):
class Meta:
model = ExportTemplate
fields = ['content_type', 'name']
fields = ['content_type', 'name', 'template_language']
class TagFilter(django_filters.FilterSet):
@@ -208,6 +207,20 @@ class ConfigContextFilter(django_filters.FilterSet):
)
#
# Filter for Local Config Context Data
#
class LocalConfigContextFilter(django_filters.FilterSet):
local_context_data = django_filters.BooleanFilter(
method='_local_context_data',
label='Has local config context data',
)
def _local_context_data(self, queryset, name, value):
return queryset.exclude(local_context_data__isnull=value)
class ObjectChangeFilter(django_filters.FilterSet):
q = django_filters.CharFilter(
method='search',
@@ -217,7 +230,9 @@ class ObjectChangeFilter(django_filters.FilterSet):
class Meta:
model = ObjectChange
fields = ['user', 'user_name', 'request_id', 'action', 'changed_object_type', 'object_repr']
fields = [
'user', 'user_name', 'request_id', 'action', 'changed_object_type', 'changed_object_id', 'object_repr',
]
def search(self, queryset, name, value):
if not value.strip():

View File

@@ -4,21 +4,17 @@ from django import forms
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from mptt.forms import TreeNodeMultipleChoiceField
from taggit.forms import TagField
from taggit.models import Tag
from dcim.models import DeviceRole, Platform, Region, Site
from tenancy.models import Tenant, TenantGroup
from utilities.forms import (
add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ContentTypeSelect,
FilterChoiceField, FilterTreeNodeMultipleChoiceField, LaxURLField, JSONField, SlugField,
add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorSelect,
CommentField, ContentTypeSelect, FilterChoiceField, LaxURLField, JSONField, SlugField, StaticSelect2,
BOOLEAN_WITH_BLANK_CHOICES,
)
from .constants import (
CF_FILTER_DISABLED, CF_TYPE_BOOLEAN, CF_TYPE_DATE, CF_TYPE_INTEGER, CF_TYPE_SELECT, CF_TYPE_URL,
OBJECTCHANGE_ACTION_CHOICES,
)
from .models import ConfigContext, CustomField, CustomFieldValue, ImageAttachment, ObjectChange
from .constants import *
from .models import ConfigContext, CustomField, CustomFieldValue, ImageAttachment, ObjectChange, Tag
#
@@ -113,8 +109,10 @@ class CustomFieldForm(forms.ModelForm):
# If editing an existing object, initialize values for all custom fields
if self.instance.pk:
existing_values = CustomFieldValue.objects.filter(obj_type=self.obj_type, obj_id=self.instance.pk)\
.select_related('field')
existing_values = CustomFieldValue.objects.filter(
obj_type=self.obj_type,
obj_id=self.instance.pk
).prefetch_related('field')
for cfv in existing_values:
self.initial['cf_{}'.format(str(cfv.field.name))] = cfv.serialized_value
@@ -122,9 +120,11 @@ class CustomFieldForm(forms.ModelForm):
for field_name in self.custom_fields:
try:
cfv = CustomFieldValue.objects.select_related('field').get(field=self.fields[field_name].model,
obj_type=self.obj_type,
obj_id=self.instance.pk)
cfv = CustomFieldValue.objects.prefetch_related('field').get(
field=self.fields[field_name].model,
obj_type=self.obj_type,
obj_id=self.instance.pk
)
except CustomFieldValue.DoesNotExist:
# Skip this field if none exists already and its value is empty
if self.cleaned_data[field_name] in [None, '']:
@@ -190,11 +190,12 @@ class CustomFieldFilterForm(forms.Form):
class TagForm(BootstrapMixin, forms.ModelForm):
slug = SlugField()
comments = CommentField()
class Meta:
model = Tag
fields = [
'name', 'slug',
'name', 'slug', 'color', 'comments'
]
@@ -216,12 +217,29 @@ class TagFilterForm(BootstrapMixin, forms.Form):
)
class TagBulkEditForm(BootstrapMixin, BulkEditForm):
pk = forms.ModelMultipleChoiceField(
queryset=Tag.objects.all(),
widget=forms.MultipleHiddenInput
)
color = forms.CharField(
max_length=6,
required=False,
widget=ColorSelect()
)
class Meta:
nullable_fields = []
#
# Config contexts
#
class ConfigContextForm(BootstrapMixin, forms.ModelForm):
data = JSONField()
data = JSONField(
label=''
)
class Meta:
model = ConfigContext
@@ -330,6 +348,20 @@ class ConfigContextFilterForm(BootstrapMixin, forms.Form):
)
#
# Filter form for local config context data
#
class LocalConfigContextFilterForm(forms.Form):
local_context_data = forms.NullBooleanField(
required=False,
label='Has local config context data',
widget=StaticSelect2(
choices=BOOLEAN_WITH_BLANK_CHOICES
)
)
#
# Image attachments
#
@@ -381,3 +413,38 @@ class ObjectChangeFilterForm(BootstrapMixin, forms.Form):
widget=ContentTypeSelect(),
label='Object Type'
)
#
# Scripts
#
class ScriptForm(BootstrapMixin, forms.Form):
_commit = forms.BooleanField(
required=False,
initial=True,
label="Commit changes",
help_text="Commit changes to the database (uncheck for a dry-run)"
)
def __init__(self, vars, *args, commit_default=True, **kwargs):
super().__init__(*args, **kwargs)
# Dynamically populate fields for variables
for name, var in vars.items():
self.fields[name] = var.as_field()
# Toggle default commit behavior based on Meta option
if not commit_default:
self.fields['_commit'].initial = False
# Move _commit to the end of the form
self.fields.move_to_end('_commit', True)
@property
def requires_input(self):
"""
A boolean indicating whether the form requires user input (ignore the _commit field).
"""
return bool(len(self.fields) > 1)

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