Fixes #20713: Record pre-change snapshots on VC members being added/removed (#20714)
Some checks failed
CI / build (20.x, 3.10) (push) Waiting to run
CI / build (20.x, 3.11) (push) Waiting to run
CI / build (20.x, 3.12) (push) Waiting to run
CodeQL / Analyze (${{ matrix.language }}) (none, actions) (push) Has been cancelled
CodeQL / Analyze (${{ matrix.language }}) (none, javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (${{ matrix.language }}) (none, python) (push) Has been cancelled

This commit is contained in:
Jeremy Stretch 2025-10-30 08:50:10 -04:00 committed by GitHub
parent df688ce064
commit 0b61d69e05
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 12 additions and 18 deletions

View File

@ -453,6 +453,7 @@ class VirtualChassisCreateForm(NetBoxModelForm):
if instance.pk and self.cleaned_data['members']: if instance.pk and self.cleaned_data['members']:
initial_position = self.cleaned_data.get('initial_position', 1) initial_position = self.cleaned_data.get('initial_position', 1)
for i, member in enumerate(self.cleaned_data['members'], start=initial_position): for i, member in enumerate(self.cleaned_data['members'], start=initial_position):
member.snapshot()
member.virtual_chassis = instance member.virtual_chassis = instance
member.vc_position = i member.vc_position = i
member.save() member.save()

View File

@ -3779,6 +3779,7 @@ class VirtualChassisEditView(ObjectPermissionRequiredMixin, GetReturnURLMixin, V
def post(self, request, pk): def post(self, request, pk):
virtual_chassis = get_object_or_404(self.queryset, pk=pk) virtual_chassis = get_object_or_404(self.queryset, pk=pk)
virtual_chassis.snapshot()
VCMemberFormSet = modelformset_factory( VCMemberFormSet = modelformset_factory(
model=Device, model=Device,
form=forms.DeviceVCMembershipForm, form=forms.DeviceVCMembershipForm,
@ -3831,9 +3832,7 @@ class VirtualChassisAddMemberView(ObjectPermissionRequiredMixin, GetReturnURLMix
return 'dcim.change_virtualchassis' return 'dcim.change_virtualchassis'
def get(self, request, pk): def get(self, request, pk):
virtual_chassis = get_object_or_404(self.queryset, pk=pk) virtual_chassis = get_object_or_404(self.queryset, pk=pk)
initial_data = {k: request.GET[k] for k in request.GET} initial_data = {k: request.GET[k] for k in request.GET}
member_select_form = forms.VCMemberSelectForm(initial=initial_data) member_select_form = forms.VCMemberSelectForm(initial=initial_data)
membership_form = forms.DeviceVCMembershipForm(initial=initial_data) membership_form = forms.DeviceVCMembershipForm(initial=initial_data)
@ -3846,20 +3845,20 @@ class VirtualChassisAddMemberView(ObjectPermissionRequiredMixin, GetReturnURLMix
}) })
def post(self, request, pk): def post(self, request, pk):
virtual_chassis = get_object_or_404(self.queryset, pk=pk) virtual_chassis = get_object_or_404(self.queryset, pk=pk)
member_select_form = forms.VCMemberSelectForm(request.POST) member_select_form = forms.VCMemberSelectForm(request.POST)
if member_select_form.is_valid(): if member_select_form.is_valid():
device = member_select_form.cleaned_data['device'] device = member_select_form.cleaned_data['device']
device.snapshot()
device.virtual_chassis = virtual_chassis device.virtual_chassis = virtual_chassis
data = {k: request.POST[k] for k in ['vc_position', 'vc_priority']} data = {
'vc_position': request.POST['vc_position'],
'vc_priority': request.POST['vc_priority'],
}
membership_form = forms.DeviceVCMembershipForm(data=data, validate_vc_position=True, instance=device) membership_form = forms.DeviceVCMembershipForm(data=data, validate_vc_position=True, instance=device)
if membership_form.is_valid(): if membership_form.is_valid():
membership_form.save() membership_form.save()
messages.success(request, mark_safe( messages.success(request, mark_safe(
_('Added member <a href="{url}">{device}</a>').format( _('Added member <a href="{url}">{device}</a>').format(
@ -3869,11 +3868,9 @@ class VirtualChassisAddMemberView(ObjectPermissionRequiredMixin, GetReturnURLMix
if '_addanother' in request.POST and safe_for_redirect(request.get_full_path()): if '_addanother' in request.POST and safe_for_redirect(request.get_full_path()):
return redirect(request.get_full_path()) return redirect(request.get_full_path())
return redirect(self.get_return_url(request, device)) return redirect(self.get_return_url(request, device))
else: else:
membership_form = forms.DeviceVCMembershipForm(data=request.POST) membership_form = forms.DeviceVCMembershipForm(data=request.POST)
return render(request, 'dcim/virtualchassis_add_member.html', { return render(request, 'dcim/virtualchassis_add_member.html', {
@ -3891,7 +3888,6 @@ class VirtualChassisRemoveMemberView(ObjectPermissionRequiredMixin, GetReturnURL
return 'dcim.change_device' return 'dcim.change_device'
def get(self, request, pk): def get(self, request, pk):
device = get_object_or_404(self.queryset, pk=pk, virtual_chassis__isnull=False) device = get_object_or_404(self.queryset, pk=pk, virtual_chassis__isnull=False)
form = ConfirmationForm(initial=request.GET) form = ConfirmationForm(initial=request.GET)
@ -3902,7 +3898,6 @@ class VirtualChassisRemoveMemberView(ObjectPermissionRequiredMixin, GetReturnURL
}) })
def post(self, request, pk): def post(self, request, pk):
device = get_object_or_404(self.queryset, pk=pk, virtual_chassis__isnull=False) device = get_object_or_404(self.queryset, pk=pk, virtual_chassis__isnull=False)
form = ConfirmationForm(request.POST) form = ConfirmationForm(request.POST)
@ -3916,13 +3911,11 @@ class VirtualChassisRemoveMemberView(ObjectPermissionRequiredMixin, GetReturnURL
return redirect(device.get_absolute_url()) return redirect(device.get_absolute_url())
if form.is_valid(): if form.is_valid():
device.snapshot()
devices = Device.objects.filter(pk=device.pk) device.virtual_chassis = None
for device in devices: device.vc_position = None
device.virtual_chassis = None device.vc_priority = None
device.vc_position = None device.save()
device.vc_priority = None
device.save()
msg = _('Removed {device} from virtual chassis {chassis}').format( msg = _('Removed {device} from virtual chassis {chassis}').format(
device=device, device=device,