Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #14482 - Fix validation error when primary IP is moved #14514

Merged
merged 7 commits into from
Dec 28, 2023

Conversation

DanSheps
Copy link
Member

Fixes: #14482

When an Primary IP address is moved, it should result in a validation error. However, it does not normally due to the fact that the clean() method on the model returns with the field assigned_object instead of interface, vminterface, or fhrpgroup. When the _post_clean() method tries to resolve the fields, it will fail because the form does not contain assigned_object as a field and returns a ValueError.

This will correctly map the assigned_object field to the correct field, however we should maybe look at instead providing a method to have assigned_object (or similar GFK fields) as proper form fields in the future, perhaps using a custom form field.

@@ -341,6 +341,19 @@ def __init__(self, *args, **kwargs):
self.fields['vminterface'].disabled = True
self.fields['fhrpgroup'].disabled = True

# Correctly assigned assigned_object if the error exists when validating this form.
def add_error(self, field, errors):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't a tenable approach to fixing the validation. We need to adjust the logic under the model form's clean() method to ensure it's catching the error and raising a ValidationError against the appropriate field before the model's full_clean() method is called.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to adjust the logic under the model form's clean() method to ensure it's catching the error and raising a ValidationError against the appropriate field before the model's full_clean() method is called.

This isn't an option.

The "clean" flow is:

form.save() -> form.full_clean() -> form._clean_fields()
                                                       form._clean_form() -> form.clean()
                                                       form._post_clean() -> model.full_clean()

the form.clean_form() catches the validation error and adds it to the stack, however the post_clean calls the model clean regardless of whether there is already an error on the stack.

The error stack looks like this:

{
    "assigned_object": "Cannot reassign IP address while it is designated as the primary IP for the parent object",
    "vminterface": "Cannot reassign IP address while it is designated as the primary IP for the parent object"
}

The only way to remove this problem seems to be to also remove the ErrorDict from the model clean() and just throw a straight string validation error (pushing this change now).

I think realistically we need to re-investigate GFK's and see if there isn't a way to:

  • Create a fieldgroup called "assigned_object" (or whatever the GFK name is)
  • Create a select field called "assigned_object_type" (or whatever the GFK type field name is) that is populated with the types available for selection
  • Create a select field called "assigned_object_id" (or whatever the GFK id field name is) that is populated with the objects available for selection based on the type selected
  • Modify add_error to also match on the assigned_object fieldgroup

This obviously wouldn't be do-able in this FR but something to think about.

Comment on lines 372 to 384
if self.instance.pk:
prev_interface = self.instance.interface.first() or self.instance.vminterface.first()
# If the prev interface exists and does not match the new interface, we need to validate it isn't set as primary
# for the parent
if prev_interface and prev_interface != interface:
prev_parent = prev_interface.device or prev_interface.virtual_machine
# Check that the parent exists and if it is set as a primary ip.
if prev_parent and prev_parent.primary_ip4 == self.instance or prev_parent.primary_ip6 == self.instance:
self.add_error(
selected_objects[0],
_("Cannot reassign IP address while it is designated as the primary IP for the parent object")
)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this needed? Decoupling the ValidationError from a specific field under the model's clean() seems sufficient to address the bug.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It likely isn't needed, but it was an attempt to maintain the field highlighting. However you are correct that it likely isn't needed with just the decoupling.

Copy link
Member

@jeremystretch jeremystretch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @DanSheps!

@jeremystretch jeremystretch merged commit 45c646d into develop Dec 28, 2023
8 checks passed
@jeremystretch jeremystretch deleted the 14482-ipaddress-move-return-validation-error branch December 28, 2023 18:28
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jan 28, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Cannot move primary IP from device to VM or vice versa
2 participants