Fix elevation overlap, add device images, rack merge, and size override
- Fix elevation rendering bug: device blocks were drawn MARGIN px further right than the rack frame/unit slots, causing them to overlap and extend past the right border. All three now share one RACK_X origin. - Show the device type's front/rear image inside each elevation block (clipped to the block, translucent label bar on top for legibility) when one is set on the device type; falls back to a plain colour block with text otherwise. - Add "Merge" action to combine two concepts into one taller one (e.g. two half-height racks stacked into a full-height rack): the top concept's devices are shifted up by the bottom concept's height, neither source is modified. Available from the plugin menu, from a concept's detail page, and via POST /concepts/merge/. - Copying a real rack into a concept can now target a different width, height and/or starting unit than the source rack; devices that no longer fit at the chosen size are skipped and reported as warnings instead of aborting the copy. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ from dcim.api.serializers import (
|
||||
DeviceRoleSerializer, DeviceTypeSerializer, LocationSerializer, PlatformSerializer, RackRoleSerializer,
|
||||
RackSerializer, RackTypeSerializer, SiteSerializer,
|
||||
)
|
||||
from dcim.choices import RackWidthChoices
|
||||
from dcim.models import Location, Rack, Site
|
||||
from netbox.api.serializers import NetBoxModelSerializer
|
||||
from tenancy.api.serializers import TenantGroupSerializer, TenantSerializer
|
||||
@@ -93,6 +94,9 @@ class RackCopySerializer(serializers.Serializer):
|
||||
description = serializers.CharField(max_length=200, required=False, allow_blank=True, default='')
|
||||
include_devices = serializers.BooleanField(default=True)
|
||||
include_device_names = serializers.BooleanField(default=True)
|
||||
override_width = serializers.ChoiceField(choices=RackWidthChoices, required=False, allow_null=True, default=None)
|
||||
override_u_height = serializers.IntegerField(min_value=1, max_value=100, required=False, allow_null=True, default=None)
|
||||
override_starting_unit = serializers.IntegerField(min_value=1, required=False, allow_null=True, default=None)
|
||||
|
||||
|
||||
class RackConceptCloneSerializer(serializers.Serializer):
|
||||
@@ -103,6 +107,24 @@ class RackConceptCloneSerializer(serializers.Serializer):
|
||||
include_devices = serializers.BooleanField(default=True)
|
||||
|
||||
|
||||
class MergeConceptsSerializer(serializers.Serializer):
|
||||
"""POST body for stacking two concepts into one taller one."""
|
||||
concept_bottom = serializers.PrimaryKeyRelatedField(queryset=RackConcept.objects.all())
|
||||
concept_top = serializers.PrimaryKeyRelatedField(queryset=RackConcept.objects.all())
|
||||
name = serializers.CharField(max_length=100)
|
||||
status = serializers.ChoiceField(choices=ConceptStatusChoices, default=ConceptStatusChoices.STATUS_DRAFT)
|
||||
tenant = serializers.PrimaryKeyRelatedField(queryset=Tenant.objects.all(), required=False, allow_null=True)
|
||||
width = serializers.ChoiceField(choices=RackWidthChoices, required=False, allow_null=True, default=None)
|
||||
u_height = serializers.IntegerField(min_value=1, max_value=100, required=False, allow_null=True, default=None)
|
||||
starting_unit = serializers.IntegerField(min_value=1, default=1)
|
||||
description = serializers.CharField(max_length=200, required=False, allow_blank=True, default='')
|
||||
|
||||
def validate(self, attrs):
|
||||
if attrs['concept_bottom'].pk == attrs['concept_top'].pk:
|
||||
raise serializers.ValidationError('concept_bottom and concept_top must be different concepts.')
|
||||
return attrs
|
||||
|
||||
|
||||
class ConceptDeploySerializer(serializers.Serializer):
|
||||
"""POST body for materialising a concept."""
|
||||
name_pattern = serializers.CharField(max_length=100)
|
||||
|
||||
Reference in New Issue
Block a user