From 45aa9de22ae6158c6ce2de16b1756ddf5e3f14ec Mon Sep 17 00:00:00 2001 From: Sander Steffann Date: Sun, 20 Oct 2019 21:30:01 +0200 Subject: [PATCH] Let console connections use the same flexible framework --- netbox/dcim/api/views.py | 2 +- .../0076_add_generic_connected_endpoint.py | 41 +++++++++++++++++++ .../0077_migrate_connected_endpoint.py | 6 ++- ..._connected_interface_circuittermination.py | 4 ++ netbox/dcim/models.py | 37 ++++++++++++++--- netbox/dcim/views.py | 4 +- netbox/netbox/views.py | 7 ++-- 7 files changed, 88 insertions(+), 13 deletions(-) diff --git a/netbox/dcim/api/views.py b/netbox/dcim/api/views.py index b363ab51f..fd3224c15 100644 --- a/netbox/dcim/api/views.py +++ b/netbox/dcim/api/views.py @@ -513,7 +513,7 @@ class ConsoleConnectionViewSet(ListModelMixin, GenericViewSet): queryset = ConsolePort.objects.prefetch_related( 'device', 'connected_endpoint__device' ).filter( - connected_endpoint__isnull=False + connected_endpoint_type=ContentType.objects.get_for_model(ConsoleServerPort) ) serializer_class = serializers.ConsolePortSerializer filterset_class = filters.ConsoleConnectionFilter diff --git a/netbox/dcim/migrations/0076_add_generic_connected_endpoint.py b/netbox/dcim/migrations/0076_add_generic_connected_endpoint.py index 3f1d12e68..88b911242 100644 --- a/netbox/dcim/migrations/0076_add_generic_connected_endpoint.py +++ b/netbox/dcim/migrations/0076_add_generic_connected_endpoint.py @@ -30,4 +30,45 @@ class Migration(migrations.Migration): name='_trace', field=django.contrib.postgres.fields.jsonb.JSONField(default=list), ), + migrations.RenameField( + model_name='consoleport', + old_name='connected_endpoint', + new_name='old_connected_endpoint', + ), + migrations.AddField( + model_name='consoleport', + name='connected_endpoint_id', + field=models.PositiveIntegerField(blank=True, null=True), + ), + migrations.AddField( + model_name='consoleport', + name='connected_endpoint_type', + field=models.ForeignKey(blank=True, limit_choices_to={ + 'model__in': ['consoleport', 'consoleserverport', 'interface', 'poweroutlet', 'powerport', 'frontport', + 'rearport', 'circuittermination'] + }, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.ContentType'), + ), + migrations.AddField( + model_name='consoleport', + name='_trace', + field=django.contrib.postgres.fields.jsonb.JSONField(default=list), + ), + migrations.AddField( + model_name='consoleserverport', + name='connected_endpoint_id', + field=models.PositiveIntegerField(blank=True, null=True), + ), + migrations.AddField( + model_name='consoleserverport', + name='connected_endpoint_type', + field=models.ForeignKey(blank=True, limit_choices_to={ + 'model__in': ['consoleport', 'consoleserverport', 'interface', 'poweroutlet', 'powerport', 'frontport', + 'rearport', 'circuittermination'] + }, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.ContentType'), + ), + migrations.AddField( + model_name='consoleserverport', + name='_trace', + field=django.contrib.postgres.fields.jsonb.JSONField(default=list), + ), ] diff --git a/netbox/dcim/migrations/0077_migrate_connected_endpoint.py b/netbox/dcim/migrations/0077_migrate_connected_endpoint.py index 9a2a60870..8f3539bad 100644 --- a/netbox/dcim/migrations/0077_migrate_connected_endpoint.py +++ b/netbox/dcim/migrations/0077_migrate_connected_endpoint.py @@ -91,13 +91,17 @@ def to_generic_connected_endpoint(apps, schema_editor): print("\nReconstructing all endpoints...", end='') interface_model = apps.get_model('dcim', 'Interface') + consoleport_model = apps.get_model('dcim', 'ConsolePort') + consoleserverport_model = apps.get_model('dcim', 'ConsoleServerPort') circuittermination_model = apps.get_model('circuits', 'CircuitTermination') contenttype_model = apps.get_model('contenttypes', 'ContentType') db_alias = schema_editor.connection.alias interface_endpoints = interface_model.objects.using(db_alias).all() circuittermination_endpoints = circuittermination_model.objects.using(db_alias).all() - for endpoint in chain(interface_endpoints, circuittermination_endpoints): + consoleport_endpoints = consoleport_model.objects.using(db_alias).all() + consoleserverport_endpoints = consoleserverport_model.objects.using(db_alias).all() + for endpoint in chain(interface_endpoints, circuittermination_endpoints, consoleport_endpoints, consoleserverport_endpoints): path = migration_trace(apps, endpoint) # The trace returns left and right, we just want a single list diff --git a/netbox/dcim/migrations/0078_remove_connected_interface_circuittermination.py b/netbox/dcim/migrations/0078_remove_connected_interface_circuittermination.py index 38ff1b013..94c9e05a7 100644 --- a/netbox/dcim/migrations/0078_remove_connected_interface_circuittermination.py +++ b/netbox/dcim/migrations/0078_remove_connected_interface_circuittermination.py @@ -18,4 +18,8 @@ class Migration(migrations.Migration): model_name='interface', name='_connected_interface', ), + migrations.RemoveField( + model_name='consoleport', + name='old_connected_endpoint', + ), ] diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index 02039f805..754fd84b7 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -1888,7 +1888,7 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel): # Console ports # -class ConsolePort(CableTermination, ComponentModel): +class ConsolePort(CableTermination, ComponentModel, CachedTraceModel): """ A physical console port within a Device. ConsolePorts connect to ConsoleServerPorts. """ @@ -1900,13 +1900,22 @@ class ConsolePort(CableTermination, ComponentModel): name = models.CharField( max_length=50 ) - connected_endpoint = models.OneToOneField( - to='dcim.ConsoleServerPort', - on_delete=models.SET_NULL, - related_name='connected_endpoint', + connected_endpoint_type = models.ForeignKey( + to=ContentType, + limit_choices_to={'model__in': CABLE_TERMINATION_TYPES}, + on_delete=models.PROTECT, + related_name='+', blank=True, null=True ) + connected_endpoint_id = models.PositiveIntegerField( + blank=True, + null=True + ) + connected_endpoint = GenericForeignKey( + ct_field='connected_endpoint_type', + fk_field='connected_endpoint_id' + ) connection_status = models.NullBooleanField( choices=CONNECTION_STATUS_CHOICES, blank=True @@ -1939,7 +1948,7 @@ class ConsolePort(CableTermination, ComponentModel): # Console server ports # -class ConsoleServerPort(CableTermination, ComponentModel): +class ConsoleServerPort(CableTermination, ComponentModel, CachedTraceModel): """ A physical port within a Device (typically a designated console server) which provides access to ConsolePorts. """ @@ -1951,6 +1960,22 @@ class ConsoleServerPort(CableTermination, ComponentModel): name = models.CharField( max_length=50 ) + connected_endpoint_type = models.ForeignKey( + to=ContentType, + limit_choices_to={'model__in': CABLE_TERMINATION_TYPES}, + on_delete=models.PROTECT, + related_name='+', + blank=True, + null=True + ) + connected_endpoint_id = models.PositiveIntegerField( + blank=True, + null=True + ) + connected_endpoint = GenericForeignKey( + ct_field='connected_endpoint_type', + fk_field='connected_endpoint_id' + ) connection_status = models.NullBooleanField( choices=CONNECTION_STATUS_CHOICES, blank=True diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index cbc0c3368..f91ff8bee 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -1879,9 +1879,9 @@ class ConsoleConnectionsListView(PermissionRequiredMixin, ObjectListView): queryset = ConsolePort.objects.prefetch_related( 'device', 'connected_endpoint__device' ).filter( - connected_endpoint__isnull=False + connected_endpoint_type=ContentType.objects.get_for_model(ConsoleServerPort) ).order_by( - 'cable', 'connected_endpoint__device__name', 'connected_endpoint__name' + 'cable', # 'connected_endpoint__device__name', 'connected_endpoint__name' ) filter = filters.ConsoleConnectionFilter filter_form = forms.ConsoleConnectionFilterForm diff --git a/netbox/netbox/views.py b/netbox/netbox/views.py index e99c0f70c..b0e92c8fa 100644 --- a/netbox/netbox/views.py +++ b/netbox/netbox/views.py @@ -16,8 +16,9 @@ from dcim.filters import ( VirtualChassisFilter, ) from dcim.models import ( - Cable, ConsolePort, Device, DeviceType, Interface, PowerPanel, PowerFeed, PowerPort, Rack, RackGroup, Site, VirtualChassis -) + Cable, ConsolePort, Device, DeviceType, Interface, PowerPanel, PowerFeed, PowerPort, Rack, RackGroup, Site, + VirtualChassis, + ConsoleServerPort) from dcim.tables import ( CableTable, DeviceDetailTable, DeviceTypeTable, PowerFeedTable, RackTable, RackGroupTable, SiteTable, VirtualChassisTable, @@ -195,7 +196,7 @@ class HomeView(View): def get(self, request): connected_consoleports = ConsolePort.objects.filter( - connected_endpoint__isnull=False + connected_endpoint_type=ContentType.objects.get_for_model(ConsoleServerPort), ) connected_powerports = PowerPort.objects.filter( _connected_poweroutlet__isnull=False