Mirror device cabling 1:1 (per-type tabs) and copy cables on rack import
- Split the device detail page's single "Components" table into the same seven tabs a real dcim.Device has, in the same order: Console Ports, Console Server Ports, Power Ports, Power Outlets, Interfaces, Front Ports, Rear Ports. Each tab is its own ObjectChildrenView filtered by component_type (one ConceptComponent model backs all of them, so this only needed the view/URL layer, not a schema change), hidden when empty, exactly like core NetBox. Replace the ad hoc "Add component" button with the same "Add Components" dropdown real device pages use, one entry per type, each landing back on its own tab. - When copying a real rack into a concept (or merging two real racks into one), automatically sync every copied device's components and recreate, as ConceptCables, every real cable whose both ends land on one of those devices - reading the same CableTermination data dcim itself uses, so the copy comes out cabled internally the same as the source. Cables reaching outside the copied rack(s), or multi- termination trunk cables, are skipped and reported as a count in the success message - the other end isn't part of the concept, so it can't be represented. copy_rack_to_concept() and merge_racks_to_concept() now return an extra cable_summary value; updated all their callers (views and API). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load i18n %}
|
||||
{% load helpers %}
|
||||
{% load render_table from django_tables2 %}
|
||||
|
||||
{% block extra_controls %}
|
||||
{{ block.super }}
|
||||
@@ -10,6 +9,22 @@
|
||||
<i class="mdi mdi-sync"></i> {% trans "Sync components" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if perms.netbox_rack_concept.add_conceptcomponent %}
|
||||
<div class="dropdown">
|
||||
<button id="add-components" type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="mdi mdi-plus-thick" aria-hidden="true"></i> {% trans "Add Components" %}
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="add-components">
|
||||
<li><a class="dropdown-item" href="{% url 'plugins:netbox_rack_concept:conceptcomponent_add' %}?device={{ object.pk }}&component_type=console-port&return_url={% url 'plugins:netbox_rack_concept:conceptdevice_consoleports' pk=object.pk %}">{% trans "Console Ports" %}</a></li>
|
||||
<li><a class="dropdown-item" href="{% url 'plugins:netbox_rack_concept:conceptcomponent_add' %}?device={{ object.pk }}&component_type=console-server-port&return_url={% url 'plugins:netbox_rack_concept:conceptdevice_consoleserverports' pk=object.pk %}">{% trans "Console Server Ports" %}</a></li>
|
||||
<li><a class="dropdown-item" href="{% url 'plugins:netbox_rack_concept:conceptcomponent_add' %}?device={{ object.pk }}&component_type=power-port&return_url={% url 'plugins:netbox_rack_concept:conceptdevice_powerports' pk=object.pk %}">{% trans "Power Ports" %}</a></li>
|
||||
<li><a class="dropdown-item" href="{% url 'plugins:netbox_rack_concept:conceptcomponent_add' %}?device={{ object.pk }}&component_type=power-outlet&return_url={% url 'plugins:netbox_rack_concept:conceptdevice_poweroutlets' pk=object.pk %}">{% trans "Power Outlets" %}</a></li>
|
||||
<li><a class="dropdown-item" href="{% url 'plugins:netbox_rack_concept:conceptcomponent_add' %}?device={{ object.pk }}&component_type=interface&return_url={% url 'plugins:netbox_rack_concept:conceptdevice_interfaces' pk=object.pk %}">{% trans "Interfaces" %}</a></li>
|
||||
<li><a class="dropdown-item" href="{% url 'plugins:netbox_rack_concept:conceptcomponent_add' %}?device={{ object.pk }}&component_type=front-port&return_url={% url 'plugins:netbox_rack_concept:conceptdevice_frontports' pk=object.pk %}">{% trans "Front Ports" %}</a></li>
|
||||
<li><a class="dropdown-item" href="{% url 'plugins:netbox_rack_concept:conceptcomponent_add' %}?device={{ object.pk }}&component_type=rear-port&return_url={% url 'plugins:netbox_rack_concept:conceptdevice_rearports' pk=object.pk %}">{% trans "Rear Ports" %}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
@@ -109,21 +124,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col col-md-12">
|
||||
<div class="card">
|
||||
<h2 class="card-header">
|
||||
{% trans "Components" %}
|
||||
{% if perms.netbox_rack_concept.add_conceptcomponent %}
|
||||
<a href="{% url 'plugins:netbox_rack_concept:conceptcomponent_add' %}?device={{ object.pk }}"
|
||||
class="btn btn-sm btn-primary float-end">
|
||||
<i class="mdi mdi-plus-thick"></i> {% trans "Add component" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</h2>
|
||||
{% render_table component_table %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{% extends 'generic/object_children.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block bulk_extra_controls %}
|
||||
{{ block.super }}
|
||||
{% if perms.netbox_rack_concept.add_conceptcomponent %}
|
||||
<a href="{% url 'plugins:netbox_rack_concept:conceptcomponent_add' %}?device={{ object.pk }}&component_type={{ component_type }}&return_url={{ request.path }}"
|
||||
class="btn btn-primary">
|
||||
<i class="mdi mdi-plus-thick"></i> {% blocktrans %}Add {{ component_type_label }}{% endblocktrans %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user