From 34bfb899d17c0bf152da05b7d645978851b913c4 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 20 Nov 2018 11:58:19 -0500 Subject: [PATCH 01/24] Post-release version bump --- netbox/netbox/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 42451c9a2..f6fbcdf70 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -22,7 +22,7 @@ if sys.version_info[0] < 3: DeprecationWarning ) -VERSION = '2.4.8' +VERSION = '2.4.9-dev' BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) From f052bbc36e5849060aa347a263d7be56f0218a72 Mon Sep 17 00:00:00 2001 From: Tyler Bigler Date: Mon, 26 Nov 2018 14:16:37 -0500 Subject: [PATCH 02/24] Refactor Extras Migration Version Check (#2604) * Add constant for DB_MINIMUM_VERSION * Refactor verify_postgresql_version to use Django connection pg_version method for comparing versions. * Remove StrictVersion import * Remove DB_MINIMUM_VERSION as not necessary in constants. * Define DB_MINIMUM_VERSION locally to freeze to migration. * Refactor database version verification to use django builtin methods. --- ...ial_squashed_0010_customfield_filter_logic.py | 16 +++++++--------- netbox/extras/migrations/0008_reports.py | 15 +++++++-------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/netbox/extras/migrations/0001_initial_squashed_0010_customfield_filter_logic.py b/netbox/extras/migrations/0001_initial_squashed_0010_customfield_filter_logic.py index 0ac826ba4..1021c20c5 100644 --- a/netbox/extras/migrations/0001_initial_squashed_0010_customfield_filter_logic.py +++ b/netbox/extras/migrations/0001_initial_squashed_0010_customfield_filter_logic.py @@ -2,9 +2,6 @@ # Generated by Django 1.11.14 on 2018-07-31 02:19 from __future__ import unicode_literals -import re -from distutils.version import StrictVersion - from django.conf import settings import django.contrib.postgres.fields.jsonb from django.db import connection, migrations, models @@ -19,13 +16,14 @@ def verify_postgresql_version(apps, schema_editor): """ Verify that PostgreSQL is version 9.4 or higher. """ + # https://www.postgresql.org/docs/current/libpq-status.html#LIBPQ-PQSERVERVERSION + DB_MINIMUM_VERSION = 90400 # 9.4.0 + try: - with connection.cursor() as cursor: - cursor.execute("SELECT VERSION()") - row = cursor.fetchone() - pg_version = re.match(r'^PostgreSQL (\d+\.\d+(\.\d+)?)', row[0]).group(1) - if StrictVersion(pg_version) < StrictVersion('9.4.0'): - raise Exception("PostgreSQL 9.4.0 or higher is required ({} found). Upgrade PostgreSQL and then run migrations again.".format(pg_version)) + pg_version = connection.pg_version + + if pg_version < DB_MINIMUM_VERSION: + raise Exception("PostgreSQL 9.4.0 ({}) or higher is required ({} found). Upgrade PostgreSQL and then run migrations again.".format(DB_MINIMUM_VERSION, pg_version)) # Skip if the database is missing (e.g. for CI testing) or misconfigured. except OperationalError: diff --git a/netbox/extras/migrations/0008_reports.py b/netbox/extras/migrations/0008_reports.py index fbfde2cba..9c26f50ba 100644 --- a/netbox/extras/migrations/0008_reports.py +++ b/netbox/extras/migrations/0008_reports.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # Generated by Django 1.11.4 on 2017-09-26 21:25 from __future__ import unicode_literals -from distutils.version import StrictVersion -import re from django.conf import settings import django.contrib.postgres.fields.jsonb @@ -15,13 +13,14 @@ def verify_postgresql_version(apps, schema_editor): """ Verify that PostgreSQL is version 9.4 or higher. """ + # https://www.postgresql.org/docs/current/libpq-status.html#LIBPQ-PQSERVERVERSION + DB_MINIMUM_VERSION = 90400 # 9.4.0 + try: - with connection.cursor() as cursor: - cursor.execute("SELECT VERSION()") - row = cursor.fetchone() - pg_version = re.match(r'^PostgreSQL (\d+\.\d+(\.\d+)?)', row[0]).group(1) - if StrictVersion(pg_version) < StrictVersion('9.4.0'): - raise Exception("PostgreSQL 9.4.0 or higher is required ({} found). Upgrade PostgreSQL and then run migrations again.".format(pg_version)) + pg_version = connection.pg_version + + if pg_version < DB_MINIMUM_VERSION: + raise Exception("PostgreSQL 9.4.0 ({}) or higher is required ({} found). Upgrade PostgreSQL and then run migrations again.".format(DB_MINIMUM_VERSION, pg_version)) # Skip if the database is missing (e.g. for CI testing) or misconfigured. except OperationalError: From 7346083b2630b4a2b645f530d5268d0b25170dd4 Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Mon, 26 Nov 2018 13:19:05 -0600 Subject: [PATCH 03/24] Fixes #2606 - Added MultipleChoiceFilter for form_factor (#2610) * Fixes #2606 - Added MultipleChoiceFilter for form_factor * Fixes #2606 - Add MultipleChoiceField for form_factor Fixes error with too many lines. --- netbox/dcim/filters.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/netbox/dcim/filters.py b/netbox/dcim/filters.py index 8b40ca7b7..a8fb27954 100644 --- a/netbox/dcim/filters.py +++ b/netbox/dcim/filters.py @@ -13,7 +13,7 @@ from utilities.filters import NullableCharFieldFilter, NumericInFilter, TagFilte from virtualization.models import Cluster from .constants import ( DEVICE_STATUS_CHOICES, IFACE_FF_LAG, NONCONNECTABLE_IFACE_TYPES, SITE_STATUS_CHOICES, VIRTUAL_IFACE_TYPES, - WIRELESS_IFACE_TYPES, + WIRELESS_IFACE_TYPES, IFACE_FF_CHOICES, ) from .models import ( ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay, @@ -652,10 +652,14 @@ class InterfaceFilter(django_filters.FilterSet): method='filter_vlan', label='Assigned VID' ) + form_factor = django_filters.MultipleChoiceFilter( + choices=IFACE_FF_CHOICES, + null_value=None + ) class Meta: model = Interface - fields = ['name', 'form_factor', 'enabled', 'mtu', 'mgmt_only'] + fields = ['name', 'enabled', 'mtu', 'mgmt_only'] def filter_device(self, queryset, name, value): try: From 90a4b629769d106a4591c0d7415eab10980e4153 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 26 Nov 2018 14:41:09 -0500 Subject: [PATCH 04/24] Changelog for #2606 --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6560f0e68..c0fa5b244 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +v2.4.9 (FUTURE) + +## Bug Fixes + +* [#2606](https://github.com/digitalocean/netbox/issues/2606) - Fixed filtering for interfaces with a virtual form factor + +--- + v2.4.8 (2018-11-20) ## Enhancements From f3aef371631b13d7dc4ecd47bbf4ca93eca89c51 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 27 Nov 2018 10:45:10 -0500 Subject: [PATCH 05/24] Add developer guidance for the introduction of new dependencies --- docs/development/style-guide.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/development/style-guide.md b/docs/development/style-guide.md index 18dadd2d2..138d0e12d 100644 --- a/docs/development/style-guide.md +++ b/docs/development/style-guide.md @@ -28,6 +28,19 @@ To invoke `pycodestyle` manually, run: pycodestyle --ignore=W504,E501 netbox/ ``` +## Introducing New Dependencies + +The introduction of a new dependency is best avoided unless it is absolutely necessary. For small features, it's generally preferable to replicate functionality within the NetBox code base rather than to introduce reliance on an external project. This reduces both the burden of tracking new releases and our exposure to outside bugs and attacks. + +If there's a strong case for introducing a new depdency, it must meet the following criteria: + +* Its complete source code must be published and freely accessible without registration. +* Its license must be conducive to inclusion in an open source project. +* It must be actively maintained, with no longer than one year between releases. +* It must be available via the [Python Package Index](https://pypi.org/) (PyPI). + +When adding a new dependency, a short description of the package and the URL of its code repository must be added to `base_requirements.txt`. Additionally, a line specifying the package name pinned to the current stable release must be added to `requirements.txt`. This ensures that NetBox will install only the known-good release and simplify support efforts. + ## General Guidance * When in doubt, remain consistent: It is better to be consistently incorrect than inconsistently correct. If you notice in the course of unrelated work a pattern that should be corrected, continue to follow the pattern for now and open a bug so that the entire code base can be evaluated at a later point. From 5d07a5a6703e3162c4ebc701bb890f3ae4996d2b Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 27 Nov 2018 12:20:52 -0500 Subject: [PATCH 06/24] Fixes #2613: Decrease live search minimum characters to three --- CHANGELOG.md | 1 + netbox/project-static/js/livesearch.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0fa5b244..da129391f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ v2.4.9 (FUTURE) ## Bug Fixes * [#2606](https://github.com/digitalocean/netbox/issues/2606) - Fixed filtering for interfaces with a virtual form factor +* [#2613](https://github.com/digitalocean/netbox/issues/2613) - Decrease live search minimum characters to three --- diff --git a/netbox/project-static/js/livesearch.js b/netbox/project-static/js/livesearch.js index e00aefbaf..2c7ae3a17 100644 --- a/netbox/project-static/js/livesearch.js +++ b/netbox/project-static/js/livesearch.js @@ -49,7 +49,7 @@ $(document).ready(function() { // Disable parent selection fields // $('select[filter-for="' + real_field.attr('name') + '"]').val(''); }, - minLength: 4, + minLength: 3, delay: 500 }); From d2744700c682b97e948e6a2c728e077ada22ea61 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 27 Nov 2018 12:41:00 -0500 Subject: [PATCH 07/24] Fixes #2615: Tweak live search widget to use brief format for API requests --- CHANGELOG.md | 1 + netbox/project-static/js/livesearch.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index da129391f..2a4a4f24b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ v2.4.9 (FUTURE) * [#2606](https://github.com/digitalocean/netbox/issues/2606) - Fixed filtering for interfaces with a virtual form factor * [#2613](https://github.com/digitalocean/netbox/issues/2613) - Decrease live search minimum characters to three +* [#2615](https://github.com/digitalocean/netbox/issues/2615) - Tweak live search widget to use brief format for API requests --- diff --git a/netbox/project-static/js/livesearch.js b/netbox/project-static/js/livesearch.js index 2c7ae3a17..2d5afe700 100644 --- a/netbox/project-static/js/livesearch.js +++ b/netbox/project-static/js/livesearch.js @@ -24,7 +24,7 @@ $(document).ready(function() { source: function(request, response) { $.ajax({ type: 'GET', - url: search_field.attr('data-source'), + url: search_field.attr('data-source') + '?brief=1', data: search_key + '=' + request.term, success: function(data) { var choices = []; From c3cdf8e97e43338cff5581dc9b9be2814d85caa5 Mon Sep 17 00:00:00 2001 From: Tatsushi Demachi Date: Wed, 28 Nov 2018 06:14:45 +0900 Subject: [PATCH 08/24] Fix type mismatches in API view (#2429) * Fix tags field to be shown as array in API view `tags` field in serializers is defineded as `TagListSerializerField`. It should be shown as an array value in API view but actually, it is a simple string value. This fixes it by introducing a new `FieldInspector` to handle `TagListSerializerField` type field as an array. It doesn't affects any other type fields. * Fix SerializedPKRelatedField type API expression A field definded as `SerializedPKRelatedField` should be shown as an array of child serializer objects in a response value definition in API view but it is shown as an array of primary key values (usually `integer` type) of a child serializer. This fixes it by introducing a new `FieldInspector` to handle the field. It doesn't affect any other type fields. * Fix request parameter representation in API view In API view, representation of a parameter defined as a sub class of `WritableNestedSerializer` should be vary between a request and a response. For example, `tenant` field in `IPAddressSerializer` should be shown like following as a request body: ``` tenant: integer ... ``` while it should be shown like following as a response body: ``` tenant: { id: integer ..., url: string ..., name: string ..., slug: string ... } ``` But in both cases, it is shown as a response body type expression. This causes an error at sending an API request with that type value. It is only an API view issue, API can handle a request if a request parameter is structured as an expected request body by ignoring the wrong expression. This fixes the issue by replacing an implicitly used default auto schema generator class by its sub class and returning a pseudo serializer with 'Writable' prefix at generating a request body. The reason to introduce a new generator class is that there is no other point which can distinguish a request and a response. It is not enough to distinguish POST, PUT, PATCH methods from GET because former cases may return a JSON object as a response but it is also represented as same as a request body, causes another mismatch. This also fixes `SerializedPKRelatedField` type field representation. It should be shown as an array of primary keys in a request body. Fixed #2400 --- CHANGELOG.md | 1 + netbox/netbox/settings.py | 3 ++ netbox/utilities/custom_inspectors.py | 47 +++++++++++++++++++++++++-- 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a4a4f24b..468263ee8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,6 +73,7 @@ v2.4.5 (2018-10-02) ## Bug Fixes +* [#2400](https://github.com/digitalocean/netbox/issues/2400) - API variable type mismatch at creating/modifying an entry * [#2406](https://github.com/digitalocean/netbox/issues/2406) - Remove hard-coded limit of 1000 objects from API-populated form fields * [#2414](https://github.com/digitalocean/netbox/issues/2414) - Tags field missing from device/VM component creation forms * [#2442](https://github.com/digitalocean/netbox/issues/2442) - Nullify "next" link in API when limit=0 is passed diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index f6fbcdf70..7034788fb 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -275,9 +275,12 @@ RQ_QUEUES = { # drf_yasg settings for Swagger SWAGGER_SETTINGS = { + 'DEFAULT_AUTO_SCHEMA_CLASS': 'utilities.custom_inspectors.NetBoxSwaggerAutoSchema', 'DEFAULT_FIELD_INSPECTORS': [ 'utilities.custom_inspectors.NullableBooleanFieldInspector', 'utilities.custom_inspectors.CustomChoiceFieldInspector', + 'utilities.custom_inspectors.TagListFieldInspector', + 'utilities.custom_inspectors.SerializedPKRelatedFieldInspector', 'drf_yasg.inspectors.CamelCaseJSONFilter', 'drf_yasg.inspectors.ReferencingSerializerInspector', 'drf_yasg.inspectors.RelatedFieldInspector', diff --git a/netbox/utilities/custom_inspectors.py b/netbox/utilities/custom_inspectors.py index ca6e08fc1..5975788bc 100644 --- a/netbox/utilities/custom_inspectors.py +++ b/netbox/utilities/custom_inspectors.py @@ -1,9 +1,52 @@ from drf_yasg import openapi -from drf_yasg.inspectors import FieldInspector, NotHandled, PaginatorInspector, FilterInspector +from drf_yasg.inspectors import FieldInspector, NotHandled, PaginatorInspector, FilterInspector, SwaggerAutoSchema from rest_framework.fields import ChoiceField +from rest_framework.relations import ManyRelatedField +from taggit_serializer.serializers import TagListSerializerField from extras.api.customfields import CustomFieldsSerializer -from utilities.api import ChoiceField +from utilities.api import ChoiceField, SerializedPKRelatedField, WritableNestedSerializer + + +class NetBoxSwaggerAutoSchema(SwaggerAutoSchema): + def get_request_serializer(self): + serializer = super().get_request_serializer() + + if serializer is not None and self.method in self.implicit_body_methods: + properties = {} + for child_name, child in serializer.fields.items(): + if isinstance(child, (ChoiceField, WritableNestedSerializer)): + properties[child_name] = None + elif isinstance(child, ManyRelatedField) and isinstance(child.child_relation, SerializedPKRelatedField): + properties[child_name] = None + + if properties: + writable_class = type('Writable' + type(serializer).__name__, (type(serializer),), properties) + serializer = writable_class() + + return serializer + + +class SerializedPKRelatedFieldInspector(FieldInspector): + def field_to_swagger_object(self, field, swagger_object_type, use_references, **kwargs): + SwaggerType, ChildSwaggerType = self._get_partial_types(field, swagger_object_type, use_references, **kwargs) + if isinstance(field, SerializedPKRelatedField): + return self.probe_field_inspectors(field.serializer(), ChildSwaggerType, use_references) + + return NotHandled + + +class TagListFieldInspector(FieldInspector): + def field_to_swagger_object(self, field, swagger_object_type, use_references, **kwargs): + SwaggerType, ChildSwaggerType = self._get_partial_types(field, swagger_object_type, use_references, **kwargs) + if isinstance(field, TagListSerializerField): + child_schema = self.probe_field_inspectors(field.child, ChildSwaggerType, use_references) + return SwaggerType( + type=openapi.TYPE_ARRAY, + items=child_schema, + ) + + return NotHandled class CustomChoiceFieldInspector(FieldInspector): From 112aaea51fcfbd55e1c5c4dd760bfc3b058b6de5 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 27 Nov 2018 16:18:06 -0500 Subject: [PATCH 09/24] Updated changelog for #2400 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 468263ee8..9fc62af13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ v2.4.9 (FUTURE) ## Bug Fixes +* [#2400](https://github.com/digitalocean/netbox/issues/2400) - Correct representation of nested object assignment in API docs * [#2606](https://github.com/digitalocean/netbox/issues/2606) - Fixed filtering for interfaces with a virtual form factor * [#2613](https://github.com/digitalocean/netbox/issues/2613) - Decrease live search minimum characters to three * [#2615](https://github.com/digitalocean/netbox/issues/2615) - Tweak live search widget to use brief format for API requests @@ -73,7 +74,6 @@ v2.4.5 (2018-10-02) ## Bug Fixes -* [#2400](https://github.com/digitalocean/netbox/issues/2400) - API variable type mismatch at creating/modifying an entry * [#2406](https://github.com/digitalocean/netbox/issues/2406) - Remove hard-coded limit of 1000 objects from API-populated form fields * [#2414](https://github.com/digitalocean/netbox/issues/2414) - Tags field missing from device/VM component creation forms * [#2442](https://github.com/digitalocean/netbox/issues/2442) - Nullify "next" link in API when limit=0 is passed From c6d048ca51e612209655b3902217e0fcea91374f Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 27 Nov 2018 16:27:47 -0500 Subject: [PATCH 10/24] Fixes #2576: Correct type for count_* fields in site API representation --- CHANGELOG.md | 1 + netbox/dcim/api/serializers.py | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fc62af13..20d701112 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ v2.4.9 (FUTURE) ## Bug Fixes * [#2400](https://github.com/digitalocean/netbox/issues/2400) - Correct representation of nested object assignment in API docs +* [#2576](https://github.com/digitalocean/netbox/issues/2576) - Correct type for count_* fields in site API representation * [#2606](https://github.com/digitalocean/netbox/issues/2606) - Fixed filtering for interfaces with a virtual form factor * [#2613](https://github.com/digitalocean/netbox/issues/2613) - Decrease live search minimum characters to three * [#2615](https://github.com/digitalocean/netbox/issues/2615) - Tweak live search widget to use brief format for API requests diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py index b0a1628de..94d2b07a8 100644 --- a/netbox/dcim/api/serializers.py +++ b/netbox/dcim/api/serializers.py @@ -56,6 +56,11 @@ 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) class Meta: model = Site From 879d879e569b06836a54c029047f9e9257987e5b Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 28 Nov 2018 09:35:33 -0500 Subject: [PATCH 11/24] Closes #2617: Explicitly mention that test service runs on port 8000 --- docs/installation/2-netbox.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/installation/2-netbox.md b/docs/installation/2-netbox.md index ad4556383..06c8a3d5c 100644 --- a/docs/installation/2-netbox.md +++ b/docs/installation/2-netbox.md @@ -246,13 +246,13 @@ At this point, NetBox should be able to run. We can verify this by starting a de Performing system checks... System check identified no issues (0 silenced). -June 17, 2016 - 16:17:36 -Django version 1.9.7, using settings 'netbox.settings' +November 28, 2018 - 09:33:45 +Django version 2.0.9, using settings 'netbox.settings' Starting development server at http://0.0.0.0:8000/ Quit the server with CONTROL-C. ``` -Now if we navigate to the name or IP of the server (as defined in `ALLOWED_HOSTS`) we should be greeted with the NetBox home page. Note that this built-in web service is for development and testing purposes only. **It is not suited for production use.** +Next, connect to the name or IP of the server (as defined in `ALLOWED_HOSTS`) on port 8000; for example, . You should be greeted with the NetBox home page. Note that this built-in web service is for development and testing purposes only. **It is not suited for production use.** !!! warning If the test service does not run, or you cannot reach the NetBox home page, something has gone wrong. Do not proceed with the rest of this guide until the installation has been corrected. From 6c2a9107dd943c9d8c1039d89663ff8cc654a389 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 28 Nov 2018 09:56:48 -0500 Subject: [PATCH 12/24] Closes #2597: Add FibreChannel SFP28 (32GFC) interface form factor --- CHANGELOG.md | 4 ++++ netbox/dcim/constants.py | 2 ++ netbox/dcim/migrations/0062_interface_mtu.py | 4 ++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20d701112..aa0f0d2fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ v2.4.9 (FUTURE) +## Enhancements + +* [#2597](https://github.com/digitalocean/netbox/issues/2597) - Add FibreChannel SFP28 (32GFC) interface form factor + ## Bug Fixes * [#2400](https://github.com/digitalocean/netbox/issues/2400) - Correct representation of nested object assignment in API docs diff --git a/netbox/dcim/constants.py b/netbox/dcim/constants.py index c41259533..bc2ef5687 100644 --- a/netbox/dcim/constants.py +++ b/netbox/dcim/constants.py @@ -82,6 +82,7 @@ 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 # Serial IFACE_FF_T1 = 4000 IFACE_FF_E1 = 4010 @@ -154,6 +155,7 @@ IFACE_FF_CHOICES = [ [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)'], ] ], [ diff --git a/netbox/dcim/migrations/0062_interface_mtu.py b/netbox/dcim/migrations/0062_interface_mtu.py index 592f11bb7..acc47bfa0 100644 --- a/netbox/dcim/migrations/0062_interface_mtu.py +++ b/netbox/dcim/migrations/0062_interface_mtu.py @@ -19,11 +19,11 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='interface', name='form_factor', - field=models.PositiveSmallIntegerField(choices=[['Virtual interfaces', [[0, 'Virtual'], [200, 'Link Aggregation Group (LAG)']]], ['Ethernet (fixed)', [[800, '100BASE-TX (10/100ME)'], [1000, '1000BASE-T (1GE)'], [1150, '10GBASE-T (10GE)'], [1170, '10GBASE-CX4 (10GE)']]], ['Ethernet (modular)', [[1050, 'GBIC (1GE)'], [1100, 'SFP (1GE)'], [1200, 'SFP+ (10GE)'], [1300, 'XFP (10GE)'], [1310, 'XENPAK (10GE)'], [1320, 'X2 (10GE)'], [1350, 'SFP28 (25GE)'], [1400, 'QSFP+ (40GE)'], [1500, 'CFP (100GE)'], [1510, 'CFP2 (100GE)'], [1520, 'CFP4 (100GE)'], [1550, 'Cisco CPAK (100GE)'], [1600, 'QSFP28 (100GE)']]], ['Wireless', [[2600, 'IEEE 802.11a'], [2610, 'IEEE 802.11b/g'], [2620, 'IEEE 802.11n'], [2630, 'IEEE 802.11ac'], [2640, 'IEEE 802.11ad']]], ['FibreChannel', [[3010, 'SFP (1GFC)'], [3020, 'SFP (2GFC)'], [3040, 'SFP (4GFC)'], [3080, 'SFP+ (8GFC)'], [3160, 'SFP+ (16GFC)']]], ['Serial', [[4000, 'T1 (1.544 Mbps)'], [4010, 'E1 (2.048 Mbps)'], [4040, 'T3 (45 Mbps)'], [4050, 'E3 (34 Mbps)']]], ['Stacking', [[5000, 'Cisco StackWise'], [5050, 'Cisco StackWise Plus'], [5100, 'Cisco FlexStack'], [5150, 'Cisco FlexStack Plus'], [5200, 'Juniper VCP'], [5300, 'Extreme SummitStack'], [5310, 'Extreme SummitStack-128'], [5320, 'Extreme SummitStack-256'], [5330, 'Extreme SummitStack-512']]], ['Other', [[32767, 'Other']]]], default=1200), + field=models.PositiveSmallIntegerField(choices=[['Virtual interfaces', [[0, 'Virtual'], [200, 'Link Aggregation Group (LAG)']]], ['Ethernet (fixed)', [[800, '100BASE-TX (10/100ME)'], [1000, '1000BASE-T (1GE)'], [1150, '10GBASE-T (10GE)'], [1170, '10GBASE-CX4 (10GE)']]], ['Ethernet (modular)', [[1050, 'GBIC (1GE)'], [1100, 'SFP (1GE)'], [1200, 'SFP+ (10GE)'], [1300, 'XFP (10GE)'], [1310, 'XENPAK (10GE)'], [1320, 'X2 (10GE)'], [1350, 'SFP28 (25GE)'], [1400, 'QSFP+ (40GE)'], [1500, 'CFP (100GE)'], [1510, 'CFP2 (100GE)'], [1520, 'CFP4 (100GE)'], [1550, 'Cisco CPAK (100GE)'], [1600, 'QSFP28 (100GE)']]], ['Wireless', [[2600, 'IEEE 802.11a'], [2610, 'IEEE 802.11b/g'], [2620, 'IEEE 802.11n'], [2630, 'IEEE 802.11ac'], [2640, 'IEEE 802.11ad']]], ['FibreChannel', [[3010, 'SFP (1GFC)'], [3020, 'SFP (2GFC)'], [3040, 'SFP (4GFC)'], [3080, 'SFP+ (8GFC)'], [3160, 'SFP+ (16GFC)'], [3320, 'SFP28 (32GFC)']]], ['Serial', [[4000, 'T1 (1.544 Mbps)'], [4010, 'E1 (2.048 Mbps)'], [4040, 'T3 (45 Mbps)'], [4050, 'E3 (34 Mbps)']]], ['Stacking', [[5000, 'Cisco StackWise'], [5050, 'Cisco StackWise Plus'], [5100, 'Cisco FlexStack'], [5150, 'Cisco FlexStack Plus'], [5200, 'Juniper VCP'], [5300, 'Extreme SummitStack'], [5310, 'Extreme SummitStack-128'], [5320, 'Extreme SummitStack-256'], [5330, 'Extreme SummitStack-512']]], ['Other', [[32767, 'Other']]]], default=1200), ), migrations.AlterField( model_name='interfacetemplate', name='form_factor', - field=models.PositiveSmallIntegerField(choices=[['Virtual interfaces', [[0, 'Virtual'], [200, 'Link Aggregation Group (LAG)']]], ['Ethernet (fixed)', [[800, '100BASE-TX (10/100ME)'], [1000, '1000BASE-T (1GE)'], [1150, '10GBASE-T (10GE)'], [1170, '10GBASE-CX4 (10GE)']]], ['Ethernet (modular)', [[1050, 'GBIC (1GE)'], [1100, 'SFP (1GE)'], [1200, 'SFP+ (10GE)'], [1300, 'XFP (10GE)'], [1310, 'XENPAK (10GE)'], [1320, 'X2 (10GE)'], [1350, 'SFP28 (25GE)'], [1400, 'QSFP+ (40GE)'], [1500, 'CFP (100GE)'], [1510, 'CFP2 (100GE)'], [1520, 'CFP4 (100GE)'], [1550, 'Cisco CPAK (100GE)'], [1600, 'QSFP28 (100GE)']]], ['Wireless', [[2600, 'IEEE 802.11a'], [2610, 'IEEE 802.11b/g'], [2620, 'IEEE 802.11n'], [2630, 'IEEE 802.11ac'], [2640, 'IEEE 802.11ad']]], ['FibreChannel', [[3010, 'SFP (1GFC)'], [3020, 'SFP (2GFC)'], [3040, 'SFP (4GFC)'], [3080, 'SFP+ (8GFC)'], [3160, 'SFP+ (16GFC)']]], ['Serial', [[4000, 'T1 (1.544 Mbps)'], [4010, 'E1 (2.048 Mbps)'], [4040, 'T3 (45 Mbps)'], [4050, 'E3 (34 Mbps)']]], ['Stacking', [[5000, 'Cisco StackWise'], [5050, 'Cisco StackWise Plus'], [5100, 'Cisco FlexStack'], [5150, 'Cisco FlexStack Plus'], [5200, 'Juniper VCP'], [5300, 'Extreme SummitStack'], [5310, 'Extreme SummitStack-128'], [5320, 'Extreme SummitStack-256'], [5330, 'Extreme SummitStack-512']]], ['Other', [[32767, 'Other']]]], default=1200), + field=models.PositiveSmallIntegerField(choices=[['Virtual interfaces', [[0, 'Virtual'], [200, 'Link Aggregation Group (LAG)']]], ['Ethernet (fixed)', [[800, '100BASE-TX (10/100ME)'], [1000, '1000BASE-T (1GE)'], [1150, '10GBASE-T (10GE)'], [1170, '10GBASE-CX4 (10GE)']]], ['Ethernet (modular)', [[1050, 'GBIC (1GE)'], [1100, 'SFP (1GE)'], [1200, 'SFP+ (10GE)'], [1300, 'XFP (10GE)'], [1310, 'XENPAK (10GE)'], [1320, 'X2 (10GE)'], [1350, 'SFP28 (25GE)'], [1400, 'QSFP+ (40GE)'], [1500, 'CFP (100GE)'], [1510, 'CFP2 (100GE)'], [1520, 'CFP4 (100GE)'], [1550, 'Cisco CPAK (100GE)'], [1600, 'QSFP28 (100GE)']]], ['Wireless', [[2600, 'IEEE 802.11a'], [2610, 'IEEE 802.11b/g'], [2620, 'IEEE 802.11n'], [2630, 'IEEE 802.11ac'], [2640, 'IEEE 802.11ad']]], ['FibreChannel', [[3010, 'SFP (1GFC)'], [3020, 'SFP (2GFC)'], [3040, 'SFP (4GFC)'], [3080, 'SFP+ (8GFC)'], [3160, 'SFP+ (16GFC)'], [3320, 'SFP28 (32GFC)']]], ['Serial', [[4000, 'T1 (1.544 Mbps)'], [4010, 'E1 (2.048 Mbps)'], [4040, 'T3 (45 Mbps)'], [4050, 'E3 (34 Mbps)']]], ['Stacking', [[5000, 'Cisco StackWise'], [5050, 'Cisco StackWise Plus'], [5100, 'Cisco FlexStack'], [5150, 'Cisco FlexStack Plus'], [5200, 'Juniper VCP'], [5300, 'Extreme SummitStack'], [5310, 'Extreme SummitStack-128'], [5320, 'Extreme SummitStack-256'], [5330, 'Extreme SummitStack-512']]], ['Other', [[32767, 'Other']]]], default=1200), ), ] From a43fc0d3d353b248c91a7532cbac712e130f5d4f Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 28 Nov 2018 16:19:05 -0500 Subject: [PATCH 13/24] Closes #2560: Add slug to DeviceType UI view --- netbox/templates/dcim/devicetype.html | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/netbox/templates/dcim/devicetype.html b/netbox/templates/dcim/devicetype.html index 35931b49f..b109c399b 100644 --- a/netbox/templates/dcim/devicetype.html +++ b/netbox/templates/dcim/devicetype.html @@ -55,7 +55,10 @@ Model Name - {{ devicetype.model }} + + {{ devicetype.model }}
+ {{ devicetype.slug }} + Part Number From ca0248c3a270439157309c345a3e14edca4439cb Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 30 Nov 2018 09:28:56 -0500 Subject: [PATCH 14/24] Closes #2089: Add SONET interface form factors --- CHANGELOG.md | 1 + netbox/dcim/constants.py | 20 ++++++++++++++++++++ netbox/dcim/migrations/0062_interface_mtu.py | 4 ++-- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa0f0d2fd..b95cbe20c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ v2.4.9 (FUTURE) ## Enhancements +* [#2089](https://github.com/digitalocean/netbox/issues/2089) - Add SONET interface form factors * [#2597](https://github.com/digitalocean/netbox/issues/2597) - Add FibreChannel SFP28 (32GFC) interface form factor ## Bug Fixes diff --git a/netbox/dcim/constants.py b/netbox/dcim/constants.py index bc2ef5687..d41825390 100644 --- a/netbox/dcim/constants.py +++ b/netbox/dcim/constants.py @@ -76,6 +76,14 @@ IFACE_FF_80211G = 2610 IFACE_FF_80211N = 2620 IFACE_FF_80211AC = 2630 IFACE_FF_80211AD = 2640 +# 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 # Fibrechannel IFACE_FF_1GFC_SFP = 3010 IFACE_FF_2GFC_SFP = 3020 @@ -147,6 +155,18 @@ IFACE_FF_CHOICES = [ [IFACE_FF_80211AD, 'IEEE 802.11ad'], ] ], + [ + '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'], + ] + ], [ 'FibreChannel', [ diff --git a/netbox/dcim/migrations/0062_interface_mtu.py b/netbox/dcim/migrations/0062_interface_mtu.py index acc47bfa0..d1ae92520 100644 --- a/netbox/dcim/migrations/0062_interface_mtu.py +++ b/netbox/dcim/migrations/0062_interface_mtu.py @@ -19,11 +19,11 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='interface', name='form_factor', - field=models.PositiveSmallIntegerField(choices=[['Virtual interfaces', [[0, 'Virtual'], [200, 'Link Aggregation Group (LAG)']]], ['Ethernet (fixed)', [[800, '100BASE-TX (10/100ME)'], [1000, '1000BASE-T (1GE)'], [1150, '10GBASE-T (10GE)'], [1170, '10GBASE-CX4 (10GE)']]], ['Ethernet (modular)', [[1050, 'GBIC (1GE)'], [1100, 'SFP (1GE)'], [1200, 'SFP+ (10GE)'], [1300, 'XFP (10GE)'], [1310, 'XENPAK (10GE)'], [1320, 'X2 (10GE)'], [1350, 'SFP28 (25GE)'], [1400, 'QSFP+ (40GE)'], [1500, 'CFP (100GE)'], [1510, 'CFP2 (100GE)'], [1520, 'CFP4 (100GE)'], [1550, 'Cisco CPAK (100GE)'], [1600, 'QSFP28 (100GE)']]], ['Wireless', [[2600, 'IEEE 802.11a'], [2610, 'IEEE 802.11b/g'], [2620, 'IEEE 802.11n'], [2630, 'IEEE 802.11ac'], [2640, 'IEEE 802.11ad']]], ['FibreChannel', [[3010, 'SFP (1GFC)'], [3020, 'SFP (2GFC)'], [3040, 'SFP (4GFC)'], [3080, 'SFP+ (8GFC)'], [3160, 'SFP+ (16GFC)'], [3320, 'SFP28 (32GFC)']]], ['Serial', [[4000, 'T1 (1.544 Mbps)'], [4010, 'E1 (2.048 Mbps)'], [4040, 'T3 (45 Mbps)'], [4050, 'E3 (34 Mbps)']]], ['Stacking', [[5000, 'Cisco StackWise'], [5050, 'Cisco StackWise Plus'], [5100, 'Cisco FlexStack'], [5150, 'Cisco FlexStack Plus'], [5200, 'Juniper VCP'], [5300, 'Extreme SummitStack'], [5310, 'Extreme SummitStack-128'], [5320, 'Extreme SummitStack-256'], [5330, 'Extreme SummitStack-512']]], ['Other', [[32767, 'Other']]]], default=1200), + field=models.PositiveSmallIntegerField(choices=[['Virtual interfaces', [[0, 'Virtual'], [200, 'Link Aggregation Group (LAG)']]], ['Ethernet (fixed)', [[800, '100BASE-TX (10/100ME)'], [1000, '1000BASE-T (1GE)'], [1150, '10GBASE-T (10GE)'], [1170, '10GBASE-CX4 (10GE)']]], ['Ethernet (modular)', [[1050, 'GBIC (1GE)'], [1100, 'SFP (1GE)'], [1200, 'SFP+ (10GE)'], [1300, 'XFP (10GE)'], [1310, 'XENPAK (10GE)'], [1320, 'X2 (10GE)'], [1350, 'SFP28 (25GE)'], [1400, 'QSFP+ (40GE)'], [1500, 'CFP (100GE)'], [1510, 'CFP2 (100GE)'], [1520, 'CFP4 (100GE)'], [1550, 'Cisco CPAK (100GE)'], [1600, 'QSFP28 (100GE)']]], ['Wireless', [[2600, 'IEEE 802.11a'], [2610, 'IEEE 802.11b/g'], [2620, 'IEEE 802.11n'], [2630, 'IEEE 802.11ac'], [2640, 'IEEE 802.11ad']]], ['SONET', [[6100, 'OC-3/STM-1'], [6200, 'OC-12/STM-4'], [6300, 'OC-48/STM-16'], [6400, 'OC-192/STM-64'], [6500, 'OC-768/STM-256'], [6600, 'OC-1920/STM-640'], [6700, 'OC-3840/STM-1234']]], ['FibreChannel', [[3010, 'SFP (1GFC)'], [3020, 'SFP (2GFC)'], [3040, 'SFP (4GFC)'], [3080, 'SFP+ (8GFC)'], [3160, 'SFP+ (16GFC)'], [3320, 'SFP28 (32GFC)']]], ['Serial', [[4000, 'T1 (1.544 Mbps)'], [4010, 'E1 (2.048 Mbps)'], [4040, 'T3 (45 Mbps)'], [4050, 'E3 (34 Mbps)']]], ['Stacking', [[5000, 'Cisco StackWise'], [5050, 'Cisco StackWise Plus'], [5100, 'Cisco FlexStack'], [5150, 'Cisco FlexStack Plus'], [5200, 'Juniper VCP'], [5300, 'Extreme SummitStack'], [5310, 'Extreme SummitStack-128'], [5320, 'Extreme SummitStack-256'], [5330, 'Extreme SummitStack-512']]], ['Other', [[32767, 'Other']]]], default=1200), ), migrations.AlterField( model_name='interfacetemplate', name='form_factor', - field=models.PositiveSmallIntegerField(choices=[['Virtual interfaces', [[0, 'Virtual'], [200, 'Link Aggregation Group (LAG)']]], ['Ethernet (fixed)', [[800, '100BASE-TX (10/100ME)'], [1000, '1000BASE-T (1GE)'], [1150, '10GBASE-T (10GE)'], [1170, '10GBASE-CX4 (10GE)']]], ['Ethernet (modular)', [[1050, 'GBIC (1GE)'], [1100, 'SFP (1GE)'], [1200, 'SFP+ (10GE)'], [1300, 'XFP (10GE)'], [1310, 'XENPAK (10GE)'], [1320, 'X2 (10GE)'], [1350, 'SFP28 (25GE)'], [1400, 'QSFP+ (40GE)'], [1500, 'CFP (100GE)'], [1510, 'CFP2 (100GE)'], [1520, 'CFP4 (100GE)'], [1550, 'Cisco CPAK (100GE)'], [1600, 'QSFP28 (100GE)']]], ['Wireless', [[2600, 'IEEE 802.11a'], [2610, 'IEEE 802.11b/g'], [2620, 'IEEE 802.11n'], [2630, 'IEEE 802.11ac'], [2640, 'IEEE 802.11ad']]], ['FibreChannel', [[3010, 'SFP (1GFC)'], [3020, 'SFP (2GFC)'], [3040, 'SFP (4GFC)'], [3080, 'SFP+ (8GFC)'], [3160, 'SFP+ (16GFC)'], [3320, 'SFP28 (32GFC)']]], ['Serial', [[4000, 'T1 (1.544 Mbps)'], [4010, 'E1 (2.048 Mbps)'], [4040, 'T3 (45 Mbps)'], [4050, 'E3 (34 Mbps)']]], ['Stacking', [[5000, 'Cisco StackWise'], [5050, 'Cisco StackWise Plus'], [5100, 'Cisco FlexStack'], [5150, 'Cisco FlexStack Plus'], [5200, 'Juniper VCP'], [5300, 'Extreme SummitStack'], [5310, 'Extreme SummitStack-128'], [5320, 'Extreme SummitStack-256'], [5330, 'Extreme SummitStack-512']]], ['Other', [[32767, 'Other']]]], default=1200), + field=models.PositiveSmallIntegerField(choices=[['Virtual interfaces', [[0, 'Virtual'], [200, 'Link Aggregation Group (LAG)']]], ['Ethernet (fixed)', [[800, '100BASE-TX (10/100ME)'], [1000, '1000BASE-T (1GE)'], [1150, '10GBASE-T (10GE)'], [1170, '10GBASE-CX4 (10GE)']]], ['Ethernet (modular)', [[1050, 'GBIC (1GE)'], [1100, 'SFP (1GE)'], [1200, 'SFP+ (10GE)'], [1300, 'XFP (10GE)'], [1310, 'XENPAK (10GE)'], [1320, 'X2 (10GE)'], [1350, 'SFP28 (25GE)'], [1400, 'QSFP+ (40GE)'], [1500, 'CFP (100GE)'], [1510, 'CFP2 (100GE)'], [1520, 'CFP4 (100GE)'], [1550, 'Cisco CPAK (100GE)'], [1600, 'QSFP28 (100GE)']]], ['Wireless', [[2600, 'IEEE 802.11a'], [2610, 'IEEE 802.11b/g'], [2620, 'IEEE 802.11n'], [2630, 'IEEE 802.11ac'], [2640, 'IEEE 802.11ad']]], ['SONET', [[6100, 'OC-3/STM-1'], [6200, 'OC-12/STM-4'], [6300, 'OC-48/STM-16'], [6400, 'OC-192/STM-64'], [6500, 'OC-768/STM-256'], [6600, 'OC-1920/STM-640'], [6700, 'OC-3840/STM-1234']]], ['FibreChannel', [[3010, 'SFP (1GFC)'], [3020, 'SFP (2GFC)'], [3040, 'SFP (4GFC)'], [3080, 'SFP+ (8GFC)'], [3160, 'SFP+ (16GFC)'], [3320, 'SFP28 (32GFC)']]], ['Serial', [[4000, 'T1 (1.544 Mbps)'], [4010, 'E1 (2.048 Mbps)'], [4040, 'T3 (45 Mbps)'], [4050, 'E3 (34 Mbps)']]], ['Stacking', [[5000, 'Cisco StackWise'], [5050, 'Cisco StackWise Plus'], [5100, 'Cisco FlexStack'], [5150, 'Cisco FlexStack Plus'], [5200, 'Juniper VCP'], [5300, 'Extreme SummitStack'], [5310, 'Extreme SummitStack-128'], [5320, 'Extreme SummitStack-256'], [5330, 'Extreme SummitStack-512']]], ['Other', [[32767, 'Other']]]], default=1200), ), ] From 232e6f5076a9e91eb0a16dbe190c7e4ac9af1bd6 Mon Sep 17 00:00:00 2001 From: mmahacek Date: Mon, 3 Dec 2018 06:56:43 -0800 Subject: [PATCH 15/24] #2635 - Update documentation for python3 update (#2636) Add reference to reinstalling the django-rq module --- docs/installation/migrating-to-python3.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/installation/migrating-to-python3.md b/docs/installation/migrating-to-python3.md index b2efadea1..1d5ceb977 100644 --- a/docs/installation/migrating-to-python3.md +++ b/docs/installation/migrating-to-python3.md @@ -36,3 +36,9 @@ 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 +``` From 0dcab07519f8dcb87fecc73c734631b48f3a4d35 Mon Sep 17 00:00:00 2001 From: John Anderson Date: Tue, 4 Dec 2018 00:40:54 -0500 Subject: [PATCH 16/24] fixes #2623 - model class being passed to rqworker --- netbox/extras/webhooks.py | 2 +- netbox/extras/webhooks_worker.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/netbox/extras/webhooks.py b/netbox/extras/webhooks.py index a0c927b64..35ec56feb 100644 --- a/netbox/extras/webhooks.py +++ b/netbox/extras/webhooks.py @@ -45,7 +45,7 @@ def enqueue_webhooks(instance, action): "extras.webhooks_worker.process_webhook", webhook, serializer.data, - instance.__class__, + instance._meta.model_name, action, str(datetime.datetime.now()) ) diff --git a/netbox/extras/webhooks_worker.py b/netbox/extras/webhooks_worker.py index 2122d1154..30f86f311 100644 --- a/netbox/extras/webhooks_worker.py +++ b/netbox/extras/webhooks_worker.py @@ -10,14 +10,14 @@ from extras.constants import WEBHOOK_CT_JSON, WEBHOOK_CT_X_WWW_FORM_ENCODED, OBJ @job('default') -def process_webhook(webhook, data, model_class, event, timestamp): +def process_webhook(webhook, data, model_name, event, timestamp): """ Make a POST request to the defined Webhook """ payload = { 'event': dict(OBJECTCHANGE_ACTION_CHOICES)[event].lower(), 'timestamp': timestamp, - 'model': model_class._meta.model_name, + 'model': model_name, 'data': data } headers = { From e6ee26cf0e2115983b9f26c0104b6f82efec64ef Mon Sep 17 00:00:00 2001 From: John Anderson Date: Tue, 4 Dec 2018 00:46:36 -0500 Subject: [PATCH 17/24] CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b95cbe20c..323526e5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ v2.4.9 (FUTURE) * [#2606](https://github.com/digitalocean/netbox/issues/2606) - Fixed filtering for interfaces with a virtual form factor * [#2613](https://github.com/digitalocean/netbox/issues/2613) - Decrease live search minimum characters to three * [#2615](https://github.com/digitalocean/netbox/issues/2615) - Tweak live search widget to use brief format for API requests +* [#2623](https://github.com/digitalocean/netbox/issues/2623) - Removed the need to pass the model class to the rqworker process for webhooks --- From 90e7080b63fee0e477685c8c83688d98818b24f8 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 4 Dec 2018 09:19:32 -0500 Subject: [PATCH 18/24] Closes #2641: Restored link to NetBox shell documentation --- docs/{additional-features => administration}/netbox-shell.md | 0 mkdocs.yml | 1 + 2 files changed, 1 insertion(+) rename docs/{additional-features => administration}/netbox-shell.md (100%) diff --git a/docs/additional-features/netbox-shell.md b/docs/administration/netbox-shell.md similarity index 100% rename from docs/additional-features/netbox-shell.md rename to docs/administration/netbox-shell.md diff --git a/mkdocs.yml b/mkdocs.yml index cd718cc76..a0185e56e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -38,6 +38,7 @@ pages: - Change Logging: 'additional-features/change-logging.md' - Administration: - Replicating NetBox: 'administration/replicating-netbox.md' + - NetBox Shell: 'administration/netbox-shell.md' - API: - Overview: 'api/overview.md' - Authentication: 'api/authentication.md' From 7bbf33ee39aec3170f86821a8e0659438966861f Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 4 Dec 2018 09:44:25 -0500 Subject: [PATCH 19/24] Don't force the docs to open in a new window --- netbox/templates/_base.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/templates/_base.html b/netbox/templates/_base.html index 27ebb052d..2a9da7b15 100644 --- a/netbox/templates/_base.html +++ b/netbox/templates/_base.html @@ -54,7 +54,7 @@

- Docs · + Docs · API · Code · Help From 4e3567659a04d461691e48c0eb88e1486af70cab Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 4 Dec 2018 15:19:38 -0500 Subject: [PATCH 20/24] Add reminder to update static field choices --- docs/development/extending-models.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/development/extending-models.md b/docs/development/extending-models.md index 190fad5e0..1fde8067b 100644 --- a/docs/development/extending-models.md +++ b/docs/development/extending-models.md @@ -44,7 +44,11 @@ If you're adding a relational field (e.g. `ForeignKey`) and intend to include th Extend the model's API serializer in `.api.serializers` to include the new field. In most cases, it will not be necessary to also extend the nested serializer, which produces a minimal represenation of the model. -### 6. Add field to forms +### 6. Add choices to API view + +If the new field has static choices, add it to the `FieldChoicesViewSet` for the app. + +### 7. Add field to forms Extend any forms to include the new field as appropriate. Common forms include: @@ -53,18 +57,18 @@ Extend any forms to include the new field as appropriate. Common forms include: * **CSV import** - The form used when bulk importing objects in CSV format * **Filter** - Displays the options available for filtering a list of objects (both UI and API) -### 7. Extend object filter set +### 8. Extend object filter set If the new field should be filterable, add it to the `FilterSet` for the model. If the field should be searchable, remember to reference it in the FilterSet's `search()` method. -### 8. Add column to object table +### 9. Add column to object table If the new field will be included in the object list view, add a column to the model's table. For simple fields, adding the field name to `Meta.fields` will be sufficient. More complex fields may require explicitly declaring a new column. -### 9. Update the UI templates +### 10. Update the UI templates Edit the object's view template to display the new field. There may also be a custom add/edit form template that needs to be updated. -### 10. Adjust API and model tests +### 11. Adjust API and model tests Extend the model and/or API tests to verify that the new field and any accompanying validation logic perform as expected. This is especially important for relational fields. From d3d6c83fbb6623757803a2eeda6b3210003cf88c Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 4 Dec 2018 15:29:58 -0500 Subject: [PATCH 21/24] Fixes #2634: Enforce consistent representation of unnamed devices in rack view --- CHANGELOG.md | 1 + netbox/templates/dcim/inc/rack_elevation.html | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 323526e5a..d9433aacf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ v2.4.9 (FUTURE) * [#2613](https://github.com/digitalocean/netbox/issues/2613) - Decrease live search minimum characters to three * [#2615](https://github.com/digitalocean/netbox/issues/2615) - Tweak live search widget to use brief format for API requests * [#2623](https://github.com/digitalocean/netbox/issues/2623) - Removed the need to pass the model class to the rqworker process for webhooks +* [#2634](https://github.com/digitalocean/netbox/issues/2634) - Enforce consistent representation of unnamed devices in rack view --- diff --git a/netbox/templates/dcim/inc/rack_elevation.html b/netbox/templates/dcim/inc/rack_elevation.html index 46fe01c8d..e7beeb9ba 100644 --- a/netbox/templates/dcim/inc/rack_elevation.html +++ b/netbox/templates/dcim/inc/rack_elevation.html @@ -27,13 +27,13 @@ {% ifequal u.device.face face_id %} - {{ u.device.name|default:u.device.device_role }} + {{ u.device }} {% if u.device.devicebay_count %} ({{ u.device.get_children.count }}/{{ u.device.devicebay_count }}) {% endif %} {% else %} - {{ u.device.name|default:u.device.device_role }} + {{ u.device }} {% endifequal %} {% else %} From 686a65880eb17cfb6b592253a2cc6fe027ee6fd1 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 5 Dec 2018 14:34:49 -0500 Subject: [PATCH 22/24] Closes #2495: Enable deep-merging of config context data --- CHANGELOG.md | 1 + netbox/extras/models.py | 8 +-- netbox/utilities/tests/test_utils.py | 89 ++++++++++++++++++++++++++++ netbox/utilities/utils.py | 14 +++++ 4 files changed, 108 insertions(+), 4 deletions(-) create mode 100644 netbox/utilities/tests/test_utils.py diff --git a/CHANGELOG.md b/CHANGELOG.md index d9433aacf..e496d31d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ v2.4.9 (FUTURE) ## Enhancements * [#2089](https://github.com/digitalocean/netbox/issues/2089) - Add SONET interface form factors +* [#2495](https://github.com/digitalocean/netbox/issues/2495) - Enable deep-merging of config context data * [#2597](https://github.com/digitalocean/netbox/issues/2597) - Add FibreChannel SFP28 (32GFC) interface form factor ## Bug Fixes diff --git a/netbox/extras/models.py b/netbox/extras/models.py index de3edca9b..1605df6df 100644 --- a/netbox/extras/models.py +++ b/netbox/extras/models.py @@ -18,7 +18,7 @@ from django.utils.encoding import python_2_unicode_compatible from django.utils.safestring import mark_safe from dcim.constants import CONNECTION_STATUS_CONNECTED -from utilities.utils import foreground_color +from utilities.utils import deepmerge, foreground_color from .constants import * from .querysets import ConfigContextQuerySet @@ -727,11 +727,11 @@ class ConfigContextModel(models.Model): # Compile all config data, overwriting lower-weight values with higher-weight values where a collision occurs data = OrderedDict() for context in ConfigContext.objects.get_for_object(self): - data.update(context.data) + data = deepmerge(data, context.data) - # If the object has local config context data defined, that data overwrites all rendered data + # If the object has local config context data defined, merge it last if self.local_context_data is not None: - data.update(self.local_context_data) + data = deepmerge(data, self.local_context_data) return data diff --git a/netbox/utilities/tests/test_utils.py b/netbox/utilities/tests/test_utils.py new file mode 100644 index 000000000..4e0fec1ba --- /dev/null +++ b/netbox/utilities/tests/test_utils.py @@ -0,0 +1,89 @@ +from django.test import TestCase + +from utilities.utils import deepmerge + + +class DeepMergeTest(TestCase): + """ + Validate the behavior of the deepmerge() utility. + """ + + def setUp(self): + return + + def test_deepmerge(self): + + dict1 = { + 'active': True, + 'foo': 123, + 'fruits': { + 'orange': 1, + 'apple': 2, + 'pear': 3, + }, + 'vegetables': None, + 'dairy': { + 'milk': 1, + 'cheese': 2, + }, + 'deepnesting': { + 'foo': { + 'a': 10, + 'b': 20, + 'c': 30, + }, + }, + } + + dict2 = { + 'active': False, + 'bar': 456, + 'fruits': { + 'banana': 4, + 'grape': 5, + }, + 'vegetables': { + 'celery': 1, + 'carrots': 2, + 'corn': 3, + }, + 'dairy': None, + 'deepnesting': { + 'foo': { + 'a': 100, + 'd': 40, + }, + }, + } + + merged = { + 'active': False, + 'foo': 123, + 'bar': 456, + 'fruits': { + 'orange': 1, + 'apple': 2, + 'pear': 3, + 'banana': 4, + 'grape': 5, + }, + 'vegetables': { + 'celery': 1, + 'carrots': 2, + 'corn': 3, + }, + 'dairy': None, + 'deepnesting': { + 'foo': { + 'a': 100, + 'b': 20, + 'c': 30, + 'd': 40, + }, + }, + } + + self.assertEqual( + deepmerge(dict1, dict2), + merged + ) diff --git a/netbox/utilities/utils.py b/netbox/utilities/utils.py index 14c29d211..642242d30 100644 --- a/netbox/utilities/utils.py +++ b/netbox/utilities/utils.py @@ -1,5 +1,6 @@ from __future__ import unicode_literals +from collections import OrderedDict import datetime import json import six @@ -109,3 +110,16 @@ def serialize_object(obj, extra=None): data.update(extra) return data + + +def deepmerge(original, new): + """ + Deep merge two dictionaries (new into original) and return a new dict + """ + merged = OrderedDict(original) + for key, val in new.items(): + if key in original and isinstance(original[key], dict) and isinstance(val, dict): + merged[key] = deepmerge(original[key], val) + else: + merged[key] = val + return merged From aa8c836b94a65b42e66ca2d0fdb1f4920f1aa88e Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 7 Dec 2018 09:57:55 -0500 Subject: [PATCH 23/24] Closes #2611: Fix error handling when assigning a clustered device to a different site --- CHANGELOG.md | 1 + netbox/dcim/forms.py | 23 ++++++++++++++++++++--- netbox/templates/dcim/device_edit.html | 7 +++++++ 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e496d31d2..b82decb92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ v2.4.9 (FUTURE) * [#2400](https://github.com/digitalocean/netbox/issues/2400) - Correct representation of nested object assignment in API docs * [#2576](https://github.com/digitalocean/netbox/issues/2576) - Correct type for count_* fields in site API representation * [#2606](https://github.com/digitalocean/netbox/issues/2606) - Fixed filtering for interfaces with a virtual form factor +* [#2611](https://github.com/digitalocean/netbox/issues/2611) - Fix error handling when assigning a clustered device to a different site * [#2613](https://github.com/digitalocean/netbox/issues/2613) - Decrease live search minimum characters to three * [#2615](https://github.com/digitalocean/netbox/issues/2615) - Tweak live search widget to use brief format for API requests * [#2623](https://github.com/digitalocean/netbox/issues/2623) - Removed the need to pass the model class to the rqworker process for webhooks diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 46e039211..4a75ac386 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -20,7 +20,7 @@ from utilities.forms import ( ConfirmationForm, CSVChoiceField, ExpandableNameField, FilterChoiceField, FilterTreeNodeMultipleChoiceField, FlexibleModelChoiceField, JSONField, Livesearch, SelectWithDisabled, SelectWithPK, SmallTextarea, SlugField, ) -from virtualization.models import Cluster +from virtualization.models import Cluster, ClusterGroup from .constants import ( CONNECTION_STATUS_CHOICES, CONNECTION_STATUS_CONNECTED, DEVICE_STATUS_CHOICES, IFACE_FF_CHOICES, IFACE_FF_LAG, IFACE_MODE_ACCESS, IFACE_MODE_CHOICES, IFACE_MODE_TAGGED_ALL, IFACE_ORDERING_CHOICES, RACK_FACE_CHOICES, @@ -820,6 +820,23 @@ class DeviceForm(BootstrapMixin, TenancyForm, CustomFieldForm): display_field='model' ) ) + cluster_group = forms.ModelChoiceField( + queryset=ClusterGroup.objects.all(), + required=False, + widget=forms.Select( + attrs={'filter-for': 'cluster', 'nullable': 'true'} + ) + ) + cluster = ChainedModelChoiceField( + queryset=Cluster.objects.all(), + chains=( + ('group', 'cluster_group'), + ), + required=False, + widget=APISelect( + api_url='/api/virtualization/clusters/?group_id={{cluster_group}}', + ) + ) comments = CommentField() tags = TagField(required=False) local_context_data = JSONField(required=False) @@ -828,8 +845,8 @@ class DeviceForm(BootstrapMixin, TenancyForm, CustomFieldForm): model = Device fields = [ 'name', 'device_role', 'device_type', 'serial', 'asset_tag', 'site', 'rack', 'position', 'face', - 'status', 'platform', 'primary_ip4', 'primary_ip6', 'tenant_group', 'tenant', 'comments', 'tags', - 'local_context_data' + 'status', 'platform', 'primary_ip4', 'primary_ip6', 'cluster_group', 'cluster', 'tenant_group', 'tenant', + 'comments', 'tags', 'local_context_data' ] help_texts = { 'device_role': "The function this device serves", diff --git a/netbox/templates/dcim/device_edit.html b/netbox/templates/dcim/device_edit.html index 23b2b404e..1486c1ad5 100644 --- a/netbox/templates/dcim/device_edit.html +++ b/netbox/templates/dcim/device_edit.html @@ -62,6 +62,13 @@ {% endif %}

+
+
Virtualization
+
+ {% render_field form.cluster_group %} + {% render_field form.cluster %} +
+
Tenancy
From 869194354c253987a85713b444f064a050fe1f32 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 7 Dec 2018 10:19:57 -0500 Subject: [PATCH 24/24] Release v2.4.9 --- CHANGELOG.md | 2 +- netbox/netbox/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b82decb92..75dfe2dab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -v2.4.9 (FUTURE) +v2.4.9 (2018-12-07) ## Enhancements diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 7034788fb..be34f2be3 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -22,7 +22,7 @@ if sys.version_info[0] < 3: DeprecationWarning ) -VERSION = '2.4.9-dev' +VERSION = '2.4.9' BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))