mirror of
https://github.com/netbox-community/netbox.git
synced 2025-08-26 09:16:10 -06:00
Allow racking of existing devices from rack view
This commit is contained in:
parent
c8f4a7c742
commit
b1f15de8f1
@ -25,6 +25,7 @@ __all__ = (
|
|||||||
'ConsolePortTemplateForm',
|
'ConsolePortTemplateForm',
|
||||||
'ConsoleServerPortForm',
|
'ConsoleServerPortForm',
|
||||||
'ConsoleServerPortTemplateForm',
|
'ConsoleServerPortTemplateForm',
|
||||||
|
'DeviceAssignForm',
|
||||||
'DeviceBayForm',
|
'DeviceBayForm',
|
||||||
'DeviceBayTemplateForm',
|
'DeviceBayTemplateForm',
|
||||||
'DeviceForm',
|
'DeviceForm',
|
||||||
@ -40,9 +41,9 @@ __all__ = (
|
|||||||
'InventoryItemTemplateForm',
|
'InventoryItemTemplateForm',
|
||||||
'LocationForm',
|
'LocationForm',
|
||||||
'ManufacturerForm',
|
'ManufacturerForm',
|
||||||
'ModuleForm',
|
|
||||||
'ModuleBayForm',
|
'ModuleBayForm',
|
||||||
'ModuleBayTemplateForm',
|
'ModuleBayTemplateForm',
|
||||||
|
'ModuleForm',
|
||||||
'ModuleTypeForm',
|
'ModuleTypeForm',
|
||||||
'PlatformForm',
|
'PlatformForm',
|
||||||
'PopulateDeviceBayForm',
|
'PopulateDeviceBayForm',
|
||||||
@ -474,6 +475,18 @@ class PlatformForm(NetBoxModelForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceAssignForm(BootstrapMixin, forms.Form):
|
||||||
|
q = forms.CharField(
|
||||||
|
required=False,
|
||||||
|
label=_('Search'),
|
||||||
|
)
|
||||||
|
show_racked_devices = forms.BooleanField(
|
||||||
|
required=False,
|
||||||
|
initial=False,
|
||||||
|
label=_('Show Racked Devices?'),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class DeviceForm(TenancyForm, NetBoxModelForm):
|
class DeviceForm(TenancyForm, NetBoxModelForm):
|
||||||
region = DynamicModelChoiceField(
|
region = DynamicModelChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
|
@ -12,6 +12,7 @@ __all__ = (
|
|||||||
'CableTerminationTable',
|
'CableTerminationTable',
|
||||||
'ConsolePortTable',
|
'ConsolePortTable',
|
||||||
'ConsoleServerPortTable',
|
'ConsoleServerPortTable',
|
||||||
|
'DeviceAssignTable',
|
||||||
'DeviceBayTable',
|
'DeviceBayTable',
|
||||||
'DeviceConsolePortTable',
|
'DeviceConsolePortTable',
|
||||||
'DeviceConsoleServerPortTable',
|
'DeviceConsoleServerPortTable',
|
||||||
@ -21,8 +22,8 @@ __all__ = (
|
|||||||
'DeviceInterfaceTable',
|
'DeviceInterfaceTable',
|
||||||
'DeviceInventoryItemTable',
|
'DeviceInventoryItemTable',
|
||||||
'DeviceModuleBayTable',
|
'DeviceModuleBayTable',
|
||||||
'DevicePowerPortTable',
|
|
||||||
'DevicePowerOutletTable',
|
'DevicePowerOutletTable',
|
||||||
|
'DevicePowerPortTable',
|
||||||
'DeviceRearPortTable',
|
'DeviceRearPortTable',
|
||||||
'DeviceRoleTable',
|
'DeviceRoleTable',
|
||||||
'DeviceTable',
|
'DeviceTable',
|
||||||
@ -219,6 +220,20 @@ class DeviceTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceAssignTable(NetBoxTable):
|
||||||
|
device = tables.TemplateColumn(
|
||||||
|
template_code=DEVICE_ASSIGN_LINK,
|
||||||
|
verbose_name='Device'
|
||||||
|
)
|
||||||
|
status = columns.ChoiceFieldColumn()
|
||||||
|
|
||||||
|
class Meta(NetBoxTable.Meta):
|
||||||
|
model = models.Device
|
||||||
|
fields = ('device', 'status', 'device_role', 'site', 'location', 'rack', 'position')
|
||||||
|
exclude = ('id',)
|
||||||
|
orderable = False
|
||||||
|
|
||||||
|
|
||||||
class DeviceImportTable(TenancyColumnsMixin, NetBoxTable):
|
class DeviceImportTable(TenancyColumnsMixin, NetBoxTable):
|
||||||
name = tables.TemplateColumn(
|
name = tables.TemplateColumn(
|
||||||
template_code=DEVICE_LINK,
|
template_code=DEVICE_LINK,
|
||||||
|
@ -24,6 +24,10 @@ DEVICE_LINK = """
|
|||||||
{{ value|default:'<span class="badge bg-info">Unnamed device</span>' }}
|
{{ value|default:'<span class="badge bg-info">Unnamed device</span>' }}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
DEVICE_ASSIGN_LINK = """
|
||||||
|
<a href="{% url 'dcim:device_edit' pk=record.pk %}?{% if request.GET.rack %}rack={{ request.GET.rack }}&face={{ request.GET.face }}&position={{ request.GET.position }}{% endif %}&return_url={{ request.GET.return_url }}">{{ record }}</a>
|
||||||
|
"""
|
||||||
|
|
||||||
DEVICEBAY_STATUS = """
|
DEVICEBAY_STATUS = """
|
||||||
{% if record.installed_device_id %}
|
{% if record.installed_device_id %}
|
||||||
<span class="badge bg-{{ record.installed_device.get_status_color }}">
|
<span class="badge bg-{{ record.installed_device.get_status_color }}">
|
||||||
|
@ -176,6 +176,7 @@ urlpatterns = [
|
|||||||
# Devices
|
# Devices
|
||||||
path('devices/', views.DeviceListView.as_view(), name='device_list'),
|
path('devices/', views.DeviceListView.as_view(), name='device_list'),
|
||||||
path('devices/add/', views.DeviceEditView.as_view(), name='device_add'),
|
path('devices/add/', views.DeviceEditView.as_view(), name='device_add'),
|
||||||
|
path("devices/assign/", views.DeviceAssignView.as_view(), name="device_assign"),
|
||||||
path('devices/import/', views.DeviceBulkImportView.as_view(), name='device_import'),
|
path('devices/import/', views.DeviceBulkImportView.as_view(), name='device_import'),
|
||||||
path('devices/import/child-devices/', views.ChildDeviceBulkImportView.as_view(), name='device_import_child'),
|
path('devices/import/child-devices/', views.ChildDeviceBulkImportView.as_view(), name='device_import_child'),
|
||||||
path('devices/edit/', views.DeviceBulkEditView.as_view(), name='device_bulk_edit'),
|
path('devices/edit/', views.DeviceBulkEditView.as_view(), name='device_bulk_edit'),
|
||||||
|
@ -1880,6 +1880,49 @@ class DeviceDeleteView(generic.ObjectDeleteView):
|
|||||||
queryset = Device.objects.all()
|
queryset = Device.objects.all()
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceAssignView(generic.ObjectView):
|
||||||
|
"""
|
||||||
|
Search for Devices to be assigned to a rack.
|
||||||
|
"""
|
||||||
|
queryset = Device.objects.all()
|
||||||
|
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
|
||||||
|
# Redirect user if a rack has not been provided
|
||||||
|
if 'rack' not in request.GET:
|
||||||
|
return redirect('dcim:device_add')
|
||||||
|
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def get(self, request):
|
||||||
|
form = forms.DeviceAssignForm()
|
||||||
|
|
||||||
|
return render(request, 'dcim/device_assign.html', {
|
||||||
|
'form': form,
|
||||||
|
'return_url': request.GET.get('return_url', ''),
|
||||||
|
})
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
form = forms.DeviceAssignForm(request.POST)
|
||||||
|
table = None
|
||||||
|
|
||||||
|
if form.is_valid():
|
||||||
|
rack = Rack.objects.get(pk=request.GET['rack'])
|
||||||
|
# Only get devices belonging to the same site as rack
|
||||||
|
devices = self.queryset.filter(site=rack.site)
|
||||||
|
if not form.cleaned_data['show_racked_devices']:
|
||||||
|
devices = devices.exclude(rack__isnull=False)
|
||||||
|
# Limit to 100 results
|
||||||
|
devices = filtersets.DeviceFilterSet(request.POST, devices).qs[:100]
|
||||||
|
table = tables.DeviceAssignTable(devices)
|
||||||
|
|
||||||
|
return render(request, 'dcim/device_assign.html', {
|
||||||
|
'form': form,
|
||||||
|
'table': table,
|
||||||
|
'return_url': request.GET.get('return_url'),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
@register_model_view(Device, 'consoleports', path='console-ports')
|
@register_model_view(Device, 'consoleports', path='console-ports')
|
||||||
class DeviceConsolePortsView(DeviceComponentsView):
|
class DeviceConsolePortsView(DeviceComponentsView):
|
||||||
child_model = ConsolePort
|
child_model = ConsolePort
|
||||||
|
48
netbox/templates/dcim/device_assign.html
Normal file
48
netbox/templates/dcim/device_assign.html
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
{% extends 'generic/object_edit.html' %}
|
||||||
|
{% load static %}
|
||||||
|
{% load form_helpers %}
|
||||||
|
{% load helpers %}
|
||||||
|
{% load render_table from django_tables2 %}
|
||||||
|
|
||||||
|
{% block title %}Assign a Device{% endblock title %}
|
||||||
|
|
||||||
|
{% block tabs %}
|
||||||
|
{% include 'dcim/inc/device_edit_header.html' with active_tab='assign' %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block form %}
|
||||||
|
<form action="{% querystring request %}" method="post" class="form form-horizontal">
|
||||||
|
{% csrf_token %}
|
||||||
|
{% for field in form.hidden_fields %}
|
||||||
|
{{ field }}
|
||||||
|
{% endfor %}
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col col-md-8 offset-md-2">
|
||||||
|
<div class="field-group">
|
||||||
|
<h6>Select Device</h6>
|
||||||
|
{% render_field form.q %}
|
||||||
|
{% render_field form.show_racked_devices %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col col-md-8 offset-md-2 text-end">
|
||||||
|
<a href="{{ return_url }}" class="btn btn-outline-danger">Cancel</a>
|
||||||
|
<button type="submit" class="btn btn-primary">Search</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% if table %}
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col col-md-12">
|
||||||
|
<h3>Search Results</h3>
|
||||||
|
<div class="table-responsive">
|
||||||
|
{% render_table table 'inc/table.html' %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock form %}
|
||||||
|
|
||||||
|
{% block buttons %}
|
||||||
|
{% endblock buttons%}
|
@ -1,6 +1,10 @@
|
|||||||
{% extends 'generic/object_edit.html' %}
|
{% extends 'generic/object_edit.html' %}
|
||||||
{% load form_helpers %}
|
{% load form_helpers %}
|
||||||
|
|
||||||
|
{% block tabs %}
|
||||||
|
{% include 'dcim/inc/device_edit_header.html' with active_tab='add' %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block form %}
|
{% block form %}
|
||||||
{% render_errors form %}
|
{% render_errors form %}
|
||||||
|
|
||||||
|
22
netbox/templates/dcim/inc/device_edit_header.html
Normal file
22
netbox/templates/dcim/inc/device_edit_header.html
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{% load helpers %}
|
||||||
|
|
||||||
|
<ul class="nav nav-tabs px-3">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a
|
||||||
|
class="nav-link {% if active_tab == 'add' %}active{% endif %}"
|
||||||
|
href="{% url 'dcim:device_add' %}{% querystring request %}"
|
||||||
|
>
|
||||||
|
{% if obj.pk %}Edit{% else %}Create{% endif %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% if 'rack' in request.GET %}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a
|
||||||
|
class="nav-link {% if active_tab == 'assign' %}active{% endif %}"
|
||||||
|
href="{% url 'dcim:device_assign' %}{% querystring request %}"
|
||||||
|
>
|
||||||
|
Assign Device
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
Loading…
Reference in New Issue
Block a user