mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Misc cleanup
This commit is contained in:
@ -13,6 +13,9 @@ Physical interfaces may be arranged into a link aggregation group (LAG) and asso
|
|||||||
|
|
||||||
### Power over Ethernet (PoE)
|
### Power over Ethernet (PoE)
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
This feature was added in NetBox v3.3.
|
||||||
|
|
||||||
Physical interfaces can be assigned a PoE mode to indicate PoE capability: power supplying equipment (PSE) or powered device (PD). Additionally, a PoE mode may be specified. This can be one of the listed IEEE 802.3 standards, or a passive setting (24 or 48 volts across two or four pairs).
|
Physical interfaces can be assigned a PoE mode to indicate PoE capability: power supplying equipment (PSE) or powered device (PD). Additionally, a PoE mode may be specified. This can be one of the listed IEEE 802.3 standards, or a passive setting (24 or 48 volts across two or four pairs).
|
||||||
|
|
||||||
### Wireless Interfaces
|
### Wireless Interfaces
|
||||||
|
@ -18,4 +18,4 @@ Each L2VPN instance must have one of the following type associated with it:
|
|||||||
* PBB-EVPN
|
* PBB-EVPN
|
||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
Choosing VPWS, EPL, EP-LAN, EP-TREE will result in only being able to add 2 terminations to a given L2VPN.
|
Choosing VPWS, EPL, EP-LAN, EP-TREE will result in only being able to add two terminations to a given L2VPN.
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
# L2VPN Termination
|
# L2VPN Termination
|
||||||
|
|
||||||
A L2VPN Termination is the termination point of a L2VPN. Certain types of L2VPN's may only have 2 termination points (point-to-point) while others may have many terminations (multipoint).
|
A L2VPN Termination is the termination point of a L2VPN. Certain types of L2VPNs may only have 2 termination points (point-to-point) while others may have many terminations (multipoint).
|
||||||
|
|
||||||
Each termination consists of a L2VPN it is a member of as well as the connected endpoint which can be an interface or a VLAN.
|
Each termination consists of a L2VPN it is a member of as well as the connected endpoint which can be an interface or a VLAN.
|
||||||
|
|
||||||
The following types of L2VPN's are considered point-to-point:
|
The following types of L2VPNs are considered point-to-point:
|
||||||
|
|
||||||
* VPWS
|
* VPWS
|
||||||
* EPL
|
* EPL
|
||||||
@ -12,4 +12,4 @@ The following types of L2VPN's are considered point-to-point:
|
|||||||
* EP-TREE
|
* EP-TREE
|
||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
Choosing any of the above types of L2VPN's will result in only being able to add 2 terminations to a given L2VPN.
|
Choosing any of the above types will result in only being able to add 2 terminations to a given L2VPN.
|
||||||
|
@ -4,7 +4,7 @@ from django.db.models import Q
|
|||||||
from dcim.filtersets import CabledObjectFilterSet
|
from dcim.filtersets import CabledObjectFilterSet
|
||||||
from dcim.models import Region, Site, SiteGroup
|
from dcim.models import Region, Site, SiteGroup
|
||||||
from ipam.models import ASN
|
from ipam.models import ASN
|
||||||
from netbox.filtersets import ChangeLoggedModelFilterSet, NetBoxModelFilterSet, OrganizationalModelFilterSet
|
from netbox.filtersets import NetBoxModelFilterSet, OrganizationalModelFilterSet
|
||||||
from tenancy.filtersets import ContactModelFilterSet, TenancyFilterSet
|
from tenancy.filtersets import ContactModelFilterSet, TenancyFilterSet
|
||||||
from utilities.filters import TreeNodeMultipleChoiceFilter
|
from utilities.filters import TreeNodeMultipleChoiceFilter
|
||||||
from .choices import *
|
from .choices import *
|
||||||
|
@ -1031,8 +1031,8 @@ class InterfacePoEModeChoices(ChoiceSet):
|
|||||||
MODE_PSE = 'pse'
|
MODE_PSE = 'pse'
|
||||||
|
|
||||||
CHOICES = (
|
CHOICES = (
|
||||||
(MODE_PD, 'Powered device (PD)'),
|
(MODE_PD, 'PD'),
|
||||||
(MODE_PSE, 'Power sourcing equipment (PSE)'),
|
(MODE_PSE, 'PSE'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1073,13 +1073,15 @@ class InterfaceBulkEditForm(
|
|||||||
choices=add_blank_choice(InterfacePoEModeChoices),
|
choices=add_blank_choice(InterfacePoEModeChoices),
|
||||||
required=False,
|
required=False,
|
||||||
initial='',
|
initial='',
|
||||||
widget=StaticSelect()
|
widget=StaticSelect(),
|
||||||
|
label='PoE mode'
|
||||||
)
|
)
|
||||||
poe_type = forms.ChoiceField(
|
poe_type = forms.ChoiceField(
|
||||||
choices=add_blank_choice(InterfacePoETypeChoices),
|
choices=add_blank_choice(InterfacePoETypeChoices),
|
||||||
required=False,
|
required=False,
|
||||||
initial='',
|
initial='',
|
||||||
widget=StaticSelect()
|
widget=StaticSelect(),
|
||||||
|
label='PoE type'
|
||||||
)
|
)
|
||||||
mark_connected = forms.NullBooleanField(
|
mark_connected = forms.NullBooleanField(
|
||||||
required=False,
|
required=False,
|
||||||
|
@ -227,6 +227,10 @@ class PathEndpoint(models.Model):
|
|||||||
# Return the path as a list of three-tuples (A termination(s), cable(s), B termination(s))
|
# Return the path as a list of three-tuples (A termination(s), cable(s), B termination(s))
|
||||||
return list(zip(*[iter(path)] * 3))
|
return list(zip(*[iter(path)] * 3))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def path(self):
|
||||||
|
return self._path
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def connected_endpoints(self):
|
def connected_endpoints(self):
|
||||||
"""
|
"""
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import decimal
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
@ -279,6 +280,12 @@ class DeviceType(NetBoxModel):
|
|||||||
def clean(self):
|
def clean(self):
|
||||||
super().clean()
|
super().clean()
|
||||||
|
|
||||||
|
# U height must be divisible by 0.5
|
||||||
|
if self.u_height % decimal.Decimal(0.5):
|
||||||
|
raise ValidationError({
|
||||||
|
'u_height': "U height must be in increments of 0.5 rack units."
|
||||||
|
})
|
||||||
|
|
||||||
# If editing an existing DeviceType to have a larger u_height, first validate that *all* instances of it have
|
# If editing an existing DeviceType to have a larger u_height, first validate that *all* instances of it have
|
||||||
# room to expand within their racks. This validation will impose a very high performance penalty when there are
|
# room to expand within their racks. This validation will impose a very high performance penalty when there are
|
||||||
# many instances to check, but increasing the u_height of a DeviceType should be a very rare occurrence.
|
# many instances to check, but increasing the u_height of a DeviceType should be a very rare occurrence.
|
||||||
@ -811,7 +818,11 @@ class Device(NetBoxModel, ConfigContextModel):
|
|||||||
'position': "Cannot select a rack position without assigning a rack.",
|
'position': "Cannot select a rack position without assigning a rack.",
|
||||||
})
|
})
|
||||||
|
|
||||||
# Validate position/face combination
|
# Validate rack position and face
|
||||||
|
if self.position and self.position % decimal.Decimal(0.5):
|
||||||
|
raise ValidationError({
|
||||||
|
'position': "Position must be in increments of 0.5 rack units."
|
||||||
|
})
|
||||||
if self.position and not self.face:
|
if self.position and not self.face:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
'face': "Must specify rack face when defining rack position.",
|
'face': "Must specify rack face when defining rack position.",
|
||||||
|
@ -260,13 +260,14 @@ class RackElevationSVG:
|
|||||||
)
|
)
|
||||||
|
|
||||||
for ru in range(0, self.rack.u_height):
|
for ru in range(0, self.rack.u_height):
|
||||||
|
unit = ru + 1 if self.rack.desc_units else self.rack.u_height - ru
|
||||||
y_offset = RACK_ELEVATION_BORDER_WIDTH + ru * self.unit_height
|
y_offset = RACK_ELEVATION_BORDER_WIDTH + ru * self.unit_height
|
||||||
text_coords = (
|
text_coords = (
|
||||||
x_offset + self.unit_width / 2,
|
x_offset + self.unit_width / 2,
|
||||||
y_offset + self.unit_height / 2
|
y_offset + self.unit_height / 2
|
||||||
)
|
)
|
||||||
|
|
||||||
link = Hyperlink(href=url_string.format(ru), target='_blank')
|
link = Hyperlink(href=url_string.format(unit), target='_blank')
|
||||||
link.add(Rect((x_offset, y_offset), (self.unit_width, self.unit_height), class_='slot'))
|
link.add(Rect((x_offset, y_offset), (self.unit_width, self.unit_height), class_='slot'))
|
||||||
link.add(Text('add device', insert=text_coords, class_='add-device'))
|
link.add(Text('add device', insert=text_coords, class_='add-device'))
|
||||||
|
|
||||||
|
@ -125,6 +125,7 @@ class LocationTable(TenancyColumnsMixin, NetBoxTable):
|
|||||||
site = tables.Column(
|
site = tables.Column(
|
||||||
linkify=True
|
linkify=True
|
||||||
)
|
)
|
||||||
|
status = columns.ChoiceFieldColumn()
|
||||||
rack_count = columns.LinkedCountColumn(
|
rack_count = columns.LinkedCountColumn(
|
||||||
viewname='dcim:rack_list',
|
viewname='dcim:rack_list',
|
||||||
url_params={'location_id': 'pk'},
|
url_params={'location_id': 'pk'},
|
||||||
|
@ -41,7 +41,11 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th scope="row">Color</th>
|
<th scope="row">Color</th>
|
||||||
<td>
|
<td>
|
||||||
|
{% if object.color %}
|
||||||
<span class="badge color-label" style="background-color: #{{ object.color }}"> </span>
|
<span class="badge color-label" style="background-color: #{{ object.color }}"> </span>
|
||||||
|
{% else %}
|
||||||
|
{{ ''|placeholder }}
|
||||||
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -41,7 +41,11 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th scope="row">Color</th>
|
<th scope="row">Color</th>
|
||||||
<td>
|
<td>
|
||||||
|
{% if object.color %}
|
||||||
<span class="badge color-label" style="background-color: #{{ object.color }}"> </span>
|
<span class="badge color-label" style="background-color: #{{ object.color }}"> </span>
|
||||||
|
{% else %}
|
||||||
|
{{ ''|placeholder }}
|
||||||
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -5,7 +5,13 @@
|
|||||||
|
|
||||||
{% block breadcrumbs %}
|
{% block breadcrumbs %}
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
<li class="breadcrumb-item"><a href="{% url 'virtualization:virtualmachine_list' %}?cluster_id={{ object.cluster.pk }}">{{ object.cluster }}</a></li>
|
<li class="breadcrumb-item">
|
||||||
|
{% if object.cluster %}
|
||||||
|
<a href="{% url 'virtualization:virtualmachine_list' %}?cluster_id={{ object.cluster.pk }}">{{ object.cluster }}</a>
|
||||||
|
{% else %}
|
||||||
|
<a href="{% url 'virtualization:virtualmachine_list' %}?site_id={{ object.site.pk }}">{{ object.site }}</a>
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block extra_controls %}
|
{% block extra_controls %}
|
||||||
|
@ -166,7 +166,8 @@ class ClusterRemoveDevicesForm(ConfirmationForm):
|
|||||||
|
|
||||||
class VirtualMachineForm(TenancyForm, NetBoxModelForm):
|
class VirtualMachineForm(TenancyForm, NetBoxModelForm):
|
||||||
site = DynamicModelChoiceField(
|
site = DynamicModelChoiceField(
|
||||||
queryset=Site.objects.all()
|
queryset=Site.objects.all(),
|
||||||
|
required=False
|
||||||
)
|
)
|
||||||
cluster_group = DynamicModelChoiceField(
|
cluster_group = DynamicModelChoiceField(
|
||||||
queryset=ClusterGroup.objects.all(),
|
queryset=ClusterGroup.objects.all(),
|
||||||
@ -178,6 +179,7 @@ class VirtualMachineForm(TenancyForm, NetBoxModelForm):
|
|||||||
)
|
)
|
||||||
cluster = DynamicModelChoiceField(
|
cluster = DynamicModelChoiceField(
|
||||||
queryset=Cluster.objects.all(),
|
queryset=Cluster.objects.all(),
|
||||||
|
required=False,
|
||||||
query_params={
|
query_params={
|
||||||
'site_id': '$site',
|
'site_id': '$site',
|
||||||
'group_id': '$cluster_group',
|
'group_id': '$cluster_group',
|
||||||
@ -188,7 +190,8 @@ class VirtualMachineForm(TenancyForm, NetBoxModelForm):
|
|||||||
required=False,
|
required=False,
|
||||||
query_params={
|
query_params={
|
||||||
'cluster_id': '$cluster'
|
'cluster_id': '$cluster'
|
||||||
}
|
},
|
||||||
|
help_text="Optionally pin this VM to a specific host device within the cluster"
|
||||||
)
|
)
|
||||||
role = DynamicModelChoiceField(
|
role = DynamicModelChoiceField(
|
||||||
queryset=DeviceRole.objects.all(),
|
queryset=DeviceRole.objects.all(),
|
||||||
@ -208,7 +211,7 @@ class VirtualMachineForm(TenancyForm, NetBoxModelForm):
|
|||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
('Virtual Machine', ('name', 'role', 'status', 'tags')),
|
('Virtual Machine', ('name', 'role', 'status', 'tags')),
|
||||||
('Cluster', ('site', 'cluster_group', 'cluster', 'device')),
|
('Site/Cluster', ('site', 'cluster_group', 'cluster', 'device')),
|
||||||
('Tenancy', ('tenant_group', 'tenant')),
|
('Tenancy', ('tenant_group', 'tenant')),
|
||||||
('Management', ('platform', 'primary_ip4', 'primary_ip6')),
|
('Management', ('platform', 'primary_ip4', 'primary_ip6')),
|
||||||
('Resources', ('vcpus', 'memory', 'disk')),
|
('Resources', ('vcpus', 'memory', 'disk')),
|
||||||
|
Reference in New Issue
Block a user