mirror of
https://github.com/netbox-community/netbox.git
synced 2025-08-08 08:38:16 -06:00
Fixes #1830: Support assgin an existing device to a rack
This commit is contained in:
parent
1a8eea5aa9
commit
7245922ea7
@ -2,6 +2,7 @@
|
||||
|
||||
## Enhancements
|
||||
|
||||
* [#1830](https://github.com/netbox-community/netbox/issues/1830) - Support Assgin existing device to a rack with 'add device' button
|
||||
* [#3840](https://github.com/netbox-community/netbox/issues/3840) - Enhance search function when selecting VLANs for interface assignment
|
||||
* [#4170](https://github.com/netbox-community/netbox/issues/4170) - Improve color contrast in rack elevation drawings
|
||||
|
||||
|
@ -2137,6 +2137,13 @@ class DeviceBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditF
|
||||
]
|
||||
|
||||
|
||||
class DeviceAssignForm(BootstrapMixin, forms.Form):
|
||||
q = forms.CharField(
|
||||
required=False,
|
||||
label='Search',
|
||||
)
|
||||
|
||||
|
||||
class DeviceFilterForm(BootstrapMixin, LocalConfigContextFilterForm, TenancyFilterForm, CustomFieldFilterForm):
|
||||
model = Device
|
||||
field_order = [
|
||||
|
@ -423,9 +423,10 @@ class RackElevationHelperMixin:
|
||||
def _draw_empty(drawing, rack, start, end, text, id_, face_id, class_, reservation):
|
||||
link = drawing.add(
|
||||
drawing.a(
|
||||
href='{}?{}'.format(
|
||||
href='{}?{}&return_url=/dcim/racks/{}'.format(
|
||||
reverse('dcim:device_add'),
|
||||
urlencode({'rack': rack.pk, 'site': rack.site.pk, 'face': face_id, 'position': id_})
|
||||
urlencode({'rack': rack.pk, 'site': rack.site.pk, 'face': face_id, 'position': id_}),
|
||||
rack.pk,
|
||||
),
|
||||
target='_top'
|
||||
)
|
||||
|
@ -40,6 +40,14 @@ DEVICE_LINK = """
|
||||
</a>
|
||||
"""
|
||||
|
||||
DEVICE_ASSIGN_LINK = """
|
||||
{% if request.GET %}
|
||||
<a href="{% url 'dcim:device_edit' pk=record.pk %}?rack={{ request.GET.rack }}&site={{ request.GET.site }}&face={{ request.GET.face }}&position={{ request.GET.position }}&return_url={{ request.GET.return_url }}">{{ record }}</a>
|
||||
{% else %}
|
||||
<a href="{% url 'dcim:device_edit' pk=record.pk %}?rack={{ record.rack.pk }}&site={{ record.site.pk }}&face={{ record.face.pk }}&position={{ record.position.pk }}&return_url={{ request.GET.return_url }}">{{ record }}</a>
|
||||
{% endif %}
|
||||
"""
|
||||
|
||||
REGION_ACTIONS = """
|
||||
<a href="{% url 'dcim:region_changelog' pk=record.pk %}" class="btn btn-default btn-xs" title="Changelog">
|
||||
<i class="fa fa-history"></i>
|
||||
@ -694,6 +702,28 @@ class DeviceDetailTable(DeviceTable):
|
||||
fields = ('pk', 'name', 'status', 'tenant', 'site', 'rack', 'device_role', 'device_type', 'primary_ip')
|
||||
|
||||
|
||||
class DeviceAssignTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
name = tables.TemplateColumn(
|
||||
order_by=('_name',),
|
||||
template_code=DEVICE_ASSIGN_LINK
|
||||
)
|
||||
status = tables.TemplateColumn(template_code=STATUS_LABEL, verbose_name='Status')
|
||||
tenant = tables.TemplateColumn(template_code=COL_TENANT)
|
||||
site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
|
||||
rack = tables.LinkColumn('dcim:rack', args=[Accessor('rack.pk')])
|
||||
device_role = tables.TemplateColumn(DEVICE_ROLE, verbose_name='Role')
|
||||
device_type = tables.LinkColumn(
|
||||
'dcim:devicetype', args=[Accessor('device_type.pk')], verbose_name='Type',
|
||||
text=lambda record: record.device_type.display_name
|
||||
)
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = Device
|
||||
fields = ('pk', 'name', 'status', 'tenant', 'site', 'rack', 'device_role', 'device_type', 'primary_ip')
|
||||
orderable = False
|
||||
|
||||
|
||||
class DeviceImportTable(BaseTable):
|
||||
name = tables.TemplateColumn(template_code=DEVICE_LINK, verbose_name='Name')
|
||||
status = tables.TemplateColumn(template_code=STATUS_LABEL, verbose_name='Status')
|
||||
|
@ -169,6 +169,7 @@ urlpatterns = [
|
||||
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/delete/', views.DeviceBulkDeleteView.as_view(), name='device_bulk_delete'),
|
||||
path('device/assign/', views.DeviceAssignView.as_view(), name='device_assign'),
|
||||
path('devices/<int:pk>/', views.DeviceView.as_view(), name='device'),
|
||||
path('devices/<int:pk>/edit/', views.DeviceEditView.as_view(), name='device_edit'),
|
||||
path('devices/<int:pk>/delete/', views.DeviceDeleteView.as_view(), name='device_delete'),
|
||||
|
@ -1223,6 +1223,53 @@ class DeviceEditView(DeviceCreateView):
|
||||
permission_required = 'dcim.change_device'
|
||||
|
||||
|
||||
class DeviceAssignView(PermissionRequiredMixin, View):
|
||||
"""
|
||||
Search for Devices to be assigned to a Rack.
|
||||
"""
|
||||
permission_required = 'dcim.change_device'
|
||||
|
||||
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')
|
||||
else:
|
||||
# Restrict device assignment in the same site
|
||||
self.site = request.GET['site']
|
||||
|
||||
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():
|
||||
|
||||
devices = Device.objects.filter(site=self.site).prefetch_related(
|
||||
'tenant', 'site', 'rack', 'device_role', 'device_type__manufacturer'
|
||||
)
|
||||
# Limit to 100 results
|
||||
devices = filters.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', ''),
|
||||
})
|
||||
|
||||
|
||||
class DeviceDeleteView(PermissionRequiredMixin, ObjectDeleteView):
|
||||
permission_required = 'dcim.delete_device'
|
||||
model = Device
|
||||
|
47
netbox/templates/dcim/device_assign.html
Normal file
47
netbox/templates/dcim/device_assign.html
Normal file
@ -0,0 +1,47 @@
|
||||
{% extends 'utilities/obj_edit.html' %}
|
||||
{% load static %}
|
||||
{% load form_helpers %}
|
||||
{% load helpers %}
|
||||
|
||||
{% block content %}
|
||||
<form action="{% querystring request %}" method="post" class="form form-horizontal">
|
||||
{% csrf_token %}
|
||||
{% for field in form.hidden_fields %}
|
||||
{{ field }}
|
||||
{% endfor %}
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<h3>Assign a Device</h3>
|
||||
{% include 'dcim/inc/device_edit_header.html' with active_tab='assign' %}
|
||||
{% if form.non_field_errors %}
|
||||
<div class="panel panel-danger">
|
||||
<div class="panel-heading"><strong>Errors</strong></div>
|
||||
<div class="panel-body">
|
||||
{{ form.non_field_errors }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><strong>Select Device</strong></div>
|
||||
<div class="panel-body">
|
||||
{% render_field form.q %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-md-offset-3 text-right">
|
||||
<button type="submit" class="btn btn-primary">Search</button>
|
||||
<a href="{{ return_url }}" class="btn btn-default">Cancel</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{% if table %}
|
||||
<div class="row">
|
||||
<div class="col-md-12" style="margin-top: 20px">
|
||||
<h3>Search Results</h3>
|
||||
{% include 'utilities/obj_table.html' with table_template='panel_table.html' %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
@ -1,5 +1,13 @@
|
||||
{% extends 'utilities/obj_edit.html' %}
|
||||
{% load static %}
|
||||
{% load form_helpers %}
|
||||
{% load helpers %}
|
||||
|
||||
{% block tabs %}
|
||||
{% if not obj.pk %}
|
||||
{% include 'dcim/inc/device_edit_header.html' with active_tab='add' %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block form %}
|
||||
<div class="panel panel-default">
|
||||
|
12
netbox/templates/dcim/inc/device_edit_header.html
Normal file
12
netbox/templates/dcim/inc/device_edit_header.html
Normal file
@ -0,0 +1,12 @@
|
||||
{% load helpers %}
|
||||
|
||||
<ul class="nav nav-tabs" style="margin-bottom: 20px">
|
||||
<li role="presentation"{% if active_tab == 'add' %} class="active"{% endif %}>
|
||||
<a href="{% url 'dcim:device_add' %}{% querystring request %}">New Device</a>
|
||||
</li>
|
||||
{% if 'rack' in request.GET %}
|
||||
<li role="presentation"{% if active_tab == 'assign' %} class="active"{% endif %}>
|
||||
<a href="{% url 'dcim:device_assign' %}{% querystring request %}">Assign Device</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
Loading…
Reference in New Issue
Block a user