Przeglądaj źródła

Change DeviceType export from CSV to YAML

Jeremy Stretch 6 lat temu
rodzic
commit
0dad9f8901
3 zmienionych plików z 114 dodań i 25 usunięć
  1. 91 16
      netbox/dcim/models/__init__.py
  2. 6 3
      netbox/dcim/views.py
  3. 17 6
      netbox/utilities/views.py

+ 91 - 16
netbox/dcim/models/__init__.py

@@ -2,6 +2,7 @@ from collections import OrderedDict
 from itertools import count, groupby
 
 import svgwrite
+import yaml
 from django.conf import settings
 from django.contrib.auth.models import User
 from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
@@ -25,15 +26,14 @@ from utilities.fields import ColorField
 from utilities.managers import NaturalOrderingManager
 from utilities.models import ChangeLoggedModel
 from utilities.utils import foreground_color, to_meters
-from .device_components import (
-    CableTermination, ConsolePort, ConsoleServerPort, DeviceBay, FrontPort, Interface, InventoryItem, PowerOutlet,
-    PowerPort, RearPort,
-)
 from .device_component_templates import (
     ConsolePortTemplate, ConsoleServerPortTemplate, DeviceBayTemplate, FrontPortTemplate, InterfaceTemplate,
     PowerOutletTemplate, PowerPortTemplate, RearPortTemplate,
 )
-
+from .device_components import (
+    CableTermination, ConsolePort, ConsoleServerPort, DeviceBay, FrontPort, Interface, InventoryItem, PowerOutlet,
+    PowerPort, RearPort,
+)
 
 __all__ = (
     'Cable',
@@ -1003,17 +1003,92 @@ class DeviceType(ChangeLoggedModel, CustomFieldModel):
     def get_absolute_url(self):
         return reverse('dcim:devicetype', args=[self.pk])
 
-    def to_csv(self):
-        return (
-            self.manufacturer.name,
-            self.model,
-            self.slug,
-            self.part_number,
-            self.u_height,
-            self.is_full_depth,
-            self.get_subdevice_role_display(),
-            self.comments,
-        )
+    def to_yaml(self):
+        data = OrderedDict((
+            ('manufacturer', self.manufacturer.name),
+            ('model', self.model),
+            ('slug', self.slug),
+            ('part_number', self.part_number),
+            ('u_height', self.u_height),
+            ('is_full_depth', self.is_full_depth),
+            ('subdevice_role', self.subdevice_role),
+            ('comments', self.comments),
+        ))
+
+        # Component templates
+        if self.consoleport_templates.exists():
+            data['console-ports'] = [
+                {
+                    'name': c.name,
+                    'type': c.type,
+                }
+                for c in self.consoleport_templates.all()
+            ]
+        if self.consoleserverport_templates.exists():
+            data['console-server-ports'] = [
+                {
+                    'name': c.name,
+                    'type': c.type,
+                }
+                for c in self.consoleserverport_templates.all()
+            ]
+        if self.powerport_templates.exists():
+            data['power-ports'] = [
+                {
+                    'name': c.name,
+                    'type': c.type,
+                    'maximum_draw': c.maximum_draw,
+                    'allocated_draw': c.allocated_draw,
+                }
+                for c in self.powerport_templates.all()
+            ]
+        if self.poweroutlet_templates.exists():
+            data['power-outlets'] = [
+                {
+                    'name': c.name,
+                    'type': c.type,
+                    'power_port': c.power_port.name if c.power_port else None,
+                    'feed_leg': c.feed_leg,
+                }
+                for c in self.poweroutlet_templates.all()
+            ]
+        if self.interface_templates.exists():
+            data['interfaces'] = [
+                {
+                    'name': c.name,
+                    'type': c.type,
+                    'mgmt_only': c.mgmt_only,
+                }
+                for c in self.interface_templates.all()
+            ]
+        if self.frontport_templates.exists():
+            data['front-ports'] = [
+                {
+                    'name': c.name,
+                    'type': c.type,
+                    'rear_port': c.rear_port.name,
+                    'rear_port_position': c.rear_port_position,
+                }
+                for c in self.frontport_templates.all()
+            ]
+        if self.rearport_templates.exists():
+            data['rear-ports'] = [
+                {
+                    'name': c.name,
+                    'type': c.type,
+                    'positions': c.positions,
+                }
+                for c in self.rearport_templates.all()
+            ]
+        if self.device_bay_templates.exists():
+            data['device-bays'] = [
+                {
+                    'name': c.name,
+                }
+                for c in self.device_bay_templates.all()
+            ]
+
+        return yaml.dump(dict(data), sort_keys=False)
 
     def clean(self):
 

+ 6 - 3
netbox/dcim/views.py

@@ -2056,7 +2056,8 @@ class ConsoleConnectionsListView(PermissionRequiredMixin, ObjectListView):
                 obj.get_connection_status_display(),
             ])
             csv_data.append(csv)
-        return csv_data
+
+        return '\n'.join(csv_data)
 
 
 class PowerConnectionsListView(PermissionRequiredMixin, ObjectListView):
@@ -2087,7 +2088,8 @@ class PowerConnectionsListView(PermissionRequiredMixin, ObjectListView):
                 obj.get_connection_status_display(),
             ])
             csv_data.append(csv)
-        return csv_data
+
+        return '\n'.join(csv_data)
 
 
 class InterfaceConnectionsListView(PermissionRequiredMixin, ObjectListView):
@@ -2126,7 +2128,8 @@ class InterfaceConnectionsListView(PermissionRequiredMixin, ObjectListView):
                 obj.get_connection_status_display(),
             ])
             csv_data.append(csv)
-        return csv_data
+
+        return '\n'.join(csv_data)
 
 
 #

+ 17 - 6
netbox/utilities/views.py

@@ -75,6 +75,14 @@ class ObjectListView(View):
     table = None
     template_name = None
 
+    def queryset_to_yaml(self):
+        """
+        Export the queryset of objects as concatenated YAML documents.
+        """
+        yaml_data = [obj.to_yaml() for obj in self.queryset]
+
+        return '---\n'.join(yaml_data)
+
     def queryset_to_csv(self):
         """
         Export the queryset of objects as comma-separated value (CSV), using the model's to_csv() method.
@@ -90,7 +98,7 @@ class ObjectListView(View):
             data = csv_format(obj.to_csv())
             csv_data.append(data)
 
-        return csv_data
+        return '\n'.join(csv_data)
 
     def get(self, request):
 
@@ -121,13 +129,16 @@ class ObjectListView(View):
                     )
                 )
 
+        # Check for YAML export support
+        elif 'export' in request.GET and hasattr(model, 'to_yaml'):
+            response = HttpResponse(self.queryset_to_yaml(), content_type='text/yaml')
+            filename = 'netbox_{}.yaml'.format(self.queryset.model._meta.verbose_name_plural)
+            response['Content-Disposition'] = 'attachment; filename="{}"'.format(filename)
+            return response
+
         # Fall back to built-in CSV formatting if export requested but no template specified
         elif 'export' in request.GET and hasattr(model, 'to_csv'):
-            data = self.queryset_to_csv()
-            response = HttpResponse(
-                '\n'.join(data),
-                content_type='text/csv'
-            )
+            response = HttpResponse(self.queryset_to_csv(), content_type='text/csv')
             filename = 'netbox_{}.csv'.format(self.queryset.model._meta.verbose_name_plural)
             response['Content-Disposition'] = 'attachment; filename="{}"'.format(filename)
             return response