mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-14 09:51:22 -06:00
Merge branch 'develop' into feature
This commit is contained in:
commit
f93d6813a9
5
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
5
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@ -13,10 +13,7 @@ body:
|
|||||||
- type: input
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: NetBox version
|
label: NetBox version
|
||||||
description: >
|
description: What version of NetBox are you currently running?
|
||||||
What version of NetBox are you currently running? (If you don't have access to the most
|
|
||||||
recent NetBox release, consider testing on our [demo instance](https://demo.netbox.dev/)
|
|
||||||
before opening a bug report to see if your issue has already been addressed.)
|
|
||||||
placeholder: v3.0.9
|
placeholder: v3.0.9
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
@ -76,14 +76,10 @@ free to add a comment with any additional justification for the feature.
|
|||||||
(However, note that comments with no substance other than a "+1" will be
|
(However, note that comments with no substance other than a "+1" will be
|
||||||
deleted. Please use GitHub's reactions feature to indicate your support.)
|
deleted. Please use GitHub's reactions feature to indicate your support.)
|
||||||
|
|
||||||
* Due to a large backlog of feature requests, we are not currently accepting
|
* Before filing a new feature request, consider raising your idea in a
|
||||||
any proposals which substantially extend NetBox's functionality beyond its
|
[GitHub discussion](https://github.com/netbox-community/netbox/discussions)
|
||||||
current feature set. This includes the introduction of any new views or models
|
first. Feedback you receive there will help validate and shape the proposed
|
||||||
which have not already been proposed in an existing feature request.
|
feature before filing a formal issue.
|
||||||
|
|
||||||
* Before filing a new feature request, consider raising your idea on the
|
|
||||||
mailing list first. Feedback you receive there will help validate and shape the
|
|
||||||
proposed feature before filing a formal issue.
|
|
||||||
|
|
||||||
* Good feature requests are very narrowly defined. Be sure to thoroughly
|
* Good feature requests are very narrowly defined. Be sure to thoroughly
|
||||||
describe the functionality and data model(s) being proposed. The more effort
|
describe the functionality and data model(s) being proposed. The more effort
|
||||||
|
@ -27,3 +27,13 @@ Device components represent discrete objects within a device which are used to t
|
|||||||
---
|
---
|
||||||
|
|
||||||
{!models/dcim/cable.md!}
|
{!models/dcim/cable.md!}
|
||||||
|
|
||||||
|
In the example below, three individual cables comprise a path between devices A and D:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Traced from Interface 1 on Device A, NetBox will show the following path:
|
||||||
|
|
||||||
|
* Cable 1: Interface 1 to Front Port 1
|
||||||
|
* Cable 2: Rear Port 1 to Rear Port 2
|
||||||
|
* Cable 3: Front Port 2 to Interface 2
|
||||||
|
@ -22,13 +22,3 @@ Each cable may be assigned a type, label, length, and color. Each cable is also
|
|||||||
## Tracing Cables
|
## Tracing Cables
|
||||||
|
|
||||||
A cable may be traced from either of its endpoints by clicking the "trace" button. (A REST API endpoint also provides this functionality.) NetBox will follow the path of connected cables from this termination across the directly connected cable to the far-end termination. If the cable connects to a pass-through port, and the peer port has another cable connected, NetBox will continue following the cable path until it encounters a non-pass-through or unconnected termination point. The entire path will be displayed to the user.
|
A cable may be traced from either of its endpoints by clicking the "trace" button. (A REST API endpoint also provides this functionality.) NetBox will follow the path of connected cables from this termination across the directly connected cable to the far-end termination. If the cable connects to a pass-through port, and the peer port has another cable connected, NetBox will continue following the cable path until it encounters a non-pass-through or unconnected termination point. The entire path will be displayed to the user.
|
||||||
|
|
||||||
In the example below, three individual cables comprise a path between devices A and D:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Traced from Interface 1 on Device A, NetBox will show the following path:
|
|
||||||
|
|
||||||
* Cable 1: Interface 1 to Front Port 1
|
|
||||||
* Cable 2: Rear Port 1 to Rear Port 2
|
|
||||||
* Cable 3: Front Port 2 to Interface 2
|
|
||||||
|
@ -2,9 +2,19 @@
|
|||||||
|
|
||||||
## v3.0.10 (FUTURE)
|
## v3.0.10 (FUTURE)
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
|
||||||
|
* [#7740](https://github.com/netbox-community/netbox/issues/7740) - Add mini-DIN 8 console port type
|
||||||
|
* [#7760](https://github.com/netbox-community/netbox/issues/7760) - Add `vid` filter field to VLANs list
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
|
* [#7701](https://github.com/netbox-community/netbox/issues/7701) - Fix conflation of assigned IP status & role in interface tables
|
||||||
|
* [#7741](https://github.com/netbox-community/netbox/issues/7741) - Fix 404 when attaching multiple images in succession
|
||||||
* [#7752](https://github.com/netbox-community/netbox/issues/7752) - Fix minimum version check under Python v3.10
|
* [#7752](https://github.com/netbox-community/netbox/issues/7752) - Fix minimum version check under Python v3.10
|
||||||
|
* [#7766](https://github.com/netbox-community/netbox/issues/7766) - Add missing outer dimension columns to rack table
|
||||||
|
* [#7780](https://github.com/netbox-community/netbox/issues/7780) - Preserve mutli-line values during CSV file import
|
||||||
|
* [#7783](https://github.com/netbox-community/netbox/issues/7783) - Fix indentation of locations under site view
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -204,6 +204,7 @@ class ConsolePortTypeChoices(ChoiceSet):
|
|||||||
TYPE_RJ11 = 'rj-11'
|
TYPE_RJ11 = 'rj-11'
|
||||||
TYPE_RJ12 = 'rj-12'
|
TYPE_RJ12 = 'rj-12'
|
||||||
TYPE_RJ45 = 'rj-45'
|
TYPE_RJ45 = 'rj-45'
|
||||||
|
TYPE_MINI_DIN_8 = 'mini-din-8'
|
||||||
TYPE_USB_A = 'usb-a'
|
TYPE_USB_A = 'usb-a'
|
||||||
TYPE_USB_B = 'usb-b'
|
TYPE_USB_B = 'usb-b'
|
||||||
TYPE_USB_C = 'usb-c'
|
TYPE_USB_C = 'usb-c'
|
||||||
@ -221,6 +222,7 @@ class ConsolePortTypeChoices(ChoiceSet):
|
|||||||
(TYPE_RJ11, 'RJ-11'),
|
(TYPE_RJ11, 'RJ-11'),
|
||||||
(TYPE_RJ12, 'RJ-12'),
|
(TYPE_RJ12, 'RJ-12'),
|
||||||
(TYPE_RJ45, 'RJ-45'),
|
(TYPE_RJ45, 'RJ-45'),
|
||||||
|
(TYPE_MINI_DIN_8, 'Mini-DIN 8'),
|
||||||
)),
|
)),
|
||||||
('USB', (
|
('USB', (
|
||||||
(TYPE_USB_A, 'USB Type A'),
|
(TYPE_USB_A, 'USB Type A'),
|
||||||
|
@ -75,12 +75,20 @@ class RackTable(BaseTable):
|
|||||||
tags = TagColumn(
|
tags = TagColumn(
|
||||||
url_name='dcim:rack_list'
|
url_name='dcim:rack_list'
|
||||||
)
|
)
|
||||||
|
outer_width = tables.TemplateColumn(
|
||||||
|
template_code="{{ record.outer_width }} {{ record.outer_unit }}",
|
||||||
|
verbose_name='Outer Width'
|
||||||
|
)
|
||||||
|
outer_depth = tables.TemplateColumn(
|
||||||
|
template_code="{{ record.outer_depth }} {{ record.outer_unit }}",
|
||||||
|
verbose_name='Outer Depth'
|
||||||
|
)
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = Rack
|
model = Rack
|
||||||
fields = (
|
fields = (
|
||||||
'pk', 'id', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'role', 'serial', 'asset_tag', 'type',
|
'pk', 'id', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'role', 'serial', 'asset_tag', 'type',
|
||||||
'width', 'u_height', 'comments', 'device_count', 'get_utilization', 'get_power_utilization', 'tags',
|
'width', 'outer_width', 'outer_depth', 'u_height', 'comments', 'device_count', 'get_utilization', 'get_power_utilization', 'tags',
|
||||||
)
|
)
|
||||||
default_columns = (
|
default_columns = (
|
||||||
'pk', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'role', 'u_height', 'device_count',
|
'pk', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'role', 'u_height', 'device_count',
|
||||||
|
@ -41,15 +41,11 @@ DEVICEBAY_STATUS = """
|
|||||||
INTERFACE_IPADDRESSES = """
|
INTERFACE_IPADDRESSES = """
|
||||||
<div class="table-badge-group">
|
<div class="table-badge-group">
|
||||||
{% for ip in record.ip_addresses.all %}
|
{% for ip in record.ip_addresses.all %}
|
||||||
<a
|
{% if ip.status != 'active' %}
|
||||||
class="table-badge{% if ip.status != 'active' %} badge bg-{{ ip.get_status_class }}{% elif ip.role %} badge bg-{{ ip.get_role_class }}{% endif %}"
|
<a href="{{ ip.get_absolute_url }}" class="table-badge badge bg-{{ ip.get_status_class }}" data-bs-toggle="tooltip" data-bs-placement="left" title="{{ ip.get_status_display }}">{{ ip }}</a>
|
||||||
href="{{ ip.get_absolute_url }}"
|
{% else %}
|
||||||
{% if ip.status != 'active'%}data-bs-toggle="tooltip" data-bs-placement="left" title="{{ ip.get_status_display }}"
|
<a href="{{ ip.get_absolute_url }}" class="table-badge">{{ ip }}</a>
|
||||||
{% elif ip.role %}data-bs-toggle="tooltip" data-bs-placement="left" title="{{ ip.get_role_display }}"
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
>
|
|
||||||
{{ ip }}
|
|
||||||
</a>
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
|
@ -353,6 +353,8 @@ class ImageAttachment(BigIDModel):
|
|||||||
|
|
||||||
objects = RestrictedQuerySet.as_manager()
|
objects = RestrictedQuerySet.as_manager()
|
||||||
|
|
||||||
|
clone_fields = ('content_type', 'object_id')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('name', 'pk') # name may be non-unique
|
ordering = ('name', 'pk') # name may be non-unique
|
||||||
|
|
||||||
|
@ -475,11 +475,7 @@ class ImageAttachmentEditView(generic.ObjectEditView):
|
|||||||
def alter_obj(self, instance, request, args, kwargs):
|
def alter_obj(self, instance, request, args, kwargs):
|
||||||
if not instance.pk:
|
if not instance.pk:
|
||||||
# Assign the parent object based on URL kwargs
|
# Assign the parent object based on URL kwargs
|
||||||
try:
|
content_type = get_object_or_404(ContentType, pk=request.GET.get('content_type'))
|
||||||
app_label, model = request.GET.get('content_type').split('.')
|
|
||||||
except (AttributeError, ValueError):
|
|
||||||
raise Http404("Content type not specified")
|
|
||||||
content_type = get_object_or_404(ContentType, app_label=app_label, model=model)
|
|
||||||
instance.parent = get_object_or_404(content_type.model_class(), pk=request.GET.get('object_id'))
|
instance.parent = get_object_or_404(content_type.model_class(), pk=request.GET.get('object_id'))
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import django_filters
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
@ -471,7 +472,7 @@ class VLANFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilterFo
|
|||||||
field_groups = [
|
field_groups = [
|
||||||
['q', 'tag'],
|
['q', 'tag'],
|
||||||
['region_id', 'site_group_id', 'site_id'],
|
['region_id', 'site_group_id', 'site_id'],
|
||||||
['group_id', 'status', 'role_id'],
|
['group_id', 'status', 'role_id', 'vid'],
|
||||||
['tenant_group_id', 'tenant_id'],
|
['tenant_group_id', 'tenant_id'],
|
||||||
]
|
]
|
||||||
q = forms.CharField(
|
q = forms.CharField(
|
||||||
@ -523,6 +524,10 @@ class VLANFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilterFo
|
|||||||
label=_('Role'),
|
label=_('Role'),
|
||||||
fetch_trigger='open'
|
fetch_trigger='open'
|
||||||
)
|
)
|
||||||
|
vid = forms.IntegerField(
|
||||||
|
required=False,
|
||||||
|
label='VLAN ID'
|
||||||
|
)
|
||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
|
BIN
netbox/project-static/dist/netbox-dark.css
vendored
BIN
netbox/project-static/dist/netbox-dark.css
vendored
Binary file not shown.
BIN
netbox/project-static/dist/netbox-light.css
vendored
BIN
netbox/project-static/dist/netbox-light.css
vendored
Binary file not shown.
BIN
netbox/project-static/dist/netbox-print.css
vendored
BIN
netbox/project-static/dist/netbox-print.css
vendored
Binary file not shown.
@ -814,7 +814,7 @@ table .table-badge-group {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.badge:not(:last-of-type):not(:only-child) {
|
&.badge:not(:last-of-type):not(:only-child) {
|
||||||
margin-bottom: map.get($spacers, 2);
|
margin-bottom: map.get($spacers, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -236,8 +236,8 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{% for location in locations %}
|
{% for location in locations %}
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding-left: {{ location.level }}8px">
|
<td>
|
||||||
<i class="mdi mdi-folder-open"></i>
|
{% for i in location.level|as_range %}<i class="mdi mdi-circle-small"></i>{% endfor %}
|
||||||
<a href="{{ location.get_absolute_url }}">{{ location }}</a>
|
<a href="{{ location.get_absolute_url }}">{{ location }}</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
@ -44,7 +44,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{% if perms.extras.add_imageattachment %}
|
{% if perms.extras.add_imageattachment %}
|
||||||
<div class="card-footer text-end noprint">
|
<div class="card-footer text-end noprint">
|
||||||
<a href="{% url 'extras:imageattachment_add' %}?content_type={{ object|meta:"app_label" }}.{{ object|meta:"model_name" }}&object_id={{ object.pk }}" class="btn btn-primary btn-sm">
|
<a href="{% url 'extras:imageattachment_add' %}?content_type={{ object|content_type_id }}&object_id={{ object.pk }}" class="btn btn-primary btn-sm">
|
||||||
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> Attach an image
|
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> Attach an image
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -248,7 +248,7 @@ class CSVFileField(forms.FileField):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
csv_str = file.read().decode('utf-8').strip()
|
csv_str = file.read().decode('utf-8').strip()
|
||||||
reader = csv.reader(csv_str.splitlines())
|
reader = csv.reader(StringIO(csv_str))
|
||||||
headers, records = parse_csv(reader)
|
headers, records = parse_csv(reader)
|
||||||
|
|
||||||
return headers, records
|
return headers, records
|
||||||
|
@ -7,6 +7,7 @@ from typing import Dict, Any
|
|||||||
import yaml
|
import yaml
|
||||||
from django import template
|
from django import template
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.template.defaultfilters import date
|
from django.template.defaultfilters import date
|
||||||
from django.urls import NoReverseMatch, reverse
|
from django.urls import NoReverseMatch, reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
@ -80,6 +81,25 @@ def meta(obj, attr):
|
|||||||
return getattr(obj._meta, attr, '')
|
return getattr(obj._meta, attr, '')
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter()
|
||||||
|
def content_type(obj):
|
||||||
|
"""
|
||||||
|
Return the ContentType for the given object.
|
||||||
|
"""
|
||||||
|
return ContentType.objects.get_for_model(obj)
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter()
|
||||||
|
def content_type_id(obj):
|
||||||
|
"""
|
||||||
|
Return the ContentType ID for the given object.
|
||||||
|
"""
|
||||||
|
content_type = ContentType.objects.get_for_model(obj)
|
||||||
|
if content_type:
|
||||||
|
return content_type.pk
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
@register.filter()
|
@register.filter()
|
||||||
def viewname(model, action):
|
def viewname(model, action):
|
||||||
"""
|
"""
|
||||||
|
@ -11,6 +11,7 @@ exec 1>&2
|
|||||||
|
|
||||||
EXIT=0
|
EXIT=0
|
||||||
RED='\033[0;31m'
|
RED='\033[0;31m'
|
||||||
|
YELLOW='\033[0;33m'
|
||||||
NOCOLOR='\033[0m'
|
NOCOLOR='\033[0m'
|
||||||
|
|
||||||
if [ -d ./venv/ ]; then
|
if [ -d ./venv/ ]; then
|
||||||
@ -22,6 +23,11 @@ if [ -d ./venv/ ]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ ${NOVALIDATE} ]; then
|
||||||
|
echo "${YELLOW}Skipping validation checks${NOCOLOR}"
|
||||||
|
exit $EXIT
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Validating PEP8 compliance..."
|
echo "Validating PEP8 compliance..."
|
||||||
pycodestyle --ignore=W504,E501 --exclude=node_modules netbox/
|
pycodestyle --ignore=W504,E501 --exclude=node_modules netbox/
|
||||||
if [ $? != 0 ]; then
|
if [ $? != 0 ]; then
|
||||||
|
Loading…
Reference in New Issue
Block a user