1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Merge branch 'develop' into develop-2.10

This commit is contained in:
Jeremy Stretch
2020-12-11 16:57:04 -05:00
12 changed files with 101 additions and 31 deletions

View File

@ -7,11 +7,9 @@ about: Report a reproducible bug in the current release of NetBox
<!-- <!--
NOTE: IF YOUR ISSUE DOES NOT FOLLOW THIS TEMPLATE, IT WILL BE CLOSED. NOTE: IF YOUR ISSUE DOES NOT FOLLOW THIS TEMPLATE, IT WILL BE CLOSED.
This form is only for reproducible bugs. If you need assistance with This form is only for reporting reproducible bugs. If you need assistance
NetBox installation, or if you have a general question, DO NOT open an with NetBox installation, or if you have a general question, please start a
issue. Instead, post to our mailing list: discussion instead: https://github.com/netbox-community/netbox/discussions
https://groups.google.com/g/netbox-discuss
Please describe the environment in which you are running NetBox. Be sure Please describe the environment in which you are running NetBox. Be sure
that you are running an unmodified instance of the latest stable release that you are running an unmodified instance of the latest stable release

View File

@ -8,10 +8,8 @@ about: Propose a new NetBox feature or enhancement
NOTE: IF YOUR ISSUE DOES NOT FOLLOW THIS TEMPLATE, IT WILL BE CLOSED. NOTE: IF YOUR ISSUE DOES NOT FOLLOW THIS TEMPLATE, IT WILL BE CLOSED.
This form is only for proposing specific new features or enhancements. This form is only for proposing specific new features or enhancements.
If you have a general idea or question, please post to our mailing list If you have a general idea or question, please start a discussion instead:
instead of opening an issue: https://github.com/netbox-community/netbox/discussions
https://groups.google.com/g/netbox-discuss
NOTE: Due to an excessive backlog of feature requests, we are not currently NOTE: Due to an excessive backlog of feature requests, we are not currently
accepting any proposals which significantly extend NetBox's feature scope. accepting any proposals which significantly extend NetBox's feature scope.

View File

@ -37,18 +37,21 @@ or join us in the **#netbox** Slack channel on [NetworkToCode](https://networkto
## Installation ## Installation
Please see [the documentation](http://netbox.readthedocs.io/en/stable/) for 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/netbox-community/netbox/releases) instructions on installing NetBox. To upgrade NetBox, please download the
and run `upgrade.sh`. [latest release](https://github.com/netbox-community/netbox/releases) and
run `upgrade.sh`.
## Providing Feedback ## Providing Feedback
Feature requests and bug reports must be submitted as GiHub issues. (Please be The best platform for general feedback, assistance, and other discussion is our
sure to use the [appropriate template](https://github.com/netbox-community/netbox/issues/new/choose).) [GitHub discussions](https://github.com/netbox-community/netbox/discussions).
For general discussion, please consider joining our [mailing list](https://groups.google.com/g/netbox-discuss). To report a bug or request a specific feature, please open a GitHub issue using
the [appropriate template](https://github.com/netbox-community/netbox/issues/new/choose).
If you are interested in contributing to the development of NetBox, please read If you are interested in contributing to the development of NetBox, please read
our [contributing guide](CONTRIBUTING.md) prior to beginning any work. our [contributing guide](CONTRIBUTING.md) prior to beginning any work.
## Related projects ## Related projects
Please see [our wiki](https://github.com/netbox-community/netbox/wiki/Community-Contributions) for a list of relevant community projects. Please see [our wiki](https://github.com/netbox-community/netbox/wiki/Community-Contributions)
for a list of relevant community projects.

View File

@ -7,7 +7,8 @@ NetBox is maintained as a [GitHub project](https://github.com/netbox-community/n
Communication among developers should always occur via public channels: Communication among developers should always occur via public channels:
* [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. * [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/g/netbox-discuss) - The preferred forum for general discussion and support issues. Ideal for shaping a feature request prior to submitting an issue. * [GitHub discussions](https://github.com/netbox-community/netbox/discussions) - The preferred forum for general discussion and support issues. Ideal for shaping a feature request prior to submitting an issue.
* [The mailing list](https://groups.google.com/g/netbox-discuss) - An alternative forum for general discussion (GitHub is preferred).
* [#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. * [#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.
## Governance ## Governance

View File

@ -1,19 +1,23 @@
# NetBox v2.9 # NetBox v2.9
## v2.9.11 (FUTURE) ## v2.9.11 (2020-12-11)
### Enhancements ### Enhancements
* [#5424](https://github.com/netbox-community/netbox/issues/5424) - Allow passing Python code to `nbshell` using `--command` * [#5424](https://github.com/netbox-community/netbox/issues/5424) - Allow passing Python code to `nbshell` using `--command`
* [#5439](https://github.com/netbox-community/netbox/issues/5439) - Add CS and SN fiber port types
### Bug Fixes ### Bug Fixes
* [#5383](https://github.com/netbox-community/netbox/issues/5383) - Fix setting user password via REST API * [#5383](https://github.com/netbox-community/netbox/issues/5383) - Fix setting user password via REST API
* [#5396](https://github.com/netbox-community/netbox/issues/5396) - Fix uniqueness constraint for virtual machine names * [#5396](https://github.com/netbox-community/netbox/issues/5396) - Fix uniqueness constraint for virtual machine names
* [#5387](https://github.com/netbox-community/netbox/issues/5387) - Fix error when rendering config contexts when objects have multiple tags assigned
* [#5407](https://github.com/netbox-community/netbox/issues/5407) - Add direct link to secret on secrets list * [#5407](https://github.com/netbox-community/netbox/issues/5407) - Add direct link to secret on secrets list
* [#5408](https://github.com/netbox-community/netbox/issues/5408) - Fix updating secrets without setting new plaintext * [#5408](https://github.com/netbox-community/netbox/issues/5408) - Fix updating secrets without setting new plaintext
* [#5410](https://github.com/netbox-community/netbox/issues/5410) - Restore tags field on cable connection forms * [#5410](https://github.com/netbox-community/netbox/issues/5410) - Restore tags field on cable connection forms
* [#5433](https://github.com/netbox-community/netbox/issues/5433) - Exclude SVG files from front/rear image upload for device types (currently unsupported)
* [#5436](https://github.com/netbox-community/netbox/issues/5436) - Show assigned IP addresses in interfaces list * [#5436](https://github.com/netbox-community/netbox/issues/5436) - Show assigned IP addresses in interfaces list
* [#5446](https://github.com/netbox-community/netbox/issues/5446) - Fix validation for plugin version and required settings
--- ---

View File

@ -887,6 +887,8 @@ class PortTypeChoices(ChoiceSet):
TYPE_LSH = 'lsh' TYPE_LSH = 'lsh'
TYPE_LSH_APC = 'lsh-apc' TYPE_LSH_APC = 'lsh-apc'
TYPE_SPLICE = 'splice' TYPE_SPLICE = 'splice'
TYPE_CS = 'cs'
TYPE_SN = 'sn'
CHOICES = ( CHOICES = (
( (
@ -914,6 +916,8 @@ class PortTypeChoices(ChoiceSet):
(TYPE_SC, 'SC'), (TYPE_SC, 'SC'),
(TYPE_SC_APC, 'SC/APC'), (TYPE_SC_APC, 'SC/APC'),
(TYPE_ST, 'ST'), (TYPE_ST, 'ST'),
(TYPE_CS, 'CS'),
(TYPE_SN, 'SN'),
(TYPE_SPLICE, 'Splice'), (TYPE_SPLICE, 'Splice'),
) )
) )

View File

@ -922,7 +922,14 @@ class DeviceTypeForm(BootstrapMixin, CustomFieldModelForm):
'front_image', 'rear_image', 'comments', 'tags', 'front_image', 'rear_image', 'comments', 'tags',
] ]
widgets = { widgets = {
'subdevice_role': StaticSelect2() 'subdevice_role': StaticSelect2(),
# Exclude SVG images (unsupported by PIL)
'front_image': forms.FileInput(attrs={
'accept': 'image/bmp,image/gif,image/jpeg,image/png,image/tiff'
}),
'rear_image': forms.FileInput(attrs={
'accept': 'image/bmp,image/gif,image/jpeg,image/png,image/tiff'
})
} }

View File

@ -3,7 +3,6 @@ import inspect
from packaging import version from packaging import version
from django.apps import AppConfig from django.apps import AppConfig
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.template.loader import get_template from django.template.loader import get_template
@ -71,10 +70,10 @@ class PluginConfig(AppConfig):
register_menu_items(self.verbose_name, menu_items) register_menu_items(self.verbose_name, menu_items)
@classmethod @classmethod
def validate(cls, user_config): def validate(cls, user_config, netbox_version):
# Enforce version constraints # Enforce version constraints
current_version = version.parse(settings.VERSION) current_version = version.parse(netbox_version)
if cls.min_version is not None: if cls.min_version is not None:
min_version = version.parse(cls.min_version) min_version = version.parse(cls.min_version)
if current_version < min_version: if current_version < min_version:

View File

@ -1,5 +1,6 @@
from django.db.models import OuterRef, Subquery, Q from django.db.models import OuterRef, Subquery, Q
from extras.models.tags import TaggedItem
from utilities.query_functions import EmptyGroupByJSONBAgg, OrderableJSONBAgg from utilities.query_functions import EmptyGroupByJSONBAgg, OrderableJSONBAgg
from utilities.querysets import RestrictedQuerySet from utilities.querysets import RestrictedQuerySet
@ -81,11 +82,25 @@ class ConfigContextModelQuerySet(RestrictedQuerySet):
def _get_config_context_filters(self): def _get_config_context_filters(self):
# Construct the set of Q objects for the specific object types # Construct the set of Q objects for the specific object types
tag_query_filters = {
"object_id": OuterRef(OuterRef('pk')),
"content_type__app_label": self.model._meta.app_label,
"content_type__model": self.model._meta.model_name
}
base_query = Q( base_query = Q(
Q(platforms=OuterRef('platform')) | Q(platforms=None), Q(platforms=OuterRef('platform')) | Q(platforms=None),
Q(tenant_groups=OuterRef('tenant__group')) | Q(tenant_groups=None), Q(tenant_groups=OuterRef('tenant__group')) | Q(tenant_groups=None),
Q(tenants=OuterRef('tenant')) | Q(tenants=None), Q(tenants=OuterRef('tenant')) | Q(tenants=None),
Q(tags=OuterRef('tags')) | Q(tags=None), Q(
tags__pk__in=Subquery(
TaggedItem.objects.filter(
**tag_query_filters
).values_list(
'tag_id',
flat=True
)
)
) | Q(tags=None),
is_active=True, is_active=True,
) )

View File

@ -321,3 +321,46 @@ class ConfigContextTest(TestCase):
annotated_queryset = Device.objects.filter(name=device.name).annotate_config_context_data() annotated_queryset = Device.objects.filter(name=device.name).annotate_config_context_data()
self.assertEqual(ConfigContext.objects.get_for_object(device).count(), 1) self.assertEqual(ConfigContext.objects.get_for_object(device).count(), 1)
self.assertEqual(device.get_config_context(), annotated_queryset[0].get_config_context()) self.assertEqual(device.get_config_context(), annotated_queryset[0].get_config_context())
def test_multiple_tags_return_distinct_objects_with_seperate_config_contexts(self):
"""
Tagged items use a generic relationship, which results in duplicate rows being returned when queried.
This is combatted by by appending distinct() to the config context querysets. This test creates a config
context assigned to two tags and ensures objects related by those same two tags result in only a single
config context record being returned.
This test case is seperate from the above in that it deals with multiple config context objects in play.
See https://github.com/netbox-community/netbox/issues/5387
"""
tag_context_1 = ConfigContext.objects.create(
name="tag-1",
weight=100,
data={
"tag": 1
}
)
tag_context_1.tags.add(self.tag)
tag_context_2 = ConfigContext.objects.create(
name="tag-2",
weight=100,
data={
"tag": 1
}
)
tag_context_2.tags.add(self.tag2)
device = Device.objects.create(
name="Device 3",
site=self.site,
tenant=self.tenant,
platform=self.platform,
device_role=self.devicerole,
device_type=self.devicetype
)
device.tags.add(self.tag)
device.tags.add(self.tag2)
annotated_queryset = Device.objects.filter(name=device.name).annotate_config_context_data()
self.assertEqual(ConfigContext.objects.get_for_object(device).count(), 2)
self.assertEqual(device.get_config_context(), annotated_queryset[0].get_config_context())

View File

@ -86,21 +86,19 @@ class PluginTest(TestCase):
""" """
self.assertIn('extras.tests.dummy_plugin.*', settings.CACHEOPS) self.assertIn('extras.tests.dummy_plugin.*', settings.CACHEOPS)
@override_settings(VERSION='0.9')
def test_min_version(self): def test_min_version(self):
""" """
Check enforcement of minimum NetBox version. Check enforcement of minimum NetBox version.
""" """
with self.assertRaises(ImproperlyConfigured): with self.assertRaises(ImproperlyConfigured):
dummy_config.validate({}) dummy_config.validate({}, '0.9')
@override_settings(VERSION='10.0')
def test_max_version(self): def test_max_version(self):
""" """
Check enforcement of maximum NetBox version. Check enforcement of maximum NetBox version.
""" """
with self.assertRaises(ImproperlyConfigured): with self.assertRaises(ImproperlyConfigured):
dummy_config.validate({}) dummy_config.validate({}, '10.0')
def test_required_settings(self): def test_required_settings(self):
""" """
@ -110,11 +108,11 @@ class PluginTest(TestCase):
required_settings = ['foo'] required_settings = ['foo']
# Validation should pass when all required settings are present # Validation should pass when all required settings are present
DummyConfigWithRequiredSettings.validate({'foo': True}) DummyConfigWithRequiredSettings.validate({'foo': True}, settings.VERSION)
# Validation should fail when a required setting is missing # Validation should fail when a required setting is missing
with self.assertRaises(ImproperlyConfigured): with self.assertRaises(ImproperlyConfigured):
DummyConfigWithRequiredSettings.validate({}) DummyConfigWithRequiredSettings.validate({}, settings.VERSION)
def test_default_settings(self): def test_default_settings(self):
""" """
@ -127,10 +125,10 @@ class PluginTest(TestCase):
# Populate the default value if setting has not been specified # Populate the default value if setting has not been specified
user_config = {} user_config = {}
DummyConfigWithDefaultSettings.validate(user_config) DummyConfigWithDefaultSettings.validate(user_config, settings.VERSION)
self.assertEqual(user_config['bar'], 123) self.assertEqual(user_config['bar'], 123)
# Don't overwrite specified values # Don't overwrite specified values
user_config = {'bar': 456} user_config = {'bar': 456}
DummyConfigWithDefaultSettings.validate(user_config) DummyConfigWithDefaultSettings.validate(user_config, settings.VERSION)
self.assertEqual(user_config['bar'], 456) self.assertEqual(user_config['bar'], 456)

View File

@ -609,7 +609,7 @@ for plugin_name in PLUGINS:
# Validate user-provided configuration settings and assign defaults # Validate user-provided configuration settings and assign defaults
if plugin_name not in PLUGINS_CONFIG: if plugin_name not in PLUGINS_CONFIG:
PLUGINS_CONFIG[plugin_name] = {} PLUGINS_CONFIG[plugin_name] = {}
plugin_config.validate(PLUGINS_CONFIG[plugin_name]) plugin_config.validate(PLUGINS_CONFIG[plugin_name], VERSION)
# Add middleware # Add middleware
plugin_middleware = plugin_config.middleware plugin_middleware = plugin_config.middleware