diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index e1012212d..54dc5ca8c 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -30,10 +30,9 @@ about: Report a reproducible bug in the current release of NetBox library such as pynetbox. --> ### Steps to Reproduce -1. Disable any installed plugins by commenting out the `PLUGINS` setting in - `configuration.py`. -2. -3. +1. +2. +3. ### Expected Behavior diff --git a/docs/configuration/optional-settings.md b/docs/configuration/optional-settings.md index 4d5251f25..3c4392915 100644 --- a/docs/configuration/optional-settings.md +++ b/docs/configuration/optional-settings.md @@ -86,7 +86,12 @@ CORS_ORIGIN_WHITELIST = [ Default: False -This setting enables debugging. This should be done only during development or troubleshooting. Never enable debugging on a production system, as it can expose sensitive data to unauthenticated users. +This setting enables debugging. This should be done only during development or troubleshooting. Note that only clients +which access NetBox from a recognized [internal IP address](#internal_ips) will see debugging tools in the user +interface. + +!!! warning + Never enable debugging on a production system, as it can expose sensitive data to unauthenticated users. --- @@ -184,6 +189,16 @@ HTTP_PROXIES = { --- +## INTERNAL_IPS + +Default: `('127.0.0.1', '::1',)` + +A list of IP addresses recognized as internal to the system, used to control the display of debugging output. For +example, the debugging toolbar will be viewable only when a client is accessing NetBox from one of the listed IP +addresses (and [`DEBUG`](#debug) is true). + +--- + ## LOGGING 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`. @@ -385,7 +400,7 @@ When remote user authentication is in use, this is the name of the HTTP header w ## REMOTE_AUTH_AUTO_CREATE_USER -Default: `True` +Default: `False` If true, NetBox will automatically create local accounts for users authenticated via a remote service. (Requires `REMOTE_AUTH_ENABLED`.) diff --git a/docs/release-notes/version-2.8.md b/docs/release-notes/version-2.8.md index 9574894a3..5ca86217a 100644 --- a/docs/release-notes/version-2.8.md +++ b/docs/release-notes/version-2.8.md @@ -1,21 +1,32 @@ # NetBox v2.8 -v2.8.5 (FUTURE) +## v2.8.5 (2020-05-26) **Note:** The minimum required version of PostgreSQL is now 9.6. +### Enhancements + +* [#4650](https://github.com/netbox-community/netbox/issues/4650) - Expose `INTERNAL_IPS` configuration parameter +* [#4651](https://github.com/netbox-community/netbox/issues/4651) - Add `csrf_token` context for plugin templates +* [#4652](https://github.com/netbox-community/netbox/issues/4652) - Add permissions context for plugin templates +* [#4665](https://github.com/netbox-community/netbox/issues/4665) - Add NEMA L14 and L21 power port/outlet types +* [#4672](https://github.com/netbox-community/netbox/issues/4672) - Set default color for rack and devices roles + ### Bug Fixes * [#3304](https://github.com/netbox-community/netbox/issues/3304) - Fix caching invalidation issue related to device/virtual machine primary IP addresses +* [#4525](https://github.com/netbox-community/netbox/issues/4525) - Allow passing initial data to custom script MultiObjectVar * [#4644](https://github.com/netbox-community/netbox/issues/4644) - Fix ordering of services table by parent * [#4646](https://github.com/netbox-community/netbox/issues/4646) - Correct UI link for reports with custom name * [#4647](https://github.com/netbox-community/netbox/issues/4647) - Fix caching invalidation issue related to assigning new IP addresses to interfaces * [#4648](https://github.com/netbox-community/netbox/issues/4648) - Fix bulk CSV import of child devices * [#4649](https://github.com/netbox-community/netbox/issues/4649) - Fix interface assignment for bulk-imported IP addresses +* [#4676](https://github.com/netbox-community/netbox/issues/4676) - Set default value of `REMOTE_AUTH_AUTO_CREATE_USER` as `False` in docs +* [#4684](https://github.com/netbox-community/netbox/issues/4684) - Respect `comments` field when importing device type in YAML/JSON format --- -v2.8.4 (2020-05-13) +## v2.8.4 (2020-05-13) ### Enhancements diff --git a/netbox/dcim/choices.py b/netbox/dcim/choices.py index 8433bb152..479563093 100644 --- a/netbox/dcim/choices.py +++ b/netbox/dcim/choices.py @@ -276,6 +276,10 @@ class PowerPortTypeChoices(ChoiceSet): TYPE_NEMA_L620P = 'nema-l6-20p' TYPE_NEMA_L630P = 'nema-l6-30p' TYPE_NEMA_L650P = 'nema-l6-50p' + TYPE_NEMA_L1420P = 'nema-l14-20p' + TYPE_NEMA_L1430P = 'nema-l14-30p' + TYPE_NEMA_L2120P = 'nema-l21-20p' + TYPE_NEMA_L2130P = 'nema-l21-30p' # California style TYPE_CS6361C = 'cs6361c' TYPE_CS6365C = 'cs6365c' @@ -337,6 +341,10 @@ class PowerPortTypeChoices(ChoiceSet): (TYPE_NEMA_L620P, 'NEMA L6-20P'), (TYPE_NEMA_L630P, 'NEMA L6-30P'), (TYPE_NEMA_L650P, 'NEMA L6-50P'), + (TYPE_NEMA_L1420P, 'NEMA L14-20P'), + (TYPE_NEMA_L1430P, 'NEMA L14-30P'), + (TYPE_NEMA_L2120P, 'NEMA L21-20P'), + (TYPE_NEMA_L2130P, 'NEMA L21-30P'), )), ('California Style', ( (TYPE_CS6361C, 'CS6361C'), @@ -405,6 +413,10 @@ class PowerOutletTypeChoices(ChoiceSet): TYPE_NEMA_L620R = 'nema-l6-20r' TYPE_NEMA_L630R = 'nema-l6-30r' TYPE_NEMA_L650R = 'nema-l6-50r' + TYPE_NEMA_L1420R = 'nema-l14-20r' + TYPE_NEMA_L1430R = 'nema-l14-30r' + TYPE_NEMA_L2120R = 'nema-l21-20r' + TYPE_NEMA_L2130R = 'nema-l21-30r' # California style TYPE_CS6360C = 'CS6360C' TYPE_CS6364C = 'CS6364C' @@ -467,6 +479,10 @@ class PowerOutletTypeChoices(ChoiceSet): (TYPE_NEMA_L620R, 'NEMA L6-20R'), (TYPE_NEMA_L630R, 'NEMA L6-30R'), (TYPE_NEMA_L650R, 'NEMA L6-50R'), + (TYPE_NEMA_L1420R, 'NEMA L14-20R'), + (TYPE_NEMA_L1430R, 'NEMA L14-30R'), + (TYPE_NEMA_L2120R, 'NEMA L21-20R'), + (TYPE_NEMA_L2130R, 'NEMA L21-30R'), )), ('California Style', ( (TYPE_CS6360C, 'CS6360C'), diff --git a/netbox/dcim/filters.py b/netbox/dcim/filters.py index 5bc6dd7f0..8c24180bb 100644 --- a/netbox/dcim/filters.py +++ b/netbox/dcim/filters.py @@ -4,7 +4,7 @@ from django.contrib.auth.models import User from extras.filters import CustomFieldFilterSet, LocalConfigContextFilterSet, CreatedUpdatedFilterSet from tenancy.filters import TenancyFilterSet from tenancy.models import Tenant -from utilities.constants import COLOR_CHOICES +from utilities.choices import ColorChoices from utilities.filters import ( BaseFilterSet, MultiValueCharFilter, MultiValueMACAddressFilter, MultiValueNumberFilter, NameSlugSearchFilterSet, TagFilter, TreeNodeMultipleChoiceFilter, @@ -1084,7 +1084,7 @@ class CableFilterSet(BaseFilterSet): choices=CableStatusChoices ) color = django_filters.MultipleChoiceFilter( - choices=COLOR_CHOICES + choices=ColorChoices ) device_id = MultiValueNumberFilter( method='filter_device' diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index cdd42ddae..94cf51fcd 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -932,6 +932,7 @@ class DeviceTypeImportForm(BootstrapMixin, forms.ModelForm): model = DeviceType fields = [ 'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role', + 'comments', ] diff --git a/netbox/dcim/migrations/0106_role_default_color.py b/netbox/dcim/migrations/0106_role_default_color.py new file mode 100644 index 000000000..c4df1b33f --- /dev/null +++ b/netbox/dcim/migrations/0106_role_default_color.py @@ -0,0 +1,24 @@ +# Generated by Django 3.0.6 on 2020-05-26 13:33 + +from django.db import migrations +import utilities.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('dcim', '0105_interface_name_collation'), + ] + + operations = [ + migrations.AlterField( + model_name='devicerole', + name='color', + field=utilities.fields.ColorField(default='9e9e9e', max_length=6), + ), + migrations.AlterField( + model_name='rackrole', + name='color', + field=utilities.fields.ColorField(default='9e9e9e', max_length=6), + ), + ] diff --git a/netbox/dcim/models/__init__.py b/netbox/dcim/models/__init__.py index 490667153..1f6478119 100644 --- a/netbox/dcim/models/__init__.py +++ b/netbox/dcim/models/__init__.py @@ -23,6 +23,7 @@ from dcim.fields import ASNField from dcim.elevations import RackElevationSVG from extras.models import ConfigContextModel, CustomFieldModel, ObjectChange, TaggedItem from extras.utils import extras_features +from utilities.choices import ColorChoices from utilities.fields import ColorField, NaturalOrderingField from utilities.models import ChangeLoggedModel from utilities.utils import serialize_object, to_meters @@ -379,7 +380,9 @@ class RackRole(ChangeLoggedModel): slug = models.SlugField( unique=True ) - color = ColorField() + color = ColorField( + default=ColorChoices.COLOR_GREY + ) description = models.CharField( max_length=200, blank=True, @@ -1190,7 +1193,9 @@ class DeviceRole(ChangeLoggedModel): slug = models.SlugField( unique=True ) - color = ColorField() + color = ColorField( + default=ColorChoices.COLOR_GREY + ) vm_role = models.BooleanField( default=True, verbose_name='VM Role', diff --git a/netbox/dcim/tests/test_views.py b/netbox/dcim/tests/test_views.py index 65f37c1d5..7ee5d7845 100644 --- a/netbox/dcim/tests/test_views.py +++ b/netbox/dcim/tests/test_views.py @@ -366,6 +366,7 @@ manufacturer: Generic model: TEST-1000 slug: test-1000 u_height: 2 +comments: test comment console-ports: - name: Console Port 1 type: de-9 @@ -456,6 +457,7 @@ device-bays: self.assertHttpStatus(response, 200) dt = DeviceType.objects.get(model='TEST-1000') + self.assertEqual(dt.comments, 'test comment') # Verify all of the components were created self.assertEqual(dt.consoleport_templates.count(), 3) diff --git a/netbox/extras/forms.py b/netbox/extras/forms.py index 469b55efd..cb9930ae2 100644 --- a/netbox/extras/forms.py +++ b/netbox/extras/forms.py @@ -432,11 +432,11 @@ class ScriptForm(BootstrapMixin, forms.Form): 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() + self.base_fields[name] = var.as_field() + + super().__init__(*args, **kwargs) # Toggle default commit behavior based on Meta option if not commit_default: diff --git a/netbox/extras/models/tags.py b/netbox/extras/models/tags.py index 3bad7fa8b..d68ca2ce6 100644 --- a/netbox/extras/models/tags.py +++ b/netbox/extras/models/tags.py @@ -3,6 +3,7 @@ from django.urls import reverse from django.utils.text import slugify from taggit.models import TagBase, GenericTaggedItemBase +from utilities.choices import ColorChoices from utilities.fields import ColorField from utilities.models import ChangeLoggedModel @@ -13,7 +14,7 @@ from utilities.models import ChangeLoggedModel class Tag(TagBase, ChangeLoggedModel): color = ColorField( - default='9e9e9e' + default=ColorChoices.COLOR_GREY ) description = models.CharField( max_length=200, diff --git a/netbox/extras/templatetags/plugins.py b/netbox/extras/templatetags/plugins.py index b66cce0a6..200b78e34 100644 --- a/netbox/extras/templatetags/plugins.py +++ b/netbox/extras/templatetags/plugins.py @@ -18,6 +18,8 @@ def _get_registered_content(obj, method, template_context): 'object': obj, 'request': template_context['request'], 'settings': template_context['settings'], + 'csrf_token': template_context['csrf_token'], + 'perms': template_context['perms'], } model_name = obj._meta.label_lower diff --git a/netbox/netbox/configuration.example.py b/netbox/netbox/configuration.example.py index a020c4322..941cbcd88 100644 --- a/netbox/netbox/configuration.example.py +++ b/netbox/netbox/configuration.example.py @@ -132,6 +132,10 @@ EXEMPT_VIEW_PERMISSIONS = [ # 'https': 'http://10.10.1.10:1080', # } +# IP addresses recognized as internal to the system. The debugging toolbar will be available only to clients accessing +# NetBox from an internal IP. +INTERNAL_IPS = ('127.0.0.1', '::1') + # Enable custom logging. Please see the Django documentation for detailed guidance on configuring custom logs: # https://docs.djangoproject.com/en/stable/topics/logging/ LOGGING = {} diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 0dc77181c..1b1d527c7 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -78,6 +78,7 @@ EMAIL = getattr(configuration, 'EMAIL', {}) ENFORCE_GLOBAL_UNIQUE = getattr(configuration, 'ENFORCE_GLOBAL_UNIQUE', False) EXEMPT_VIEW_PERMISSIONS = getattr(configuration, 'EXEMPT_VIEW_PERMISSIONS', []) HTTP_PROXIES = getattr(configuration, 'HTTP_PROXIES', None) +INTERNAL_IPS = getattr(configuration, 'INTERNAL_IPS', ('127.0.0.1', '::1')) LOGGING = getattr(configuration, 'LOGGING', {}) LOGIN_REQUIRED = getattr(configuration, 'LOGIN_REQUIRED', False) LOGIN_TIMEOUT = getattr(configuration, 'LOGIN_TIMEOUT', None) @@ -615,15 +616,6 @@ RQ_QUEUES = { 'check_releases': RQ_PARAMS, } -# -# Django debug toolbar -# - -INTERNAL_IPS = ( - '127.0.0.1', - '::1', -) - # # NetBox internal settings diff --git a/netbox/utilities/choices.py b/netbox/utilities/choices.py index aba64e63b..ce0929a8b 100644 --- a/netbox/utilities/choices.py +++ b/netbox/utilities/choices.py @@ -80,6 +80,70 @@ def unpack_grouped_choices(choices): return unpacked_choices +# +# Generic color choices +# + +class ColorChoices(ChoiceSet): + COLOR_DARK_RED = 'aa1409' + COLOR_RED = 'f44336' + COLOR_PINK = 'e91e63' + COLOR_ROSE = 'ffe4e1' + COLOR_FUCHSIA = 'ff66ff' + COLOR_PURPLE = '9c27b0' + COLOR_DARK_PURPLE = '673ab7' + COLOR_INDIGO = '3f51b5' + COLOR_BLUE = '2196f3' + COLOR_LIGHT_BLUE = '03a9f4' + COLOR_CYAN = '00bcd4' + COLOR_TEAL = '009688' + COLOR_AQUA = '00ffff' + COLOR_DARK_GREEN = '2f6a31' + COLOR_GREEN = '4caf50' + COLOR_LIGHT_GREEN = '8bc34a' + COLOR_LIME = 'cddc39' + COLOR_YELLOW = 'ffeb3b' + COLOR_AMBER = 'ffc107' + COLOR_ORANGE = 'ff9800' + COLOR_DARK_ORANGE = 'ff5722' + COLOR_BROWN = '795548' + COLOR_LIGHT_GREY = 'c0c0c0' + COLOR_GREY = '9e9e9e' + COLOR_DARK_GREY = '607d8b' + COLOR_BLACK = '111111' + COLOR_WHITE = 'ffffff' + + CHOICES = ( + (COLOR_DARK_RED, 'Dark red'), + (COLOR_RED, 'Red'), + (COLOR_PINK, 'Pink'), + (COLOR_ROSE, 'Rose'), + (COLOR_FUCHSIA, 'Fuchsia'), + (COLOR_PURPLE, 'Purple'), + (COLOR_DARK_PURPLE, 'Dark purple'), + (COLOR_INDIGO, 'Indigo'), + (COLOR_BLUE, 'Blue'), + (COLOR_LIGHT_BLUE, 'Light blue'), + (COLOR_CYAN, 'Cyan'), + (COLOR_TEAL, 'Teal'), + (COLOR_AQUA, 'Aqua'), + (COLOR_DARK_GREEN, 'Dark green'), + (COLOR_GREEN, 'Green'), + (COLOR_LIGHT_GREEN, 'Light green'), + (COLOR_LIME, 'Lime'), + (COLOR_YELLOW, 'Yellow'), + (COLOR_AMBER, 'Amber'), + (COLOR_ORANGE, 'Orange'), + (COLOR_DARK_ORANGE, 'Dark orange'), + (COLOR_BROWN, 'Brown'), + (COLOR_LIGHT_GREY, 'Light grey'), + (COLOR_GREY, 'Grey'), + (COLOR_DARK_GREY, 'Dark grey'), + (COLOR_BLACK, 'Black'), + (COLOR_WHITE, 'White'), + ) + + # # Button color choices # diff --git a/netbox/utilities/constants.py b/netbox/utilities/constants.py index bdcdeef11..9a3a7d028 100644 --- a/netbox/utilities/constants.py +++ b/netbox/utilities/constants.py @@ -1,34 +1,3 @@ -COLOR_CHOICES = ( - ('aa1409', 'Dark red'), - ('f44336', 'Red'), - ('e91e63', 'Pink'), - ('ffe4e1', 'Rose'), - ('ff66ff', 'Fuschia'), - ('9c27b0', 'Purple'), - ('673ab7', 'Dark purple'), - ('3f51b5', 'Indigo'), - ('2196f3', 'Blue'), - ('03a9f4', 'Light blue'), - ('00bcd4', 'Cyan'), - ('009688', 'Teal'), - ('00ffff', 'Aqua'), - ('2f6a31', 'Dark green'), - ('4caf50', 'Green'), - ('8bc34a', 'Light green'), - ('cddc39', 'Lime'), - ('ffeb3b', 'Yellow'), - ('ffc107', 'Amber'), - ('ff9800', 'Orange'), - ('ff5722', 'Dark orange'), - ('795548', 'Brown'), - ('c0c0c0', 'Light grey'), - ('9e9e9e', 'Grey'), - ('607d8b', 'Dark grey'), - ('111111', 'Black'), - ('ffffff', 'White'), -) - - # # Filter lookup expressions # diff --git a/netbox/utilities/forms.py b/netbox/utilities/forms.py index bfc783631..979b6ac32 100644 --- a/netbox/utilities/forms.py +++ b/netbox/utilities/forms.py @@ -14,8 +14,7 @@ from django.forms import BoundField from django.forms.models import fields_for_model from django.urls import reverse -from .choices import unpack_grouped_choices -from .constants import * +from .choices import ColorChoices, unpack_grouped_choices from .validators import EnhancedURLValidator NUMERIC_EXPANSION_PATTERN = r'\[((?:\d+[?:,-])+\d+)\]' @@ -163,7 +162,7 @@ class ColorSelect(forms.Select): option_template_name = 'widgets/colorselect_option.html' def __init__(self, *args, **kwargs): - kwargs['choices'] = add_blank_choice(COLOR_CHOICES) + kwargs['choices'] = add_blank_choice(ColorChoices) super().__init__(*args, **kwargs) self.attrs['class'] = 'netbox-select2-color-picker' @@ -607,15 +606,18 @@ class DynamicModelChoiceMixin: filter = django_filters.ModelChoiceFilter widget = APISelect - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + def _get_initial_value(self, initial_data, field_name): + return initial_data.get(field_name) def get_bound_field(self, form, field_name): bound_field = BoundField(form, self, field_name) + # Override initial() to allow passing multiple values + bound_field.initial = self._get_initial_value(form.initial, field_name) + # Modify the QuerySet of the field before we return it. Limit choices to any data already bound: Options # will be populated on-demand via the APISelect widget. - data = self.prepare_value(bound_field.data or bound_field.initial) + data = bound_field.value() if data: filter = self.filter(field_name=self.to_field_name or 'pk', queryset=self.queryset) self.queryset = filter.filter(self.queryset, data) @@ -648,6 +650,12 @@ class DynamicModelMultipleChoiceField(DynamicModelChoiceMixin, forms.ModelMultip filter = django_filters.ModelMultipleChoiceFilter widget = APISelectMultiple + def _get_initial_value(self, initial_data, field_name): + # If a QueryDict has been passed as initial form data, get *all* listed values + if hasattr(initial_data, 'getlist'): + return initial_data.getlist(field_name) + return initial_data.get(field_name) + class LaxURLField(forms.URLField): """