mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-22 20:12:00 -06:00
Initial work on power modeling (WIP)
This commit is contained in:
parent
0b95016e00
commit
5b753923b6
@ -443,3 +443,33 @@ RACK_DIMENSION_UNIT_CHOICES = (
|
||||
(LENGTH_UNIT_MILLIMETER, 'Millimeters'),
|
||||
(LENGTH_UNIT_INCH, 'Inches'),
|
||||
)
|
||||
|
||||
# Power feeds
|
||||
POWERFEED_TYPE_PRIMARY = 1
|
||||
POWERFEED_TYPE_REDUNDANT = 2
|
||||
POWERFEED_TYPE_CHOICES = (
|
||||
(POWERFEED_TYPE_PRIMARY, 'AC'),
|
||||
(POWERFEED_TYPE_REDUNDANT, 'DC'),
|
||||
)
|
||||
POWERFEED_SUPPLY_AC = 1
|
||||
POWERFEED_SUPPLY_DC = 2
|
||||
POWERFEED_SUPPLY_CHOICES = (
|
||||
(POWERFEED_SUPPLY_AC, 'AC'),
|
||||
(POWERFEED_SUPPLY_DC, 'DC'),
|
||||
)
|
||||
POWERFEED_PHASE_SINGLE = 1
|
||||
POWERFEED_PHASE_3PHASE = 3
|
||||
POWERFEED_PHASE_CHOICES = (
|
||||
(POWERFEED_PHASE_SINGLE, 'Single phase'),
|
||||
(POWERFEED_PHASE_3PHASE, 'Three-phase'),
|
||||
)
|
||||
POWERFEED_STATUS_OFFLINE = 0
|
||||
POWERFEED_STATUS_ACTIVE = 1
|
||||
POWERFEED_STATUS_PLANNED = 2
|
||||
POWERFEED_STATUS_FAILED = 4
|
||||
POWERFEED_STATUS_CHOICES = (
|
||||
(POWERFEED_STATUS_ACTIVE, 'Active'),
|
||||
(POWERFEED_STATUS_OFFLINE, 'Offline'),
|
||||
(POWERFEED_STATUS_PLANNED, 'Planned'),
|
||||
(POWERFEED_STATUS_FAILED, 'Failed'),
|
||||
)
|
||||
|
@ -26,8 +26,8 @@ from .constants import *
|
||||
from .models import (
|
||||
Cable, DeviceBay, DeviceBayTemplate, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate,
|
||||
Device, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate, Manufacturer,
|
||||
InventoryItem, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack, RackGroup,
|
||||
RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site, VirtualChassis
|
||||
InventoryItem, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate, PowerPanel, PowerPort, PowerPortTemplate,
|
||||
Rack, RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site, VirtualChassis,
|
||||
)
|
||||
|
||||
DEVICE_BY_PK_RE = r'{\d+\}'
|
||||
@ -3156,3 +3156,169 @@ class VirtualChassisFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
to_field_name='slug',
|
||||
null_label='-- None --',
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# Power panels
|
||||
#
|
||||
|
||||
class PowerPanelForm(BootstrapMixin, forms.ModelForm):
|
||||
rackgroup = ChainedModelChoiceField(
|
||||
queryset=RackGroup.objects.all(),
|
||||
chains=(
|
||||
('site', 'site'),
|
||||
),
|
||||
required=False,
|
||||
widget=APISelect(
|
||||
api_url='/api/dcim/rack-groups/',
|
||||
)
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = PowerPanel
|
||||
fields = [
|
||||
'site', 'rackgroup', 'name',
|
||||
]
|
||||
widgets = {
|
||||
'site': APISelect(
|
||||
api_url="/api/dcim/sites/",
|
||||
filter_for={
|
||||
'rackgroup': 'site_id',
|
||||
}
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
class PowerPanelCSVForm(forms.ModelForm):
|
||||
site = forms.ModelChoiceField(
|
||||
queryset=Site.objects.all(),
|
||||
to_field_name='name',
|
||||
help_text='Name of parent site',
|
||||
error_messages={
|
||||
'invalid_choice': 'Site not found.',
|
||||
}
|
||||
)
|
||||
group_name = forms.CharField(
|
||||
help_text='Name of rack group',
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = PowerPanel
|
||||
fields = PowerPanel.csv_headers
|
||||
|
||||
|
||||
#
|
||||
# Power feeds
|
||||
#
|
||||
|
||||
class PowerFeedForm(BootstrapMixin, CustomFieldForm):
|
||||
tags = TagField(
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = PowerFeed
|
||||
fields = [
|
||||
'powerpanel', 'rack', 'name', 'type', 'status', 'supply', 'voltage', 'amperage', 'phase', 'max_utilization',
|
||||
'comments', 'tags',
|
||||
]
|
||||
widgets = {
|
||||
'site': APISelect(
|
||||
api_url="/api/dcim/sites/",
|
||||
filter_for={
|
||||
'rackgroup': 'site_id',
|
||||
}
|
||||
),
|
||||
'type': StaticSelect2(),
|
||||
'status': 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'
|
||||
)
|
||||
supply = CSVChoiceField(
|
||||
choices=POWERFEED_SUPPLY_CHOICES,
|
||||
required=False,
|
||||
help_text='AC/DC'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = PowerFeed
|
||||
fields = PowerFeed.csv_headers
|
||||
|
||||
|
||||
class PowerFeedBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=PowerFeed.objects.all(),
|
||||
widget=forms.MultipleHiddenInput
|
||||
)
|
||||
powerpanel = forms.ModelChoiceField(
|
||||
queryset=PowerPanel.objects.all(),
|
||||
required=False,
|
||||
widget=APISelect(
|
||||
api_url="/api/dcim/sites",
|
||||
filter_for={
|
||||
'rackgroup': 'site_id',
|
||||
}
|
||||
)
|
||||
)
|
||||
rackgroup = forms.ModelChoiceField(
|
||||
queryset=RackGroup.objects.all(),
|
||||
required=False,
|
||||
widget=APISelect(
|
||||
api_url="/api/dcim/rack-groups",
|
||||
)
|
||||
)
|
||||
type = forms.ChoiceField(
|
||||
choices=add_blank_choice(POWERFEED_TYPE_CHOICES),
|
||||
required=False,
|
||||
initial='',
|
||||
widget=StaticSelect2()
|
||||
)
|
||||
status = forms.ChoiceField(
|
||||
choices=add_blank_choice(POWERFEED_STATUS_CHOICES),
|
||||
required=False,
|
||||
initial='',
|
||||
widget=StaticSelect2()
|
||||
)
|
||||
supply = forms.ChoiceField(
|
||||
choices=add_blank_choice(POWERFEED_SUPPLY_CHOICES),
|
||||
required=False,
|
||||
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()
|
||||
)
|
||||
max_utilization = forms.IntegerField(
|
||||
required=False
|
||||
)
|
||||
comments = forms.CharField(
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
nullable_fields = [
|
||||
'rackgroup', 'comments',
|
||||
]
|
||||
|
74
netbox/dcim/migrations/0072_powerfeeds.py
Normal file
74
netbox/dcim/migrations/0072_powerfeeds.py
Normal file
@ -0,0 +1,74 @@
|
||||
# Generated by Django 2.1.7 on 2019-03-12 02:29
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import taggit.managers
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('extras', '0020_add_color_comments_changelog_to_tag'),
|
||||
('dcim', '0071_device_components_add_description'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='PowerFeed',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)),
|
||||
('created', models.DateField(auto_now_add=True, null=True)),
|
||||
('last_updated', models.DateTimeField(auto_now=True, null=True)),
|
||||
('name', models.CharField(max_length=50)),
|
||||
('type', models.PositiveSmallIntegerField(default=1)),
|
||||
('status', models.PositiveSmallIntegerField(default=1)),
|
||||
('supply', models.PositiveSmallIntegerField(default=1)),
|
||||
('voltage', models.PositiveSmallIntegerField(default=120, validators=[django.core.validators.MinValueValidator(1)])),
|
||||
('amperage', models.PositiveSmallIntegerField(default=20, validators=[django.core.validators.MinValueValidator(1)])),
|
||||
('phase', models.PositiveSmallIntegerField(default=1)),
|
||||
('max_utilization', models.PositiveSmallIntegerField(default=80, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(100)])),
|
||||
('comments', models.TextField(blank=True)),
|
||||
],
|
||||
options={
|
||||
'ordering': ['powerpanel', 'name'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PowerPanel',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)),
|
||||
('created', models.DateField(auto_now_add=True, null=True)),
|
||||
('last_updated', models.DateTimeField(auto_now=True, null=True)),
|
||||
('name', models.CharField(max_length=50)),
|
||||
('rackgroup', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='dcim.RackGroup')),
|
||||
('site', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='dcim.Site')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['site', 'name'],
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='powerfeed',
|
||||
name='powerpanel',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='powerfeeds', to='dcim.PowerPanel'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='powerfeed',
|
||||
name='rack',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='dcim.Rack'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='powerfeed',
|
||||
name='tags',
|
||||
field=taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag'),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='powerpanel',
|
||||
unique_together={('site', 'name')},
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='powerfeed',
|
||||
unique_together={('powerpanel', 'name')},
|
||||
),
|
||||
]
|
@ -2668,3 +2668,134 @@ class Cable(ChangeLoggedModel):
|
||||
b_endpoint = b_path[-1][2]
|
||||
|
||||
return a_endpoint, b_endpoint, path_status
|
||||
|
||||
|
||||
#
|
||||
# Power
|
||||
#
|
||||
|
||||
class PowerPanel(ChangeLoggedModel):
|
||||
"""
|
||||
A distribution point for electrical power; e.g. a data center RPP.
|
||||
"""
|
||||
site = models.ForeignKey(
|
||||
to='Site',
|
||||
on_delete=models.PROTECT
|
||||
)
|
||||
rackgroup = models.ForeignKey(
|
||||
to='RackGroup',
|
||||
on_delete=models.PROTECT,
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
name = models.CharField(
|
||||
max_length=50
|
||||
)
|
||||
|
||||
csv_headers = ['site', 'rackgroup', 'name']
|
||||
|
||||
class Meta:
|
||||
ordering = ['site', 'name']
|
||||
unique_together = ['site', 'name']
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('dcim:powerpanel', args=[self.pk])
|
||||
|
||||
def to_csv(self):
|
||||
return (
|
||||
self.site.name,
|
||||
self.rackgroup.name if self.rackgroup else None,
|
||||
self.name,
|
||||
)
|
||||
|
||||
|
||||
class PowerFeed(ChangeLoggedModel, CustomFieldModel):
|
||||
"""
|
||||
An electrical circuit delivered from a PowerPanel.
|
||||
"""
|
||||
powerpanel = models.ForeignKey(
|
||||
to='PowerPanel',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='powerfeeds'
|
||||
)
|
||||
rack = models.ForeignKey(
|
||||
to='Rack',
|
||||
on_delete=models.PROTECT,
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
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
|
||||
)
|
||||
supply = models.PositiveSmallIntegerField(
|
||||
choices=POWERFEED_SUPPLY_CHOICES,
|
||||
default=POWERFEED_SUPPLY_AC
|
||||
)
|
||||
voltage = models.PositiveSmallIntegerField(
|
||||
validators=[MinValueValidator(1)],
|
||||
default=120
|
||||
)
|
||||
amperage = models.PositiveSmallIntegerField(
|
||||
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,
|
||||
help_text="Maximum permissible draw (percentage)"
|
||||
)
|
||||
comments = models.TextField(
|
||||
blank=True
|
||||
)
|
||||
custom_field_values = GenericRelation(
|
||||
to='extras.CustomFieldValue',
|
||||
content_type_field='obj_type',
|
||||
object_id_field='obj_id'
|
||||
)
|
||||
|
||||
tags = TaggableManager(through=TaggedItem)
|
||||
|
||||
csv_headers = [
|
||||
'powerpanel', 'rack', 'name', 'type', 'status', 'supply', 'voltage', 'amperage', 'phase', 'max_utilization',
|
||||
'comments',
|
||||
]
|
||||
|
||||
class Meta:
|
||||
ordering = ['powerpanel', 'name']
|
||||
unique_together = ['powerpanel', 'name']
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('dcim:powerfeed', args=[self.pk])
|
||||
|
||||
def to_csv(self):
|
||||
return (
|
||||
self.powerpanel.name,
|
||||
self.rack.name if self.rack else None,
|
||||
self.name,
|
||||
self.get_type_display(),
|
||||
self.get_status_display(),
|
||||
self.get_supply_display(),
|
||||
self.voltage,
|
||||
self.amperage,
|
||||
self.get_phase_display(),
|
||||
self.max_utilization,
|
||||
self.comments,
|
||||
)
|
||||
|
@ -6,8 +6,9 @@ from utilities.tables import BaseTable, BooleanColumn, ColorColumn, ToggleColumn
|
||||
from .models import (
|
||||
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
||||
DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
|
||||
InventoryItem, Manufacturer, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack,
|
||||
RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site, VirtualChassis,
|
||||
InventoryItem, Manufacturer, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate, PowerPanel, PowerPort,
|
||||
PowerPortTemplate, Rack, RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site,
|
||||
VirtualChassis,
|
||||
)
|
||||
|
||||
REGION_LINK = """
|
||||
@ -786,3 +787,48 @@ class VirtualChassisTable(BaseTable):
|
||||
class Meta(BaseTable.Meta):
|
||||
model = VirtualChassis
|
||||
fields = ('pk', 'master', 'domain', 'member_count', 'actions')
|
||||
|
||||
|
||||
#
|
||||
# Power panels
|
||||
#
|
||||
|
||||
class PowerPanelTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
name = tables.LinkColumn()
|
||||
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')
|
||||
|
||||
|
||||
#
|
||||
# Power feeds
|
||||
#
|
||||
|
||||
class PowerFeedTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
name = tables.LinkColumn()
|
||||
powerpanel = tables.LinkColumn(
|
||||
viewname='dcim:powerpanel',
|
||||
args=[Accessor('powerpanel.pk')],
|
||||
|
||||
)
|
||||
rack = tables.LinkColumn(
|
||||
viewname='dcim:rack',
|
||||
accessor=Accessor('rack.pk')
|
||||
)
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = PowerFeed
|
||||
fields = ('pk', 'name', 'powerpanel', 'rack', 'type', 'status', 'supply', 'voltage', 'amperage', 'phase')
|
||||
|
@ -6,7 +6,8 @@ from secrets.views import secret_add
|
||||
from . import views
|
||||
from .models import (
|
||||
Cable, ConsolePort, ConsoleServerPort, Device, DeviceRole, DeviceType, FrontPort, Interface, Manufacturer, Platform,
|
||||
PowerPort, PowerOutlet, Rack, RackGroup, RackReservation, RackRole, RearPort, Region, Site, VirtualChassis,
|
||||
PowerFeed, PowerPanel, PowerPort, PowerOutlet, Rack, RackGroup, RackReservation, RackRole, RearPort, Region, Site,
|
||||
VirtualChassis,
|
||||
)
|
||||
|
||||
app_name = 'dcim'
|
||||
@ -279,4 +280,23 @@ urlpatterns = [
|
||||
url(r'^virtual-chassis/(?P<pk>\d+)/add-member/$', views.VirtualChassisAddMemberView.as_view(), name='virtualchassis_add_member'),
|
||||
url(r'^virtual-chassis-members/(?P<pk>\d+)/delete/$', views.VirtualChassisRemoveMemberView.as_view(), name='virtualchassis_remove_member'),
|
||||
|
||||
# Power panels
|
||||
url(r'^power-panels/$', views.PowerPanelListView.as_view(), name='powerpanel_list'),
|
||||
url(r'^power-panels/add/$', views.PowerPanelCreateView.as_view(), name='powerpanel_add'),
|
||||
url(r'^power-panels/import/$', views.PowerPanelBulkImportView.as_view(), name='powerpanel_import'),
|
||||
url(r'^power-panels/delete/$', views.PowerPanelBulkDeleteView.as_view(), name='powerpanel_bulk_delete'),
|
||||
url(r'^power-panels/(?P<pk>\d+)/edit/$', views.PowerPanelEditView.as_view(), name='powerpanel_edit'),
|
||||
url(r'^power-panels/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='powerpanel_changelog', kwargs={'model': PowerPanel}),
|
||||
|
||||
# Racks
|
||||
url(r'^power-feeds/$', views.PowerFeedListView.as_view(), name='powerfeed_list'),
|
||||
url(r'^power-feeds/add/$', views.PowerFeedEditView.as_view(), name='powerfeed_add'),
|
||||
url(r'^power-feeds/import/$', views.PowerFeedBulkImportView.as_view(), name='powerfeed_import'),
|
||||
url(r'^power-feeds/edit/$', views.PowerFeedBulkEditView.as_view(), name='powerfeed_bulk_edit'),
|
||||
url(r'^power-feeds/delete/$', views.PowerFeedBulkDeleteView.as_view(), name='powerfeed_bulk_delete'),
|
||||
url(r'^power-feeds/(?P<pk>\d+)/$', views.PowerFeedView.as_view(), name='powerfeed'),
|
||||
url(r'^power-feeds/(?P<pk>\d+)/edit/$', views.PowerFeedEditView.as_view(), name='powerfeed_edit'),
|
||||
url(r'^power-feeds/(?P<pk>\d+)/delete/$', views.PowerFeedDeleteView.as_view(), name='powerfeed_delete'),
|
||||
url(r'^power-feeds/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='powerfeed_changelog', kwargs={'model': PowerFeed}),
|
||||
|
||||
]
|
||||
|
@ -30,8 +30,9 @@ from . import filters, forms, tables
|
||||
from .models import (
|
||||
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
||||
DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
|
||||
InventoryItem, Manufacturer, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack,
|
||||
RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site, VirtualChassis,
|
||||
InventoryItem, Manufacturer, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate, PowerPanel, PowerPort,
|
||||
PowerPortTemplate, Rack, RackGroup, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site,
|
||||
VirtualChassis,
|
||||
)
|
||||
|
||||
|
||||
@ -2114,3 +2115,113 @@ class VirtualChassisRemoveMemberView(PermissionRequiredMixin, GetReturnURLMixin,
|
||||
'form': form,
|
||||
'return_url': self.get_return_url(request, device),
|
||||
})
|
||||
|
||||
|
||||
#
|
||||
# Power panels
|
||||
#
|
||||
|
||||
class PowerPanelListView(ObjectListView):
|
||||
queryset = PowerPanel.objects.select_related(
|
||||
'site', 'rackgroup'
|
||||
).annotate(
|
||||
rack_count=Count('powerfeeds')
|
||||
)
|
||||
table = tables.PowerPanelTable
|
||||
template_name = 'dcim/powerpanel_list.html'
|
||||
|
||||
|
||||
class PowerPanelCreateView(PermissionRequiredMixin, ObjectEditView):
|
||||
permission_required = 'dcim.add_powerpanel'
|
||||
model = PowerPanel
|
||||
model_form = forms.PowerPanelForm
|
||||
default_return_url = 'dcim:powerpanel_list'
|
||||
|
||||
|
||||
class PowerPanelEditView(PowerPanelCreateView):
|
||||
permission_required = 'dcim.change_powerpanel'
|
||||
|
||||
|
||||
class PowerPanelBulkImportView(PermissionRequiredMixin, BulkImportView):
|
||||
permission_required = 'dcim.add_powerpanel'
|
||||
model_form = forms.PowerPanelCSVForm
|
||||
table = tables.PowerPanelTable
|
||||
default_return_url = 'dcim:powerpanel_list'
|
||||
|
||||
|
||||
class PowerPanelBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||
permission_required = 'dcim.delete_powerpanel'
|
||||
queryset = PowerPanel.objects.select_related(
|
||||
'site', 'rack_group'
|
||||
).annotate(
|
||||
rack_count=Count('powerfeeds')
|
||||
)
|
||||
table = tables.PowerPanelTable
|
||||
default_return_url = 'dcim:powerpanel_list'
|
||||
|
||||
|
||||
#
|
||||
# Power feeds
|
||||
#
|
||||
|
||||
class PowerFeedListView(ObjectListView):
|
||||
queryset = PowerFeed.objects.select_related(
|
||||
'powerpanel', 'rack'
|
||||
)
|
||||
# filter = filters.PowerFeedFilter
|
||||
# filter_form = forms.PowerFeedFilterForm
|
||||
table = tables.PowerFeedTable
|
||||
template_name = 'dcim/powerfeed_list.html'
|
||||
|
||||
|
||||
class PowerFeedView(View):
|
||||
|
||||
def get(self, request, pk):
|
||||
|
||||
powerfeed = get_object_or_404(PowerFeed.objects.select_related('panel', 'rack'), pk=pk)
|
||||
|
||||
return render(request, 'dcim/powerfeed.html', {
|
||||
'powerfeed': powerfeed,
|
||||
})
|
||||
|
||||
|
||||
class PowerFeedCreateView(PermissionRequiredMixin, ObjectEditView):
|
||||
permission_required = 'dcim.add_powerfeed'
|
||||
model = PowerFeed
|
||||
model_form = forms.PowerFeedForm
|
||||
template_name = 'dcim/powerfeed_edit.html'
|
||||
default_return_url = 'dcim:powerfeed_list'
|
||||
|
||||
|
||||
class PowerFeedEditView(PowerFeedCreateView):
|
||||
permission_required = 'dcim.change_powerfeed'
|
||||
|
||||
|
||||
class PowerFeedDeleteView(PermissionRequiredMixin, ObjectDeleteView):
|
||||
permission_required = 'dcim.delete_powerfeed'
|
||||
model = PowerFeed
|
||||
default_return_url = 'dcim:powerfeed_list'
|
||||
|
||||
|
||||
class PowerFeedBulkImportView(PermissionRequiredMixin, BulkImportView):
|
||||
permission_required = 'dcim.add_powerfeed'
|
||||
model_form = forms.PowerFeedCSVForm
|
||||
table = tables.PowerFeedTable
|
||||
default_return_url = 'dcim:powerfeed_list'
|
||||
|
||||
|
||||
class PowerFeedBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||
permission_required = 'dcim.change_powerfeed'
|
||||
queryset = PowerFeed.objects.select_related('powerpanel', 'rack')
|
||||
# filter = filters.PowerFeedFilter
|
||||
table = tables.PowerFeedTable
|
||||
form = forms.PowerFeedBulkEditForm
|
||||
default_return_url = 'dcim:powerfeed_list'
|
||||
|
||||
|
||||
class PowerFeedBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||
permission_required = 'dcim.delete_powerfeed'
|
||||
queryset = PowerFeed.objects.select_related('powerpanel', 'rack')
|
||||
# filter = filters.PowerFeedFilter
|
||||
table = tables.PowerFeedTable
|
||||
default_return_url = 'dcim:powerfeed_list'
|
||||
|
111
netbox/templates/dcim/powerfeed.html
Normal file
111
netbox/templates/dcim/powerfeed.html
Normal file
@ -0,0 +1,111 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load static %}
|
||||
{% load tz %}
|
||||
{% load helpers %}
|
||||
|
||||
{% block header %}
|
||||
<div class="row noprint">
|
||||
<div class="col-sm-8 col-md-9">
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{% url 'dcim:powerfeed_list' %}">Power Feeds</a></li>
|
||||
<li><a href="{{ powerfeed.site.get_absolute_url }}">{{ powerfeed.site }}</a></li>
|
||||
{% if powerfeed.rackgroup %}
|
||||
<li><a href="{{ powerfeed.rackgroup.get_absolute_url }}">{{ powerfeed.rackgroup }}</a></li>
|
||||
{% endif %}
|
||||
<li>{{ site }}</li>
|
||||
</ol>
|
||||
</div>
|
||||
<div class="col-sm-4 col-md-3">
|
||||
<form action="{% url 'dcim:powerfeed_list' %}" method="get">
|
||||
<div class="input-group">
|
||||
<input type="text" name="q" class="form-control" placeholder="Search power feeds" />
|
||||
<span class="input-group-btn">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<span class="fa fa-search" aria-hidden="true"></span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pull-right noprint">
|
||||
{% if perms.dcim.change_powerfeed %}
|
||||
<a href="{% url 'dcim:powerfeed_edit' pk=powerfeed.pk %}" class="btn btn-warning">
|
||||
<span class="fa fa-pencil" aria-hidden="true"></span>
|
||||
Edit this power feed
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if perms.dcim.delete_powerfeed %}
|
||||
<a href="{% url 'dcim:powerfeed_delete' pk=powerfeed.pk %}" class="btn btn-danger">
|
||||
<span class="fa fa-trash" aria-hidden="true"></span>
|
||||
Delete this power feed
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<h1>{% block title %}{{ powerfeed }}{% endblock %}</h1>
|
||||
{% include 'inc/created_updated.html' with obj=powerfeed %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-7">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Power Feed</strong>
|
||||
</div>
|
||||
<table class="table table-hover panel-body attr-table">
|
||||
<tr>
|
||||
<td>Power Panel</td>
|
||||
<td>
|
||||
<a href="{{ powerfeed.powerpanel.get_absolute_url }}">{{ powerfeed.powerpanel }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Rack</td>
|
||||
<td>
|
||||
{% if powerfeed.rack %}
|
||||
<a href="{{ powerfeed.rack.get_absolute_url }}">{{ powerfeed.rack }}</a>
|
||||
{% else %}
|
||||
<span class="text-muted">None</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Type</td>
|
||||
<td>
|
||||
<span class="label label-{{ powerfeed.get_type_class }}">{{ powerfeed.get_type_display }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Status</td>
|
||||
<td>
|
||||
<span class="label label-{{ powerfeed.get_status_class }}">{{ powerfeed.get_status_display }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Supply</td>
|
||||
<td>{{ powerfeed.get_supply_display }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Voltage</td>
|
||||
<td>{{ powerfeed.voltage }}V</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Amperage</td>
|
||||
<td>{{ powerfeed.amperage }}A</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Phase</td>
|
||||
<td>{{ powerfeed.get_phase_display }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Max Utilization</td>
|
||||
<td>{{ powerfeed.max_utilization }}%</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
45
netbox/templates/dcim/powerfeed_edit.html
Normal file
45
netbox/templates/dcim/powerfeed_edit.html
Normal file
@ -0,0 +1,45 @@
|
||||
{% extends 'utilities/obj_edit.html' %}
|
||||
{% load form_helpers %}
|
||||
|
||||
{% block form %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><strong>Power Feed</strong></div>
|
||||
<div class="panel-body">
|
||||
{% render_field form.powerpanel %}
|
||||
{% 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.supply %}
|
||||
{% render_field form.voltage %}
|
||||
{% render_field form.amperage %}
|
||||
{% render_field form.phase %}
|
||||
{% render_field form.max_utilization %}
|
||||
</div>
|
||||
</div>
|
||||
{% if form.custom_fields %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><strong>Custom Fields</strong></div>
|
||||
<div class="panel-body">
|
||||
{% render_custom_fields form %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><strong>Tags</strong></div>
|
||||
<div class="panel-body">
|
||||
{% render_field form.tags %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><strong>Comments</strong></div>
|
||||
<div class="panel-body">
|
||||
{% render_field form.comments %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
22
netbox/templates/dcim/powerfeed_list.html
Normal file
22
netbox/templates/dcim/powerfeed_list.html
Normal file
@ -0,0 +1,22 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load buttons %}
|
||||
|
||||
{% block content %}
|
||||
<div class="pull-right noprint">
|
||||
{% if perms.dcim.add_powerfeed %}
|
||||
{% add_button 'dcim:powerfeed_add' %}
|
||||
{% import_button 'dcim:powerfeed_import' %}
|
||||
{% endif %}
|
||||
{% export_button content_type %}
|
||||
</div>
|
||||
<h1>{% block title %}Power Feeds{% endblock %}</h1>
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:powerfeed_bulk_edit' bulk_delete_url='dcim:powerfeed_bulk_delete' %}
|
||||
</div>
|
||||
<div class="col-md-3 noprint">
|
||||
{% include 'inc/search_panel.html' %}
|
||||
{% include 'inc/tags_panel.html' %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
18
netbox/templates/dcim/powerpanel_list.html
Normal file
18
netbox/templates/dcim/powerpanel_list.html
Normal file
@ -0,0 +1,18 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load buttons %}
|
||||
|
||||
{% block content %}
|
||||
<div class="pull-right noprint">
|
||||
{% if perms.dcim.add_powerpanel %}
|
||||
{% add_button 'dcim:powerpanel_add' %}
|
||||
{% import_button 'dcim:powerpanel_import' %}
|
||||
{% endif %}
|
||||
{% export_button content_type %}
|
||||
</div>
|
||||
<h1>{% block title %}Power Panels{% endblock %}</h1>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
{% include 'utilities/obj_table.html' with bulk_delete_url='dcim:powerpanel_bulk_delete' %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
@ -368,6 +368,29 @@
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="dropdown{% if request.path|contains:'/dcim/power' %} active{% endif %}">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Power <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
{% if perms.dcim.add_powerfeed %}
|
||||
<div class="buttons pull-right">
|
||||
<a href="{% url 'dcim:powerfeed_add' %}" class="btn btn-xs btn-success" title="Add"><i class="fa fa-plus"></i></a>
|
||||
<a href="{% url 'dcim:powerfeed_import' %}" class="btn btn-xs btn-info" title="Import"><i class="fa fa-download"></i></a>
|
||||
</div>
|
||||
{% endif %}
|
||||
<a href="{% url 'dcim:powerfeed_list' %}">Power Feeds</a>
|
||||
</li>
|
||||
<li>
|
||||
{% if perms.dcim.add_powerpanel %}
|
||||
<div class="buttons pull-right">
|
||||
<a href="{% url 'dcim:powerpanel_add' %}" class="btn btn-xs btn-success" title="Add"><i class="fa fa-plus"></i></a>
|
||||
<a href="{% url 'dcim:powerpanel_import' %}" class="btn btn-xs btn-info" title="Import"><i class="fa fa-download"></i></a>
|
||||
</div>
|
||||
{% endif %}
|
||||
<a href="{% url 'dcim:powerpanel_list' %}">Power Panels</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
{% if request.user.is_authenticated %}
|
||||
<li class="dropdown{% if request.path|contains:'/secrets/' %} active{% endif %}">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Secrets <span class="caret"></span></a>
|
||||
|
Loading…
Reference in New Issue
Block a user