1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00
This commit is contained in:
jeremystretch
2022-11-15 11:24:36 -05:00
parent 640fd8045d
commit 6f8a7fdbe3
10 changed files with 82 additions and 43 deletions

View File

@ -37,7 +37,7 @@ router.register('inventory-item-templates', views.InventoryItemTemplateViewSet)
router.register('device-roles', views.DeviceRoleViewSet) router.register('device-roles', views.DeviceRoleViewSet)
router.register('platforms', views.PlatformViewSet) router.register('platforms', views.PlatformViewSet)
router.register('devices', views.DeviceViewSet) router.register('devices', views.DeviceViewSet)
router.register('vdcs', views.VirtualDeviceContextViewSet) router.register('virtual-device-contexts', views.VirtualDeviceContextViewSet)
router.register('modules', views.ModuleViewSet) router.register('modules', views.ModuleViewSet)
# Device components # Device components

View File

@ -1407,12 +1407,12 @@ class PowerFeedPhaseChoices(ChoiceSet):
class VirtualDeviceContextStatusChoices(ChoiceSet): class VirtualDeviceContextStatusChoices(ChoiceSet):
key = 'VirtualDeviceContext.status' key = 'VirtualDeviceContext.status'
STATUS_PLANNED = 'planned'
STATUS_ACTIVE = 'active' STATUS_ACTIVE = 'active'
STATUS_PLANNED = 'planned'
STATUS_OFFLINE = 'offline' STATUS_OFFLINE = 'offline'
CHOICES = [ CHOICES = [
(STATUS_PLANNED, 'Planned', 'cyan'),
(STATUS_ACTIVE, 'Active', 'green'), (STATUS_ACTIVE, 'Active', 'green'),
(STATUS_PLANNED, 'Planned', 'cyan'),
(STATUS_OFFLINE, 'Offline', 'red'), (STATUS_OFFLINE, 'Offline', 'red'),
] ]

View File

@ -1032,7 +1032,7 @@ class VirtualDeviceContextFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
class Meta: class Meta:
model = VirtualDeviceContext model = VirtualDeviceContext
fields = ['id', 'device', 'name', ] fields = ['id', 'device', 'name']
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():

View File

@ -736,9 +736,8 @@ class VirtualDeviceContextFilterForm(
model = VirtualDeviceContext model = VirtualDeviceContext
fieldsets = ( fieldsets = (
(None, ('q', 'filter_id', 'tag')), (None, ('q', 'filter_id', 'tag')),
('Hardware', ('device', 'status', )), ('Attributes', ('device', 'status', 'has_primary_ip')),
('Tenant', ('tenant_group_id', 'tenant_id')), ('Tenant', ('tenant_group_id', 'tenant_id')),
('Miscellaneous', ('has_primary_ip',))
) )
device = DynamicModelMultipleChoiceField( device = DynamicModelMultipleChoiceField(
queryset=Device.objects.all(), queryset=Device.objects.all(),

View File

@ -1698,21 +1698,19 @@ class VirtualDeviceContextForm(TenancyForm, NetBoxModelForm):
) )
fieldsets = ( fieldsets = (
('Device', ('region', 'site_group', 'site', 'location', 'rack', 'device')), ('Assigned Device', ('region', 'site_group', 'site', 'location', 'rack', 'device')),
('Virtual Device Context', ('name', 'status', 'identifier', 'primary_ip4', 'primary_ip6', 'tenant_group', ('Virtual Device Context', ('name', 'status', 'identifier', 'primary_ip4', 'primary_ip6', 'tags')),
'tenant')), ('Tenancy', ('tenant_group', 'tenant'))
(None, ('tags', ))
) )
class Meta: class Meta:
model = VirtualDeviceContext model = VirtualDeviceContext
fields = [ fields = [
'region', 'site_group', 'site', 'location', 'rack', 'region', 'site_group', 'site', 'location', 'rack', 'device', 'name', 'status', 'identifier',
'device', 'name', 'status', 'identifier', 'primary_ip4', 'primary_ip6', 'tenant_group', 'tenant', 'primary_ip4', 'primary_ip6', 'tenant_group', 'tenant', 'comments', 'tags'
'comments', 'tags'
] ]
help_texts = {}
widgets = { widgets = {
'status': StaticSelect(),
'primary_ip4': StaticSelect(), 'primary_ip4': StaticSelect(),
'primary_ip6': StaticSelect(), 'primary_ip6': StaticSelect(),
} }

View File

@ -1113,7 +1113,7 @@ class VirtualDeviceContext(PrimaryModel):
choices=VirtualDeviceContextStatusChoices, choices=VirtualDeviceContextStatusChoices,
) )
identifier = models.PositiveSmallIntegerField( identifier = models.PositiveSmallIntegerField(
help_text='Unique identifier provided by the platform being virtualized (Example: Nexus VDC Identifier)', help_text='Numeric identifier unique to the parent device',
blank=True, blank=True,
null=True, null=True,
) )
@ -1163,6 +1163,9 @@ class VirtualDeviceContext(PrimaryModel):
def get_absolute_url(self): def get_absolute_url(self):
return reverse('dcim:virtualdevicecontext', kwargs={'pk': self.pk}) return reverse('dcim:virtualdevicecontext', kwargs={'pk': self.pk})
def get_status_color(self):
return VirtualDeviceContextStatusChoices.colors.get(self.status)
@property @property
def primary_ip(self): def primary_ip(self):
if ConfigItem('PREFER_IPV4')() and self.primary_ip4: if ConfigItem('PREFER_IPV4')() and self.primary_ip4:

View File

@ -184,12 +184,12 @@ urlpatterns = [
path('devices/<int:pk>/', include(get_model_urls('dcim', 'device'))), path('devices/<int:pk>/', include(get_model_urls('dcim', 'device'))),
# Virtual Device Context # Virtual Device Context
path('vdcs/', views.VirtualDeviceContextListView.as_view(), name='virtualdevicecontext_list'), path('virtual-device-contexts/', views.VirtualDeviceContextListView.as_view(), name='virtualdevicecontext_list'),
path('vdcs/add/', views.VirtualDeviceContextEditView.as_view(), name='virtualdevicecontext_add'), path('virtual-device-contexts/add/', views.VirtualDeviceContextEditView.as_view(), name='virtualdevicecontext_add'),
path('vdcs/import/', views.VirtualDeviceContextBulkImportView.as_view(), name='virtualdevicecontext_import'), path('virtual-device-contexts/import/', views.VirtualDeviceContextBulkImportView.as_view(), name='virtualdevicecontext_import'),
path('vdcs/edit/', views.VirtualDeviceContextBulkEditView.as_view(), name='virtualdevicecontext_bulk_edit'), path('virtual-device-contexts/edit/', views.VirtualDeviceContextBulkEditView.as_view(), name='virtualdevicecontext_bulk_edit'),
path('vdcs/delete/', views.VirtualDeviceContextBulkDeleteView.as_view(), name='virtualdevicecontext_bulk_delete'), path('virtual-device-contexts/delete/', views.VirtualDeviceContextBulkDeleteView.as_view(), name='virtualdevicecontext_bulk_delete'),
path('vdcs/<int:pk>/', include(get_model_urls('dcim', 'virtualdevicecontext'))), path('virtual-device-contexts/<int:pk>/', include(get_model_urls('dcim', 'virtualdevicecontext'))),
# Modules # Modules
path('modules/', views.ModuleListView.as_view(), name='module_list'), path('modules/', views.ModuleListView.as_view(), name='module_list'),

View File

@ -1837,11 +1837,14 @@ class DeviceView(generic.ObjectView):
else: else:
vc_members = [] vc_members = []
# Services
services = Service.objects.restrict(request.user, 'view').filter(device=instance) services = Service.objects.restrict(request.user, 'view').filter(device=instance)
vdcs = VirtualDeviceContext.objects.restrict(request.user, 'view').filter(device=instance).prefetch_related(
'tenant'
)
return { return {
'services': services, 'services': services,
'vdcs': vdcs,
'vc_members': vc_members, 'vc_members': vc_members,
'svg_extra': f'highlight=id:{instance.pk}' 'svg_extra': f'highlight=id:{instance.pk}'
} }

View File

@ -155,6 +155,38 @@
{% include 'inc/panels/custom_fields.html' %} {% include 'inc/panels/custom_fields.html' %}
{% include 'inc/panels/tags.html' %} {% include 'inc/panels/tags.html' %}
{% include 'inc/panels/comments.html' %} {% include 'inc/panels/comments.html' %}
<div class="card">
<h5 class="card-header">Virtual Device Contexts</h5>
<div class="card-body">
{% if vdcs %}
<table class="table table-hover">
<tr>
<th>Name</th>
<th>Status</th>
<th>Identifier</th>
<th>Tenant</th>
</tr>
{% for vdc in vdcs %}
<tr>
<td>{{ vdc|linkify }}</td>
<td>{% badge vdc.get_status_display bg_color=vdc.get_status_color %}</td>
<td>{{ vdc.identifier|placeholder }}</td>
<td>{{ vdc.tenant|linkify|placeholder }}</td>
</tr>
{% endfor %}
</table>
{% else %}
<div class="text-muted">None</div>
{% endif %}
</div>
{% if perms.dcim.add_virtualdevicecontext %}
<div class="card-footer text-end noprint">
<a href="{% url 'dcim:virtualdevicecontext_add' %}?device={{ object.pk }}" class="btn btn-sm btn-primary">
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> Create VDC
</a>
</div>
{% endif %}
</div>
{% plugin_left_page object %} {% plugin_left_page object %}
</div> </div>
<div class="col col-12 col-xl-6"> <div class="col col-12 col-xl-6">
@ -264,34 +296,30 @@
</div> </div>
{% endif %} {% endif %}
<div class="card"> <div class="card">
<h5 class="card-header"> <h5 class="card-header">Services</h5>
Services <div class="card-body">
</h5>
<div class="card-body">
{% if services %} {% if services %}
<table class="table table-hover"> <table class="table table-hover">
{% for service in services %} {% for service in services %}
{% include 'ipam/inc/service.html' %} {% include 'ipam/inc/service.html' %}
{% endfor %} {% endfor %}
</table> </table>
{% else %} {% else %}
<div class="text-muted"> <div class="text-muted">None</div>
None
</div>
{% endif %} {% endif %}
</div> </div>
{% if perms.ipam.add_service %} {% if perms.ipam.add_service %}
<div class="card-footer text-end noprint"> <div class="card-footer text-end noprint">
<a href="{% url 'ipam:service_add' %}?device={{ object.pk }}" class="btn btn-sm btn-primary"> <a href="{% url 'ipam:service_add' %}?device={{ object.pk }}" class="btn btn-sm btn-primary">
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> Assign Service <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Assign Service
</a> </a>
</div> </div>
{% endif %} {% endif %}
</div> </div>
{% include 'inc/panels/contacts.html' %} {% include 'inc/panels/contacts.html' %}
{% include 'inc/panels/image_attachments.html' %} {% include 'inc/panels/image_attachments.html' %}
{% if object.rack and object.position %} {% if object.rack and object.position %}
<div class="row" style="margin-bottom: 20px"> <div class="row" style="margin-bottom: 20px">
<div class="col col-md-6 col-sm-6 col-xs-12 text-center"> <div class="col col-md-6 col-sm-6 col-xs-12 text-center">
<div style="margin-left: 30px"> <div style="margin-left: 30px">
<h4>Front</h4> <h4>Front</h4>
@ -304,7 +332,7 @@
{% include 'dcim/inc/rack_elevation.html' with object=object.rack face='rear' extra_params=svg_extra %} {% include 'dcim/inc/rack_elevation.html' with object=object.rack face='rear' extra_params=svg_extra %}
</div> </div>
</div> </div>
</div> </div>
{% endif %} {% endif %}
{% plugin_right_page object %} {% plugin_right_page object %}
</div> </div>

View File

@ -28,7 +28,6 @@
<th scope="row">Identifier</th> <th scope="row">Identifier</th>
<td>{{ object.identifier|placeholder }}</td> <td>{{ object.identifier|placeholder }}</td>
</tr> </tr>
</tr>
<tr> <tr>
<th scope="row">Primary IPv4</th> <th scope="row">Primary IPv4</th>
<td> <td>
@ -41,6 +40,15 @@
{{ object.primary_ip6|placeholder }} {{ object.primary_ip6|placeholder }}
</td> </td>
</tr> </tr>
<tr>
<th scope="row">Tenant</th>
<td>
{% if object.tenant.group %}
{{ object.tenant.group|linkify }} /
{% endif %}
{{ object.tenant|linkify|placeholder }}
</td>
</tr>
</table> </table>
</div> </div>
</div> </div>