jeremystretch 3 лет назад
Родитель
Сommit
6f8a7fdbe3

+ 1 - 1
netbox/dcim/api/urls.py

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

+ 2 - 2
netbox/dcim/choices.py

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

+ 1 - 1
netbox/dcim/filtersets.py

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

+ 1 - 2
netbox/dcim/forms/filtersets.py

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

+ 6 - 8
netbox/dcim/forms/model_forms.py

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

+ 4 - 1
netbox/dcim/models/devices.py

@@ -1113,7 +1113,7 @@ class VirtualDeviceContext(PrimaryModel):
         choices=VirtualDeviceContextStatusChoices,
     )
     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,
         null=True,
     )
@@ -1163,6 +1163,9 @@ class VirtualDeviceContext(PrimaryModel):
     def get_absolute_url(self):
         return reverse('dcim:virtualdevicecontext', kwargs={'pk': self.pk})
 
+    def get_status_color(self):
+        return VirtualDeviceContextStatusChoices.colors.get(self.status)
+
     @property
     def primary_ip(self):
         if ConfigItem('PREFER_IPV4')() and self.primary_ip4:

+ 6 - 6
netbox/dcim/urls.py

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

+ 4 - 1
netbox/dcim/views.py

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

+ 48 - 20
netbox/templates/dcim/device.html

@@ -155,6 +155,38 @@
             {% include 'inc/panels/custom_fields.html' %}
             {% include 'inc/panels/tags.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 %}
         </div>
         <div class="col col-12 col-xl-6">
@@ -264,34 +296,30 @@
                 </div>
             {% endif %}
             <div class="card">
-                <h5 class="card-header">
-                    Services
-                </h5>
-                <div class="card-body">
+              <h5 class="card-header">Services</h5>
+              <div class="card-body">
                 {% if services %}
-                    <table class="table table-hover">
-                        {% for service in services %}
-                            {% include 'ipam/inc/service.html' %}
-                        {% endfor %}
-                    </table>
+                  <table class="table table-hover">
+                    {% for service in services %}
+                      {% include 'ipam/inc/service.html' %}
+                    {% endfor %}
+                  </table>
                 {% else %}
-                    <div class="text-muted">
-                        None
-                    </div>
+                  <div class="text-muted">None</div>
                 {% endif %}
-                </div>
-                {% if perms.ipam.add_service %}
+              </div>
+              {% if perms.ipam.add_service %}
                 <div class="card-footer text-end noprint">
-                    <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
-                    </a>
+                  <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
+                  </a>
                 </div>
-                {% endif %}
+              {% endif %}
             </div>
             {% include 'inc/panels/contacts.html' %}
             {% include 'inc/panels/image_attachments.html' %}
             {% 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 style="margin-left: 30px">
                     <h4>Front</h4>
@@ -304,7 +332,7 @@
                     {% include 'dcim/inc/rack_elevation.html' with object=object.rack face='rear' extra_params=svg_extra %}
                   </div>
                 </div>
-            </div>
+              </div>
             {% endif %}
             {% plugin_right_page object %}
         </div>

+ 9 - 1
netbox/templates/dcim/virtualdevicecontext.html

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