mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-14 01:41:22 -06:00
Merge branch 'develop' into select2-ui
This commit is contained in:
commit
d6d8b078b9
15
.github/ISSUE_TEMPLATE/bug_report.md
vendored
15
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -17,15 +17,20 @@ about: Report a reproducible bug in the current release of NetBox
|
|||||||
-->
|
-->
|
||||||
### Environment
|
### Environment
|
||||||
* Python version: <!-- Example: 3.5.4 -->
|
* Python version: <!-- Example: 3.5.4 -->
|
||||||
* NetBox version: <!-- Example: 2.3.6 -->
|
* NetBox version: <!-- Example: 2.5.2 -->
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Describe in detail the steps that someone else can take to reproduce this
|
Describe in detail the exact steps that someone else can take to reproduce
|
||||||
bug using the current stable release of NetBox (or the current beta release
|
this bug using the current stable release of NetBox (or the current beta
|
||||||
where applicable).
|
release where applicable). Begin with the creation of any necessary
|
||||||
|
database objects and call out every operation being performed explicitly.
|
||||||
|
If reporting a bug in the REST API, be sure to reconstruct the raw HTTP
|
||||||
|
request(s) being made: Don't rely on a wrapper like pynetbox.
|
||||||
-->
|
-->
|
||||||
### Steps to Reproduce
|
### Steps to Reproduce
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
|
||||||
<!-- What did you expect to happen? -->
|
<!-- What did you expect to happen? -->
|
||||||
### Expected Behavior
|
### Expected Behavior
|
||||||
|
3811
CHANGELOG.md
3811
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@ -2558,52 +2558,55 @@ class Cable(ChangeLoggedModel):
|
|||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
|
|
||||||
# Check that termination types are compatible
|
if self.termination_a and self.termination_b:
|
||||||
type_a = self.termination_a_type.model
|
|
||||||
type_b = self.termination_b_type.model
|
|
||||||
if type_b not in COMPATIBLE_TERMINATION_TYPES.get(type_a):
|
|
||||||
raise ValidationError("Incompatible termination types: {} and {}".format(
|
|
||||||
self.termination_a_type, self.termination_b_type
|
|
||||||
))
|
|
||||||
|
|
||||||
# A termination point cannot be connected to itself
|
type_a = self.termination_a_type.model
|
||||||
if self.termination_a == self.termination_b:
|
type_b = self.termination_b_type.model
|
||||||
raise ValidationError("Cannot connect {} to itself".format(self.termination_a_type))
|
|
||||||
|
|
||||||
# A front port cannot be connected to its corresponding rear port
|
# Check that termination types are compatible
|
||||||
if (
|
if type_b not in COMPATIBLE_TERMINATION_TYPES.get(type_a):
|
||||||
type_a in ['frontport', 'rearport'] and
|
raise ValidationError("Incompatible termination types: {} and {}".format(
|
||||||
type_b in ['frontport', 'rearport'] and
|
self.termination_a_type, self.termination_b_type
|
||||||
(
|
))
|
||||||
getattr(self.termination_a, 'rear_port', None) == self.termination_b or
|
|
||||||
getattr(self.termination_b, 'rear_port', None) == self.termination_a
|
|
||||||
)
|
|
||||||
):
|
|
||||||
raise ValidationError("A front port cannot be connected to it corresponding rear port")
|
|
||||||
|
|
||||||
# Check for an existing Cable connected to either termination object
|
# A termination point cannot be connected to itself
|
||||||
if self.termination_a.cable not in (None, self):
|
if self.termination_a == self.termination_b:
|
||||||
raise ValidationError("{} already has a cable attached (#{})".format(
|
raise ValidationError("Cannot connect {} to itself".format(self.termination_a_type))
|
||||||
self.termination_a, self.termination_a.cable_id
|
|
||||||
))
|
|
||||||
if self.termination_b.cable not in (None, self):
|
|
||||||
raise ValidationError("{} already has a cable attached (#{})".format(
|
|
||||||
self.termination_b, self.termination_b.cable_id
|
|
||||||
))
|
|
||||||
|
|
||||||
# Virtual interfaces cannot be connected
|
# A front port cannot be connected to its corresponding rear port
|
||||||
endpoint_a, endpoint_b, _ = self.get_path_endpoints()
|
if (
|
||||||
if (
|
type_a in ['frontport', 'rearport'] and
|
||||||
(
|
type_b in ['frontport', 'rearport'] and
|
||||||
isinstance(endpoint_a, Interface) and
|
(
|
||||||
endpoint_a.form_factor == IFACE_FF_VIRTUAL
|
getattr(self.termination_a, 'rear_port', None) == self.termination_b or
|
||||||
) or
|
getattr(self.termination_b, 'rear_port', None) == self.termination_a
|
||||||
(
|
)
|
||||||
isinstance(endpoint_b, Interface) and
|
):
|
||||||
endpoint_b.form_factor == IFACE_FF_VIRTUAL
|
raise ValidationError("A front port cannot be connected to it corresponding rear port")
|
||||||
)
|
|
||||||
):
|
# Check for an existing Cable connected to either termination object
|
||||||
raise ValidationError("Cannot connect to a virtual interface")
|
if self.termination_a.cable not in (None, self):
|
||||||
|
raise ValidationError("{} already has a cable attached (#{})".format(
|
||||||
|
self.termination_a, self.termination_a.cable_id
|
||||||
|
))
|
||||||
|
if self.termination_b.cable not in (None, self):
|
||||||
|
raise ValidationError("{} already has a cable attached (#{})".format(
|
||||||
|
self.termination_b, self.termination_b.cable_id
|
||||||
|
))
|
||||||
|
|
||||||
|
# Virtual interfaces cannot be connected
|
||||||
|
endpoint_a, endpoint_b, _ = self.get_path_endpoints()
|
||||||
|
if (
|
||||||
|
(
|
||||||
|
isinstance(endpoint_a, Interface) and
|
||||||
|
endpoint_a.form_factor == IFACE_FF_VIRTUAL
|
||||||
|
) or
|
||||||
|
(
|
||||||
|
isinstance(endpoint_b, Interface) and
|
||||||
|
endpoint_b.form_factor == IFACE_FF_VIRTUAL
|
||||||
|
)
|
||||||
|
):
|
||||||
|
raise ValidationError("Cannot connect to a virtual interface")
|
||||||
|
|
||||||
# Validate length and length_unit
|
# Validate length and length_unit
|
||||||
if self.length is not None and self.length_unit is None:
|
if self.length is not None and self.length_unit is None:
|
||||||
|
@ -162,7 +162,7 @@ class RegionBulkImportView(PermissionRequiredMixin, BulkImportView):
|
|||||||
|
|
||||||
class RegionBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
class RegionBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||||
permission_required = 'dcim.delete_region'
|
permission_required = 'dcim.delete_region'
|
||||||
queryset = Region.objects.annotate(site_count=Count('sites'))
|
queryset = Region.objects.all()
|
||||||
filter = filters.RegionFilter
|
filter = filters.RegionFilter
|
||||||
table = tables.RegionTable
|
table = tables.RegionTable
|
||||||
default_return_url = 'dcim:region_list'
|
default_return_url = 'dcim:region_list'
|
||||||
|
@ -996,6 +996,7 @@ class IPAddressFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
|||||||
choices=IPADDRESS_ROLE_CHOICES,
|
choices=IPADDRESS_ROLE_CHOICES,
|
||||||
required=False,
|
required=False,
|
||||||
widget=StaticSelect2Multiple()
|
widget=StaticSelect2Multiple()
|
||||||
|
include_null=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ except ImportError:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
VERSION = '2.5.3-dev'
|
VERSION = '2.5.4-dev'
|
||||||
|
|
||||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user