Cable | -- {{ object.cable|linkify }} - - - - | -
---|---|
Device | -{{ object.connected_endpoint.device|linkify }} | -
Name | -{{ object.connected_endpoint|linkify:"name" }} | -
Type | -{{ object.connected_endpoint.get_type_display|placeholder }} | -
Description | -{{ object.connected_endpoint.description|placeholder }} | -
Path Status | -- {% if object.path.is_active %} - Reachable - {% else %} - Not Reachable - {% endif %} - | -
Cable | -- {{ object.cable|linkify }} - - - - | -
---|---|
Device | -- {{ object.connected_endpoint.device|linkify }} - | -
Name | -{{ object.connected_endpoint|linkify:"name" }} | -
Type | -{{ object.connected_endpoint.get_type_display|placeholder }} | -
Description | -{{ object.connected_endpoint.description|placeholder }} | -
Path Status | -- {% if object.path.is_active %} - Reachable - {% else %} - Not Reachable - {% endif %} - | -
Cable | ++ {{ object.cable|linkify }} + + + + | +
---|---|
Path Status | ++ {% if object.path.is_complete and object.path.is_active %} + Reachable + {% else %} + Not Reachable + {% endif %} + | +
Path Endpoints | +
+ {% for endpoint in object.connected_endpoints %}
+ {% if endpoint.parent_object %}
+ {{ endpoint.parent_object|linkify }}
+
+ {% endif %}
+ {{ endpoint|linkify }}
+ {% if not forloop.last %} {% endif %} + {% empty %} + {{ ''|placeholder }} + {% endfor %} + |
+
- {% if object.connected_endpoint.enabled %} - Enabled - {% else %} - Disabled - {% endif %} - | -|
Cable | -- {{ object.cable|linkify }} - - - - | -
---|---|
Device | -{{ iface.device|linkify }} | -
Name | -{{ iface|linkify:"name" }} | -
Type | -{{ iface.get_type_display }} | -
LAG | -{{ iface.lag|linkify|placeholder }} | -
Description | -{{ iface.description|placeholder }} | -
MTU | -{{ iface.mtu|placeholder }} | -
MAC Address | -{{ iface.mac_address|placeholder }} | -
802.1Q Mode | -{{ iface.get_mode_display }} | -
Provider | -{{ ct.circuit.provider|linkify }} | -
Circuit | -{{ ct.circuit|linkify }} | -
Side | -{{ ct.term_side }} | -
Path Status | -- {% if object.path.is_complete and object.path.is_active %} - Reachable - {% else %} - Not Reachable - {% endif %} - | -
Device | {{ peer_interface.device|linkify }} | diff --git a/netbox/templates/dcim/powerfeed.html b/netbox/templates/dcim/powerfeed.html index 584454df8..54ac96bab 100644 --- a/netbox/templates/dcim/powerfeed.html +++ b/netbox/templates/dcim/powerfeed.html @@ -41,8 +41,8 @@||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Connected Device | - {% if object.connected_endpoint %} - {{ object.connected_endpoint.device|linkify }} ({{ object.connected_endpoint }}) + {% if object.connected_endpoints %} + {{ object.connected_endpoints.0.device|linkify }} ({{ object.connected_endpoints.0|linkify:"name" }}) {% else %} {{ ''|placeholder }} {% endif %} @@ -50,7 +50,7 @@ | ||||||||||||||||||||||||||||||||||||
Utilization (Allocated) | - {% with utilization=object.connected_endpoint.get_power_draw %} + {% with utilization=object.connected_endpoints.0.get_power_draw %} {% if utilization %}
{{ utilization.allocated }}VA / {{ object.available_power }}VA
@@ -100,73 +100,33 @@
{% plugin_left_page object %}
-
- - Connection --
- {% if object.mark_connected %}
-
- Marked as connected
-
- {% elif object.cable %}
-
- Not connected
-
- {% endif %}
+
+
Connection+
+ {% if object.mark_connected %}
+
+ {% include 'inc/panels/comments.html' %}
+ {% plugin_right_page object %}
+ Marked as connected
- {% if not object.mark_connected and not object.cable %}
-
- {% include 'inc/panels/comments.html' %}
- {% plugin_right_page object %}
+ {% if not object.mark_connected and not object.cable %}
+
+ {% endif %}
+
diff --git a/netbox/templates/dcim/poweroutlet.html b/netbox/templates/dcim/poweroutlet.html
index 26f4a07f8..fb6de8ddb 100644
--- a/netbox/templates/dcim/poweroutlet.html
+++ b/netbox/templates/dcim/poweroutlet.html
@@ -58,69 +58,29 @@
{% plugin_left_page object %}
-
-
- Connection --
- {% if object.mark_connected %}
-
- Marked as Connected
-
- {% elif object.cable %}
-
- Not Connected
- {% if perms.dcim.add_cable %}
-
- Connect
-
- {% endif %}
-
- {% endif %}
+
+
+ {% include 'dcim/inc/panels/inventory_items.html' %}
+ {% plugin_right_page object %}
Connection+
+ {% if object.mark_connected %}
+
- {% include 'dcim/inc/panels/inventory_items.html' %}
- {% plugin_right_page object %}
+
+ Marked as Connected
+ {% elif object.cable %}
+ {% include 'dcim/inc/connection_endpoints.html' %}
+ {% else %}
+
+ Not Connected
+ {% if perms.dcim.add_cable %}
+
+ Connect
+
+ {% endif %}
+
+ {% endif %}
diff --git a/netbox/templates/dcim/powerport.html b/netbox/templates/dcim/powerport.html
index c5eccbf14..c552c2398 100644
--- a/netbox/templates/dcim/powerport.html
+++ b/netbox/templates/dcim/powerport.html
@@ -58,79 +58,39 @@
{% plugin_left_page object %}
-
-
- Connection --
- {% if object.mark_connected %}
-
- Marked as Connected
-
- {% elif object.cable %}
-
- Not Connected
- {% if perms.dcim.add_cable %}
-
-
-
- {% endif %}
-
-
- {% endif %}
+
+
+ {% include 'dcim/inc/panels/inventory_items.html' %}
+ {% plugin_right_page object %}
Connection+
+ {% if object.mark_connected %}
+
- {% include 'dcim/inc/panels/inventory_items.html' %}
- {% plugin_right_page object %}
+
+ Marked as Connected
+ {% elif object.cable %}
+ {% include 'dcim/inc/connection_endpoints.html' %}
+ {% else %}
+
+ Not Connected
+ {% if perms.dcim.add_cable %}
+
+
+
+ {% endif %}
+
+
+ {% endif %}
diff --git a/netbox/templates/virtualization/cluster.html b/netbox/templates/virtualization/cluster.html
index 8acbb61f4..bf7c8a69a 100644
--- a/netbox/templates/virtualization/cluster.html
+++ b/netbox/templates/virtualization/cluster.html
@@ -21,7 +21,7 @@
| ||||||||||||||||||||||||||||||||||||
Group | -{{ object.group|linkify }} | +{{ object.group|linkify|placeholder }} | |||||||||||||||||||||||||||||||||||
Tenant | @@ -34,7 +34,7 @@|||||||||||||||||||||||||||||||||||||
Site | -{{ object.site|linkify }} | +{{ object.site|linkify|placeholder }} | |||||||||||||||||||||||||||||||||||
Virtual Machines | diff --git a/netbox/tenancy/models/contacts.py b/netbox/tenancy/models/contacts.py index 79c0a2db3..f2fd09de7 100644 --- a/netbox/tenancy/models/contacts.py +++ b/netbox/tenancy/models/contacts.py @@ -174,8 +174,8 @@ class ContactAssignment(WebhooksMixin, ChangeLoggedModel): def __str__(self): if self.priority: - return f"{self.contact} ({self.get_priority_display()})" - return str(self.contact) + return f"{self.contact} ({self.get_priority_display()}) -> {self.object}" + return str(f"{self.contact} -> {self.object}") def get_absolute_url(self): return reverse('tenancy:contact', args=[self.contact.pk]) diff --git a/netbox/tenancy/tables/tenants.py b/netbox/tenancy/tables/tenants.py index 8f18423be..f18f1db09 100644 --- a/netbox/tenancy/tables/tenants.py +++ b/netbox/tenancy/tables/tenants.py @@ -42,7 +42,7 @@ class TenantTable(NetBoxTable): linkify_item=True ) tags = columns.TagColumn( - url_name='tenancy:tenant_list' + url_name='tenancy:contact_list' ) class Meta(NetBoxTable.Meta): diff --git a/netbox/virtualization/models.py b/netbox/virtualization/models.py index 37fcd68ae..4e8645707 100644 --- a/netbox/virtualization/models.py +++ b/netbox/virtualization/models.py @@ -350,14 +350,12 @@ class VirtualMachine(NetBoxModel, ConfigContextModel): }) # Validate site for cluster & device - if self.cluster and self.cluster.site != self.site: + if self.cluster and self.site and self.cluster.site != self.site: raise ValidationError({ - 'cluster': f'The selected cluster ({self.cluster} is not assigned to this site ({self.site}).' - }) - if self.device and self.device.site != self.site: - raise ValidationError({ - 'device': f'The selected device ({self.device} is not assigned to this site ({self.site}).' + 'cluster': f'The selected cluster ({self.cluster}) is not assigned to this site ({self.site}).' }) + elif self.cluster: + self.site = self.cluster.site # Validate assigned cluster device if self.device and not self.cluster: @@ -366,7 +364,7 @@ class VirtualMachine(NetBoxModel, ConfigContextModel): }) if self.device and self.device not in self.cluster.devices.all(): raise ValidationError({ - 'device': f'The selected device ({self.device} is not assigned to this cluster ({self.cluster}).' + 'device': f'The selected device ({self.device}) is not assigned to this cluster ({self.cluster}).' }) # Validate primary IP addresses diff --git a/netbox/virtualization/tests/test_models.py b/netbox/virtualization/tests/test_models.py index bf0571d3d..f7fa4cb39 100644 --- a/netbox/virtualization/tests/test_models.py +++ b/netbox/virtualization/tests/test_models.py @@ -70,9 +70,10 @@ class VirtualMachineTestCase(TestCase): with self.assertRaises(ValidationError): VirtualMachine(name='vm1', site=sites[0], cluster=clusters[1]).full_clean() - # VM with cluster site but no direct site should fail - with self.assertRaises(ValidationError): - VirtualMachine(name='vm1', site=None, cluster=clusters[0]).full_clean() + # VM with cluster site but no direct site should have its site set automatically + vm = VirtualMachine(name='vm1', site=None, cluster=clusters[0]) + vm.full_clean() + self.assertEqual(vm.site, sites[0]) def test_vm_name_case_sensitivity(self): vm1 = VirtualMachine( diff --git a/requirements.txt b/requirements.txt index af91cf35b..16c55462f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,10 @@ bleach==5.0.1 Django==4.1.1 django-cors-headers==3.13.0 -django-debug-toolbar==3.6.0 +django-debug-toolbar==3.7.0 django-filter==22.1 django-graphiql-debug-toolbar==0.2.0 -django-mptt==0.13.4 +django-mptt==0.14 django-pglocks==1.0.4 django-prometheus==2.2.0 django-redis==5.2.0 @@ -13,24 +13,24 @@ django-rq==2.5.1 django-tables2==2.4.1 django-taggit==3.0.0 django-timezone-field==5.0 -djangorestframework==3.13.1 -drf-yasg[validation]==1.21.3 +djangorestframework==3.14.0 +drf-yasg[validation]==1.21.4 graphene-django==2.15.0 gunicorn==20.1.0 Jinja2==3.1.2 -Markdown==3.4.1 -mkdocs-material==8.5.1 +Markdown==3.3.7 +mkdocs-material==8.5.6 mkdocstrings[python-legacy]==0.19.0 netaddr==0.8.0 Pillow==9.2.0 psycopg2-binary==2.9.3 PyYAML==6.0 -sentry-sdk==1.9.8 +sentry-sdk==1.9.10 social-auth-app-django==5.0.0 social-auth-core==4.3.0 svgwrite==1.4.3 tablib==3.2.1 -tzdata==2022.2 +tzdata==2022.4 # Workaround for #7401 jsonschema==3.2.0