diff --git a/base_requirements.txt b/base_requirements.txt index 363f97b31..22106587d 100644 --- a/base_requirements.txt +++ b/base_requirements.txt @@ -4,7 +4,7 @@ bleach # The Python web framework on which NetBox is built # https://github.com/django/django -Django<4.1 +Django<4.2 # Django middleware which permits cross-domain API requests # https://github.com/OttoYiu/django-cors-headers diff --git a/docs/configuration/required-parameters.md b/docs/configuration/required-parameters.md index a62d14fef..15f743754 100644 --- a/docs/configuration/required-parameters.md +++ b/docs/configuration/required-parameters.md @@ -25,7 +25,7 @@ ALLOWED_HOSTS = ['*'] ## DATABASE -NetBox requires access to a PostgreSQL 10 or later database service to store data. This service can run locally on the NetBox server or on a remote system. The following parameters must be defined within the `DATABASE` dictionary: +NetBox requires access to a PostgreSQL 11 or later database service to store data. This service can run locally on the NetBox server or on a remote system. The following parameters must be defined within the `DATABASE` dictionary: * `NAME` - Database name * `USER` - PostgreSQL username diff --git a/docs/configuration/system.md b/docs/configuration/system.md index 21607e566..93f8fa902 100644 --- a/docs/configuration/system.md +++ b/docs/configuration/system.md @@ -58,7 +58,7 @@ Email is sent from NetBox only for critical events or if configured for [logging Default: None -A dictionary of HTTP proxies to use for outbound requests originating from NetBox (e.g. when sending webhook requests). Proxies should be specified by schema (HTTP and HTTPS) as per the [Python requests library documentation](https://2.python-requests.org/en/master/user/advanced/). For example: +A dictionary of HTTP proxies to use for outbound requests originating from NetBox (e.g. when sending webhook requests). Proxies should be specified by schema (HTTP and HTTPS) as per the [Python requests library documentation](https://requests.readthedocs.io/en/latest/user/advanced/#proxies). For example: ```python HTTP_PROXIES = { diff --git a/docs/installation/1-postgresql.md b/docs/installation/1-postgresql.md index a6aa27b1b..583a4f3e9 100644 --- a/docs/installation/1-postgresql.md +++ b/docs/installation/1-postgresql.md @@ -2,8 +2,8 @@ This section entails the installation and configuration of a local PostgreSQL database. If you already have a PostgreSQL database service in place, skip to [the next section](2-redis.md). -!!! warning "PostgreSQL 10 or later required" - NetBox requires PostgreSQL 10 or later. Please note that MySQL and other relational databases are **not** supported. +!!! warning "PostgreSQL 11 or later required" + NetBox requires PostgreSQL 11 or later. Please note that MySQL and other relational databases are **not** supported. ## Installation @@ -35,7 +35,7 @@ sudo systemctl start postgresql sudo systemctl enable postgresql ``` -Before continuing, verify that you have installed PostgreSQL 10 or later: +Before continuing, verify that you have installed PostgreSQL 11 or later: ```no-highlight psql -V diff --git a/docs/installation/index.md b/docs/installation/index.md index 8b588fccd..49163550d 100644 --- a/docs/installation/index.md +++ b/docs/installation/index.md @@ -18,7 +18,7 @@ The following sections detail how to set up a new instance of NetBox: | Dependency | Minimum Version | |------------|-----------------| | Python | 3.8 | -| PostgreSQL | 10 | +| PostgreSQL | 11 | | Redis | 4.0 | Below is a simplified overview of the NetBox application stack for reference: diff --git a/docs/installation/upgrading.md b/docs/installation/upgrading.md index 802c13e49..cc49cd30e 100644 --- a/docs/installation/upgrading.md +++ b/docs/installation/upgrading.md @@ -20,7 +20,7 @@ NetBox v3.0 and later require the following: | Dependency | Minimum Version | |------------|-----------------| | Python | 3.8 | -| PostgreSQL | 10 | +| PostgreSQL | 11 | | Redis | 4.0 | ## 3. Install the Latest Release @@ -28,16 +28,15 @@ NetBox v3.0 and later require the following: 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. !!! warning - Use the same method as you used to install Netbox originally + Use the same method as you used to install NetBox originally -If you are not sure how Netbox was installed originally, check with this -command: +If you are not sure how NetBox was installed originally, check with this command: ``` ls -ld /opt/netbox /opt/netbox/.git ``` -If Netbox was installed from a release package, then `/opt/netbox` will be a +If NetBox was installed from a release package, then `/opt/netbox` will be a symlink pointing to the current version, and `/opt/netbox/.git` will not exist. If it was installed from git, then `/opt/netbox` and `/opt/netbox/.git` will both exist as normal directories. diff --git a/docs/introduction.md b/docs/introduction.md index cffcb37dd..fe82e68aa 100644 --- a/docs/introduction.md +++ b/docs/introduction.md @@ -74,6 +74,6 @@ NetBox is built on the [Django](https://djangoproject.com/) Python framework and | HTTP service | nginx or Apache | | WSGI service | gunicorn or uWSGI | | Application | Django/Python | -| Database | PostgreSQL 10+ | +| Database | PostgreSQL 11+ | | Task queuing | Redis/django-rq | | Live device access | NAPALM (optional) | diff --git a/docs/plugins/development/models.md b/docs/plugins/development/models.md index c58621b81..16f5dd0df 100644 --- a/docs/plugins/development/models.md +++ b/docs/plugins/development/models.md @@ -49,24 +49,6 @@ class MyModel(NetBoxModel): ... ``` -### The `clone()` Method - -!!! info - This method was introduced in NetBox v3.3. - -The `NetBoxModel` class includes a `clone()` method to be used for gathering attributes which can be used to create a "cloned" instance. This is used primarily for form initialization, e.g. when using the "clone" button in the NetBox UI. By default, this method will replicate any fields listed in the model's `clone_fields` list, if defined. - -Plugin models can leverage this method by defining `clone_fields` as a list of field names to be replicated, or override this method to replace or extend its content: - -```python -class MyModel(NetBoxModel): - - def clone(self): - attrs = super().clone() - attrs['extra-value'] = 123 - return attrs -``` - ### Enabling Features Individually If you prefer instead to enable only a subset of these features for a plugin model, NetBox provides a discrete "mix-in" class for each feature. You can subclass each of these individually when defining your model. (Your model will also need to inherit from Django's built-in `Model` class.) @@ -116,6 +98,8 @@ For more information about database migrations, see the [Django documentation](h ::: netbox.models.features.ChangeLoggingMixin +::: netbox.models.features.CloningMixin + ::: netbox.models.features.CustomLinksMixin ::: netbox.models.features.CustomFieldsMixin diff --git a/docs/release-notes/version-3.3.md b/docs/release-notes/version-3.3.md index d779e1a93..2955e17d5 100644 --- a/docs/release-notes/version-3.3.md +++ b/docs/release-notes/version-3.3.md @@ -2,12 +2,17 @@ ## v3.3.5 (FUTURE) +### Bug Fixes + +* [#9497](https://github.com/netbox-community/netbox/issues/9497) - Adjust non-racked device filter on site and location detailed view +* [#10435](https://github.com/netbox-community/netbox/issues/10435) - Fix exception when filtering VLANs by virtual machine with no cluster assigned +* [#10439](https://github.com/netbox-community/netbox/issues/10439) - Fix form widget styling for DeviceType airflow field + --- ## v3.3.4 (2022-09-16) ### Bug Fixes - * [#10383](https://github.com/netbox-community/netbox/issues/10383) - Fix assignment of component templates to module types via web UI * [#10387](https://github.com/netbox-community/netbox/issues/10387) - Fix `MultiValueDictKeyError` exception when editing a device interface diff --git a/docs/release-notes/version-3.4.md b/docs/release-notes/version-3.4.md new file mode 100644 index 000000000..39c44f38e --- /dev/null +++ b/docs/release-notes/version-3.4.md @@ -0,0 +1,12 @@ +# NetBox v3.4 + +!!! warning "PostgreSQL 11 Required" + NetBox v3.4 requires PostgreSQL 11 or later. + +### Plugins API + +* [#10314](https://github.com/netbox-community/netbox/issues/10314) - Move `clone()` method from NetBoxModel to CloningMixin + +### Other Changes + +* [#10358](https://github.com/netbox-community/netbox/issues/10358) - Raise minimum required PostgreSQL version from 10 to 11 diff --git a/mkdocs.yml b/mkdocs.yml index 530c6d52e..8f6e2930a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -252,6 +252,7 @@ nav: - git Cheat Sheet: 'development/git-cheat-sheet.md' - Release Notes: - Summary: 'release-notes/index.md' + - Version 3.4: 'release-notes/version-3.4.md' - Version 3.3: 'release-notes/version-3.3.md' - Version 3.2: 'release-notes/version-3.2.md' - Version 3.1: 'release-notes/version-3.1.md' diff --git a/netbox/dcim/forms/models.py b/netbox/dcim/forms/models.py index 5728e7f2d..b33023ece 100644 --- a/netbox/dcim/forms/models.py +++ b/netbox/dcim/forms/models.py @@ -373,6 +373,7 @@ class DeviceTypeForm(NetBoxModelForm): 'front_image', 'rear_image', 'comments', 'tags', ] widgets = { + 'airflow': StaticSelect(), 'subdevice_role': StaticSelect(), 'front_image': ClearableFileInput(attrs={ 'accept': DEVICETYPE_IMAGE_FORMATS diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py index 8f1285901..c521ee095 100644 --- a/netbox/dcim/models/device_components.py +++ b/netbox/dcim/models/device_components.py @@ -955,12 +955,13 @@ class RearPort(ModularComponentModel, CabledObjectModel): super().clean() # Check that positions count is greater than or equal to the number of associated FrontPorts - frontport_count = self.frontports.count() - if self.positions < frontport_count: - raise ValidationError({ - "positions": f"The number of positions cannot be less than the number of mapped front ports " - f"({frontport_count})" - }) + if self.pk: + frontport_count = self.frontports.count() + if self.positions < frontport_count: + raise ValidationError({ + "positions": f"The number of positions cannot be less than the number of mapped front ports " + f"({frontport_count})" + }) # diff --git a/netbox/dcim/models/devices.py b/netbox/dcim/models/devices.py index ccf4613bf..7858960a1 100644 --- a/netbox/dcim/models/devices.py +++ b/netbox/dcim/models/devices.py @@ -268,7 +268,7 @@ class DeviceType(NetBoxModel): if ( self.subdevice_role != SubdeviceRoleChoices.ROLE_PARENT - ) and self.devicebaytemplates.count(): + ) and self.pk and self.devicebaytemplates.count(): raise ValidationError({ 'subdevice_role': "Must delete all device bay templates associated with this device before " "declassifying it as a parent device." diff --git a/netbox/ipam/querysets.py b/netbox/ipam/querysets.py index 7edac2eff..9f4463f61 100644 --- a/netbox/ipam/querysets.py +++ b/netbox/ipam/querysets.py @@ -81,30 +81,34 @@ class VLANQuerySet(RestrictedQuerySet): # Find all relevant VLANGroups q = Q() - if vm.cluster.site: - if vm.cluster.site.region: + site = vm.site or vm.cluster.site + if vm.cluster: + # Add VLANGroups scoped to the assigned cluster (or its group) + q |= Q( + scope_type=ContentType.objects.get_by_natural_key('virtualization', 'cluster'), + scope_id=vm.cluster_id + ) + if vm.cluster.group: q |= Q( - scope_type=ContentType.objects.get_by_natural_key('dcim', 'region'), - scope_id__in=vm.cluster.site.region.get_ancestors(include_self=True) - ) - if vm.cluster.site.group: - q |= Q( - scope_type=ContentType.objects.get_by_natural_key('dcim', 'sitegroup'), - scope_id__in=vm.cluster.site.group.get_ancestors(include_self=True) + scope_type=ContentType.objects.get_by_natural_key('virtualization', 'clustergroup'), + scope_id=vm.cluster.group_id ) + if site: + # Add VLANGroups scoped to the assigned site (or its group or region) q |= Q( scope_type=ContentType.objects.get_by_natural_key('dcim', 'site'), - scope_id=vm.cluster.site_id + scope_id=site.pk ) - if vm.cluster.group: - q |= Q( - scope_type=ContentType.objects.get_by_natural_key('virtualization', 'clustergroup'), - scope_id=vm.cluster.group_id - ) - q |= Q( - scope_type=ContentType.objects.get_by_natural_key('virtualization', 'cluster'), - scope_id=vm.cluster_id - ) + if site.region: + q |= Q( + scope_type=ContentType.objects.get_by_natural_key('dcim', 'region'), + scope_id__in=site.region.get_ancestors(include_self=True) + ) + if site.group: + q |= Q( + scope_type=ContentType.objects.get_by_natural_key('dcim', 'sitegroup'), + scope_id__in=site.group.get_ancestors(include_self=True) + ) vlan_groups = VLANGroup.objects.filter(q) # Return all applicable VLANs @@ -113,7 +117,7 @@ class VLANQuerySet(RestrictedQuerySet): Q(group__scope_id__isnull=True, site__isnull=True) | # Global group VLANs Q(group__isnull=True, site__isnull=True) # Global VLANs ) - if vm.cluster.site: - q |= Q(site=vm.cluster.site) + if site: + q |= Q(site=site) return self.filter(q) diff --git a/netbox/netbox/models/features.py b/netbox/netbox/models/features.py index 7f30248b4..9fa1c5cef 100644 --- a/netbox/netbox/models/features.py +++ b/netbox/netbox/models/features.py @@ -92,8 +92,17 @@ class CloningMixin(models.Model): def clone(self): """ - Return a dictionary of attributes suitable for creating a copy of the current instance. This is used for pre- - populating an object creation form in the UI. + Returns a dictionary of attributes suitable for creating a copy of the current instance. This is used for pre- + populating an object creation form in the UI. By default, this method will replicate any fields listed in the + model's `clone_fields` list (if defined), but it can be overridden to apply custom logic. + + ```python + class MyModel(NetBoxModel): + def clone(self): + attrs = super().clone() + attrs['extra-value'] = 123 + return attrs + ``` """ attrs = {} diff --git a/netbox/virtualization/models.py b/netbox/virtualization/models.py index abad57f88..4acbe6daf 100644 --- a/netbox/virtualization/models.py +++ b/netbox/virtualization/models.py @@ -367,7 +367,7 @@ class VirtualMachine(NetBoxModel, ConfigContextModel): }) # Validate primary IP addresses - interfaces = self.interfaces.all() + interfaces = self.interfaces.all() if self.pk else None for family in (4, 6): field = f'primary_ip{family}' ip = getattr(self, field) diff --git a/pyproject.toml b/pyproject.toml index 6d579b737..177b44d86 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,3 +11,12 @@ profile = "black" [tool.pylint] max-line-length = 120 + +[tool.pyright] +include = ["netbox"] +exclude = [ + "**/node_modules", + "**/__pycache__", +] +reportMissingImports = true +reportMissingTypeStubs = false diff --git a/requirements.txt b/requirements.txt index f868c4f0d..af91cf35b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ bleach==5.0.1 -Django==4.0.7 +Django==4.1.1 django-cors-headers==3.13.0 django-debug-toolbar==3.6.0 django-filter==22.1