mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
More power work
This commit is contained in:
@ -3,8 +3,8 @@ from rest_framework import serializers
|
||||
from dcim.constants import CONNECTION_STATUS_CHOICES
|
||||
from dcim.models import (
|
||||
Cable, ConsolePort, ConsoleServerPort, Device, DeviceBay, DeviceType, DeviceRole, FrontPort, FrontPortTemplate,
|
||||
Interface, Manufacturer, Platform, PowerOutlet, PowerPort, Rack, RackGroup, RackRole, RearPort, RearPortTemplate,
|
||||
Region, Site, VirtualChassis,
|
||||
Interface, Manufacturer, Platform, PowerFeed, PowerOutlet, PowerPanel, PowerPort, Rack, RackGroup, RackRole,
|
||||
RearPort, RearPortTemplate, Region, Site, VirtualChassis,
|
||||
)
|
||||
from utilities.api import ChoiceField, WritableNestedSerializer
|
||||
|
||||
@ -21,7 +21,9 @@ __all__ = [
|
||||
'NestedInterfaceSerializer',
|
||||
'NestedManufacturerSerializer',
|
||||
'NestedPlatformSerializer',
|
||||
'NestedPowerFeedSerializer',
|
||||
'NestedPowerOutletSerializer',
|
||||
'NestedPowerPanelSerializer',
|
||||
'NestedPowerPortSerializer',
|
||||
'NestedRackGroupSerializer',
|
||||
'NestedRackRoleSerializer',
|
||||
@ -247,3 +249,23 @@ class NestedVirtualChassisSerializer(WritableNestedSerializer):
|
||||
class Meta:
|
||||
model = VirtualChassis
|
||||
fields = ['id', 'url', 'master']
|
||||
|
||||
|
||||
#
|
||||
# Power panels/feeds
|
||||
#
|
||||
|
||||
class NestedPowerPanelSerializer(serializers.ModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerpanel-detail')
|
||||
|
||||
class Meta:
|
||||
model = PowerPanel
|
||||
fields = ['id', 'url', 'name']
|
||||
|
||||
|
||||
class NestedPowerFeedSerializer(serializers.ModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:powerfeed-detail')
|
||||
|
||||
class Meta:
|
||||
model = PowerFeed
|
||||
fields = ['id', 'url', 'name']
|
||||
|
@ -7,8 +7,9 @@ from dcim.constants import *
|
||||
from dcim.models import (
|
||||
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
||||
DeviceBayTemplate, DeviceType, DeviceRole, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
|
||||
Manufacturer, InventoryItem, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack,
|
||||
RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site, VirtualChassis,
|
||||
Manufacturer, InventoryItem, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate, PowerPanel, PowerPort,
|
||||
PowerPortTemplate, Rack, RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site,
|
||||
VirtualChassis,
|
||||
)
|
||||
from extras.api.customfields import CustomFieldModelSerializer
|
||||
from ipam.api.nested_serializers import NestedIPAddressSerializer, NestedVLANSerializer
|
||||
@ -587,3 +588,56 @@ class VirtualChassisSerializer(TaggitSerializer, ValidatedModelSerializer):
|
||||
class Meta:
|
||||
model = VirtualChassis
|
||||
fields = ['id', 'master', 'domain', 'tags']
|
||||
|
||||
|
||||
#
|
||||
# Power panels
|
||||
#
|
||||
|
||||
|
||||
class PowerPanelSerializer(ValidatedModelSerializer):
|
||||
site = NestedSiteSerializer()
|
||||
rack_group = NestedRackGroupSerializer(
|
||||
required=False,
|
||||
allow_null=True,
|
||||
default=None
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = PowerPanel
|
||||
fields = ['id', 'site', 'rack_group', 'name']
|
||||
|
||||
|
||||
class PowerFeedSerializer(TaggitSerializer, CustomFieldModelSerializer):
|
||||
power_panel = NestedPowerPanelSerializer()
|
||||
rack = NestedRackSerializer(
|
||||
required=False,
|
||||
allow_null=True,
|
||||
default=None
|
||||
)
|
||||
type = ChoiceField(
|
||||
choices=POWERFEED_TYPE_CHOICES,
|
||||
default=POWERFEED_TYPE_PRIMARY
|
||||
)
|
||||
status = ChoiceField(
|
||||
choices=POWERFEED_STATUS_CHOICES,
|
||||
default=POWERFEED_STATUS_ACTIVE
|
||||
)
|
||||
supply = ChoiceField(
|
||||
choices=POWERFEED_SUPPLY_CHOICES,
|
||||
default=POWERFEED_SUPPLY_AC
|
||||
)
|
||||
phase = ChoiceField(
|
||||
choices=POWERFEED_PHASE_CHOICES,
|
||||
default=POWERFEED_PHASE_SINGLE
|
||||
)
|
||||
tags = TagListSerializerField(
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = PowerFeed
|
||||
fields = [
|
||||
'id', 'power_panel', 'rack', 'name', 'status', 'type', 'supply', 'phase', 'voltage', 'amperage',
|
||||
'max_utilization', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
|
||||
]
|
||||
|
@ -68,6 +68,10 @@ router.register(r'cables', views.CableViewSet)
|
||||
# Virtual chassis
|
||||
router.register(r'virtual-chassis', views.VirtualChassisViewSet)
|
||||
|
||||
# Power
|
||||
router.register(r'power-panels', views.PowerPanelViewSet)
|
||||
router.register(r'power-feeds', views.PowerFeedViewSet)
|
||||
|
||||
# Miscellaneous
|
||||
router.register(r'connected-device', views.ConnectedDeviceViewSet, basename='connected-device')
|
||||
|
||||
|
@ -16,8 +16,9 @@ from dcim import filters
|
||||
from dcim.models import (
|
||||
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
||||
DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
|
||||
Manufacturer, InventoryItem, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack,
|
||||
RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site, VirtualChassis,
|
||||
Manufacturer, InventoryItem, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate, PowerPanel, PowerPort,
|
||||
PowerPortTemplate, Rack, RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site,
|
||||
VirtualChassis,
|
||||
)
|
||||
from extras.api.serializers import RenderedGraphSerializer
|
||||
from extras.api.views import CustomFieldModelViewSet
|
||||
@ -534,6 +535,26 @@ class VirtualChassisViewSet(ModelViewSet):
|
||||
serializer_class = serializers.VirtualChassisSerializer
|
||||
|
||||
|
||||
#
|
||||
# Power panels
|
||||
#
|
||||
|
||||
class PowerPanelViewSet(ModelViewSet):
|
||||
queryset = PowerPanel.objects.all()
|
||||
serializer_class = serializers.PowerPanelSerializer
|
||||
# filterset_class = filters.PowerPanelFilter
|
||||
|
||||
|
||||
#
|
||||
# Power feeds
|
||||
#
|
||||
|
||||
class PowerFeedViewSet(ModelViewSet):
|
||||
queryset = PowerFeed.objects.all()
|
||||
serializer_class = serializers.PowerFeedSerializer
|
||||
# filterset_class = filters.PowerFeedFilter
|
||||
|
||||
|
||||
#
|
||||
# Miscellaneous
|
||||
#
|
||||
|
@ -448,8 +448,8 @@ RACK_DIMENSION_UNIT_CHOICES = (
|
||||
POWERFEED_TYPE_PRIMARY = 1
|
||||
POWERFEED_TYPE_REDUNDANT = 2
|
||||
POWERFEED_TYPE_CHOICES = (
|
||||
(POWERFEED_TYPE_PRIMARY, 'AC'),
|
||||
(POWERFEED_TYPE_REDUNDANT, 'DC'),
|
||||
(POWERFEED_TYPE_PRIMARY, 'Primary'),
|
||||
(POWERFEED_TYPE_REDUNDANT, 'Redundant'),
|
||||
)
|
||||
POWERFEED_SUPPLY_AC = 1
|
||||
POWERFEED_SUPPLY_DC = 2
|
||||
|
@ -3183,7 +3183,7 @@ class PowerPanelForm(BootstrapMixin, forms.ModelForm):
|
||||
'site': APISelect(
|
||||
api_url="/api/dcim/sites/",
|
||||
filter_for={
|
||||
'rackgroup': 'site_id',
|
||||
'rack_group': 'site_id',
|
||||
}
|
||||
),
|
||||
}
|
||||
@ -3231,7 +3231,7 @@ class PowerFeedForm(BootstrapMixin, CustomFieldForm):
|
||||
class Meta:
|
||||
model = PowerFeed
|
||||
fields = [
|
||||
'site', 'power_panel', 'rack', 'name', 'type', 'status', 'supply', 'voltage', 'amperage', 'phase',
|
||||
'site', 'power_panel', 'rack', 'name', 'status', 'type', 'supply', 'phase', 'voltage', 'amperage',
|
||||
'max_utilization', 'comments', 'tags',
|
||||
]
|
||||
widgets = {
|
||||
@ -3241,24 +3241,24 @@ class PowerFeedForm(BootstrapMixin, CustomFieldForm):
|
||||
'rack': APISelect(
|
||||
api_url="/api/dcim/racks/"
|
||||
),
|
||||
'type': StaticSelect2(),
|
||||
'status': StaticSelect2(),
|
||||
'type': StaticSelect2(),
|
||||
'supply': StaticSelect2(),
|
||||
'phase': StaticSelect2(),
|
||||
}
|
||||
|
||||
|
||||
class PowerFeedCSVForm(forms.ModelForm):
|
||||
type = CSVChoiceField(
|
||||
choices=POWERFEED_TYPE_CHOICES,
|
||||
required=False,
|
||||
help_text='Primary or redundant'
|
||||
)
|
||||
status = CSVChoiceField(
|
||||
choices=POWERFEED_STATUS_CHOICES,
|
||||
required=False,
|
||||
help_text='Operational status'
|
||||
)
|
||||
type = CSVChoiceField(
|
||||
choices=POWERFEED_TYPE_CHOICES,
|
||||
required=False,
|
||||
help_text='Primary or redundant'
|
||||
)
|
||||
supply = CSVChoiceField(
|
||||
choices=POWERFEED_SUPPLY_CHOICES,
|
||||
required=False,
|
||||
@ -3292,14 +3292,14 @@ class PowerFeedBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEd
|
||||
api_url="/api/dcim/rack-groups",
|
||||
)
|
||||
)
|
||||
type = forms.ChoiceField(
|
||||
choices=add_blank_choice(POWERFEED_TYPE_CHOICES),
|
||||
status = forms.ChoiceField(
|
||||
choices=add_blank_choice(POWERFEED_STATUS_CHOICES),
|
||||
required=False,
|
||||
initial='',
|
||||
widget=StaticSelect2()
|
||||
)
|
||||
status = forms.ChoiceField(
|
||||
choices=add_blank_choice(POWERFEED_STATUS_CHOICES),
|
||||
type = forms.ChoiceField(
|
||||
choices=add_blank_choice(POWERFEED_TYPE_CHOICES),
|
||||
required=False,
|
||||
initial='',
|
||||
widget=StaticSelect2()
|
||||
@ -3310,18 +3310,18 @@ class PowerFeedBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEd
|
||||
initial='',
|
||||
widget=StaticSelect2()
|
||||
)
|
||||
voltage = forms.IntegerField(
|
||||
required=False
|
||||
)
|
||||
amperage = forms.IntegerField(
|
||||
required=False
|
||||
)
|
||||
phase = forms.ChoiceField(
|
||||
choices=add_blank_choice(POWERFEED_PHASE_CHOICES),
|
||||
required=False,
|
||||
initial='',
|
||||
widget=StaticSelect2()
|
||||
)
|
||||
voltage = forms.IntegerField(
|
||||
required=False
|
||||
)
|
||||
amperage = forms.IntegerField(
|
||||
required=False
|
||||
)
|
||||
max_utilization = forms.IntegerField(
|
||||
required=False
|
||||
)
|
||||
|
@ -2730,18 +2730,22 @@ class PowerFeed(ChangeLoggedModel, CustomFieldModel):
|
||||
name = models.CharField(
|
||||
max_length=50
|
||||
)
|
||||
type = models.PositiveSmallIntegerField(
|
||||
choices=POWERFEED_TYPE_CHOICES,
|
||||
default=POWERFEED_TYPE_PRIMARY
|
||||
)
|
||||
status = models.PositiveSmallIntegerField(
|
||||
choices=POWERFEED_STATUS_CHOICES,
|
||||
default=POWERFEED_STATUS_ACTIVE
|
||||
)
|
||||
type = models.PositiveSmallIntegerField(
|
||||
choices=POWERFEED_TYPE_CHOICES,
|
||||
default=POWERFEED_TYPE_PRIMARY
|
||||
)
|
||||
supply = models.PositiveSmallIntegerField(
|
||||
choices=POWERFEED_SUPPLY_CHOICES,
|
||||
default=POWERFEED_SUPPLY_AC
|
||||
)
|
||||
phase = models.PositiveSmallIntegerField(
|
||||
choices=POWERFEED_PHASE_CHOICES,
|
||||
default=POWERFEED_PHASE_SINGLE
|
||||
)
|
||||
voltage = models.PositiveSmallIntegerField(
|
||||
validators=[MinValueValidator(1)],
|
||||
default=120
|
||||
@ -2750,10 +2754,6 @@ class PowerFeed(ChangeLoggedModel, CustomFieldModel):
|
||||
validators=[MinValueValidator(1)],
|
||||
default=20
|
||||
)
|
||||
phase = models.PositiveSmallIntegerField(
|
||||
choices=POWERFEED_PHASE_CHOICES,
|
||||
default=POWERFEED_PHASE_SINGLE
|
||||
)
|
||||
max_utilization = models.PositiveSmallIntegerField(
|
||||
validators=[MinValueValidator(1), MaxValueValidator(100)],
|
||||
default=80,
|
||||
@ -2771,7 +2771,7 @@ class PowerFeed(ChangeLoggedModel, CustomFieldModel):
|
||||
tags = TaggableManager(through=TaggedItem)
|
||||
|
||||
csv_headers = [
|
||||
'power_panel', 'rack', 'name', 'type', 'status', 'supply', 'voltage', 'amperage', 'phase', 'max_utilization',
|
||||
'power_panel', 'rack', 'name', 'status', 'type', 'supply', 'phase', 'voltage', 'amperage', 'max_utilization',
|
||||
'comments',
|
||||
]
|
||||
|
||||
@ -2790,12 +2790,18 @@ class PowerFeed(ChangeLoggedModel, CustomFieldModel):
|
||||
self.power_panel.name,
|
||||
self.rack.name if self.rack else None,
|
||||
self.name,
|
||||
self.get_type_display(),
|
||||
self.get_status_display(),
|
||||
self.get_type_display(),
|
||||
self.get_supply_display(),
|
||||
self.get_phase_display(),
|
||||
self.voltage,
|
||||
self.amperage,
|
||||
self.get_phase_display(),
|
||||
self.max_utilization,
|
||||
self.comments,
|
||||
)
|
||||
|
||||
def get_type_class(self):
|
||||
return STATUS_CLASSES[self.type]
|
||||
|
||||
def get_status_class(self):
|
||||
return STATUS_CLASSES[self.status]
|
||||
|
@ -145,6 +145,10 @@ STATUS_LABEL = """
|
||||
<span class="label label-{{ record.get_status_class }}">{{ record.get_status_display }}</span>
|
||||
"""
|
||||
|
||||
TYPE_LABEL = """
|
||||
<span class="label label-{{ record.get_type_class }}">{{ record.get_type_display }}</span>
|
||||
"""
|
||||
|
||||
DEVICE_PRIMARY_IP = """
|
||||
{{ record.primary_ip6.address.ip|default:"" }}
|
||||
{% if record.primary_ip6 and record.primary_ip4 %}<br />{% endif %}
|
||||
@ -799,17 +803,10 @@ class PowerPanelTable(BaseTable):
|
||||
powerfeed_count = tables.Column(
|
||||
verbose_name='Feeds'
|
||||
)
|
||||
actions = tables.TemplateColumn(
|
||||
template_code=RACKROLE_ACTIONS,
|
||||
attrs={
|
||||
'td': {'class': 'text-right noprint'}
|
||||
},
|
||||
verbose_name=''
|
||||
)
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = PowerPanel
|
||||
fields = ('pk', 'name', 'site', 'rackgroup', 'powerfeed_count', 'actions')
|
||||
fields = ('pk', 'name', 'site', 'rack_group', 'powerfeed_count')
|
||||
|
||||
|
||||
#
|
||||
@ -819,16 +816,18 @@ class PowerPanelTable(BaseTable):
|
||||
class PowerFeedTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
name = tables.LinkColumn()
|
||||
powerpanel = tables.LinkColumn(
|
||||
power_panel = tables.LinkColumn(
|
||||
viewname='dcim:powerpanel',
|
||||
args=[Accessor('powerpanel.pk')],
|
||||
args=[Accessor('power_panel.pk')],
|
||||
|
||||
)
|
||||
rack = tables.LinkColumn(
|
||||
viewname='dcim:rack',
|
||||
accessor=Accessor('rack.pk')
|
||||
status = tables.TemplateColumn(
|
||||
template_code=STATUS_LABEL
|
||||
)
|
||||
type = tables.TemplateColumn(
|
||||
template_code=TYPE_LABEL
|
||||
)
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = PowerFeed
|
||||
fields = ('pk', 'name', 'powerpanel', 'rack', 'type', 'status', 'supply', 'voltage', 'amperage', 'phase')
|
||||
fields = ('pk', 'name', 'power_panel', 'rack', 'status', 'type', 'supply', 'voltage', 'amperage', 'phase')
|
||||
|
@ -392,10 +392,12 @@ class RackView(View):
|
||||
prev_rack = Rack.objects.filter(site=rack.site, name__lt=rack.name).order_by('-name').first()
|
||||
|
||||
reservations = RackReservation.objects.filter(rack=rack)
|
||||
power_feeds = PowerFeed.objects.filter(rack=rack).select_related('power_panel')
|
||||
|
||||
return render(request, 'dcim/rack.html', {
|
||||
'rack': rack,
|
||||
'reservations': reservations,
|
||||
'power_feeds': power_feeds,
|
||||
'nonracked_devices': nonracked_devices,
|
||||
'next_rack': next_rack,
|
||||
'prev_rack': prev_rack,
|
||||
@ -2123,9 +2125,9 @@ class VirtualChassisRemoveMemberView(PermissionRequiredMixin, GetReturnURLMixin,
|
||||
|
||||
class PowerPanelListView(ObjectListView):
|
||||
queryset = PowerPanel.objects.select_related(
|
||||
'site', 'rackgroup'
|
||||
'site', 'rack_group'
|
||||
).annotate(
|
||||
rack_count=Count('powerfeeds')
|
||||
powerfeed_count=Count('powerfeeds')
|
||||
)
|
||||
table = tables.PowerPanelTable
|
||||
template_name = 'dcim/powerpanel_list.html'
|
||||
@ -2183,7 +2185,7 @@ class PowerPanelBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||
|
||||
class PowerFeedListView(ObjectListView):
|
||||
queryset = PowerFeed.objects.select_related(
|
||||
'powerpanel', 'rack'
|
||||
'power_panel', 'rack'
|
||||
)
|
||||
# filter = filters.PowerFeedFilter
|
||||
# filter_form = forms.PowerFeedFilterForm
|
||||
@ -2229,7 +2231,7 @@ class PowerFeedBulkImportView(PermissionRequiredMixin, BulkImportView):
|
||||
|
||||
class PowerFeedBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||
permission_required = 'dcim.change_powerfeed'
|
||||
queryset = PowerFeed.objects.select_related('powerpanel', 'rack')
|
||||
queryset = PowerFeed.objects.select_related('power_panel', 'rack')
|
||||
# filter = filters.PowerFeedFilter
|
||||
table = tables.PowerFeedTable
|
||||
form = forms.PowerFeedBulkEditForm
|
||||
@ -2238,7 +2240,7 @@ class PowerFeedBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||
|
||||
class PowerFeedBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||
permission_required = 'dcim.delete_powerfeed'
|
||||
queryset = PowerFeed.objects.select_related('powerpanel', 'rack')
|
||||
queryset = PowerFeed.objects.select_related('power_panel', 'rack')
|
||||
# filter = filters.PowerFeedFilter
|
||||
table = tables.PowerFeedTable
|
||||
default_return_url = 'dcim:powerfeed_list'
|
||||
|
@ -9,13 +9,13 @@
|
||||
{% render_field form.power_panel %}
|
||||
{% render_field form.rack %}
|
||||
{% render_field form.name %}
|
||||
{% render_field form.type %}
|
||||
{% render_field form.status %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><strong>Characteristics</strong></div>
|
||||
<div class="panel-body">
|
||||
{% render_field form.type %}
|
||||
{% render_field form.supply %}
|
||||
{% render_field form.voltage %}
|
||||
{% render_field form.amperage %}
|
||||
|
@ -190,47 +190,37 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% if power_feeds %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Non-Racked Devices</strong>
|
||||
<strong>Power Feeds</strong>
|
||||
</div>
|
||||
{% if nonracked_devices %}
|
||||
<table class="table table-hover panel-body">
|
||||
<table class="table panel-body">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Role</th>
|
||||
<th>Panel</th>
|
||||
<th>Feed</th>
|
||||
<th>Status</th>
|
||||
<th>Type</th>
|
||||
<th>Parent</th>
|
||||
</tr>
|
||||
{% for device in nonracked_devices %}
|
||||
<tr{% if device.device_type.u_height %} class="warning"{% endif %}>
|
||||
{% for powerfeed in power_feeds %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{% url 'dcim:device' pk=device.pk %}">{{ device }}</a>
|
||||
<a href="{{ powerfeed.power_panel.get_absolute_url }}">{{ powerfeed.power_panel.name }}</a>
|
||||
|
||||
<td>
|
||||
<a href="{{ powerfeed.get_absolute_url }}">{{ powerfeed.name }}</a>
|
||||
</td>
|
||||
<td>{{ device.device_role }}</td>
|
||||
<td>{{ device.device_type.display_name }}</td>
|
||||
<td>
|
||||
{% if device.parent_bay %}
|
||||
<a href="{{ device.parent_bay.device.get_absolute_url }}">{{ device.parent_bay }}</a>
|
||||
{% else %}
|
||||
<span class="text-muted">—</span>
|
||||
{% endif %}
|
||||
<span class="label label-{{ powerfeed.get_status_class }}">{{ powerfeed.get_status_display }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="label label-{{ powerfeed.get_type_class }}">{{ powerfeed.get_type_display }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
<div class="panel-body text-muted">None</div>
|
||||
{% endif %}
|
||||
{% if perms.dcim.add_device %}
|
||||
<div class="panel-footer text-right noprint">
|
||||
<a href="{% url 'dcim:device_add' %}?site={{ rack.site.pk }}&rack={{ rack.pk }}" class="btn btn-primary btn-xs">
|
||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
||||
Add a non-racked device
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Images</strong>
|
||||
@ -312,6 +302,47 @@
|
||||
</div>
|
||||
{% include 'dcim/inc/rack_elevation.html' with primary_face=rear_elevation secondary_face=front_elevation face_id=1 reserved_units=rack.get_reserved_units %}
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Non-Racked Devices</strong>
|
||||
</div>
|
||||
{% if nonracked_devices %}
|
||||
<table class="table table-hover panel-body">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Role</th>
|
||||
<th>Type</th>
|
||||
<th>Parent</th>
|
||||
</tr>
|
||||
{% for device in nonracked_devices %}
|
||||
<tr{% if device.device_type.u_height %} class="warning"{% endif %}>
|
||||
<td>
|
||||
<a href="{% url 'dcim:device' pk=device.pk %}">{{ device }}</a>
|
||||
</td>
|
||||
<td>{{ device.device_role }}</td>
|
||||
<td>{{ device.device_type.display_name }}</td>
|
||||
<td>
|
||||
{% if device.parent_bay %}
|
||||
<a href="{{ device.parent_bay.device.get_absolute_url }}">{{ device.parent_bay }}</a>
|
||||
{% else %}
|
||||
<span class="text-muted">—</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
<div class="panel-body text-muted">None</div>
|
||||
{% endif %}
|
||||
{% if perms.dcim.add_device %}
|
||||
<div class="panel-footer text-right noprint">
|
||||
<a href="{% url 'dcim:device_add' %}?site={{ rack.site.pk }}&rack={{ rack.pk }}" class="btn btn-primary btn-xs">
|
||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
||||
Add a non-racked device
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
Reference in New Issue
Block a user