-
Notifications
You must be signed in to change notification settings - Fork 900
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
[V2V] Datastore validation in Transformation Mapping #19204
Conversation
@thearifismail 'djberg96 agrare' is an invalid reviewer, ignoring... |
@miq-bot add_labels transformation, ivanchuk/yes, hammer/yes |
@thearifismail Cannot apply the following label because they are not recognized: ivanchuck/yes |
# cleanup if transformation mapping or any of its items are invalid | ||
def cleanup (tm) | ||
tmis = TransformationMappingItem.where(:tranformation_mapping_id => tm.id) | ||
tm.delete |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this type of cleanup is something you want to do within a validation block...rails will rollback the transaction if something fails validation cc @djberg96
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue is that in order to perform the storage validation we need the parent TransformationMapping
object first, that's why it's being done as an after_create
, so it's not a baked in validation, but rather our custom handler.
If there's a way combine a Rails validation callback with after_create
that I'm not aware of, please let me know.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried adding validate
after after_create
but the Transformation_Mapping
but the validation would fail because the TransformationMapping
API implementation could not handle the new implementation. That's why I had to customize the cleanup to ensure their cleanup.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue is that in order to perform the storage validation we need the parent TransformationMapping object first
Is that not available when using validate
? I took this branch and changed it from after_create to validate and deleted the calls to cleanup and I can see the parent transformation_mapping in the validation block.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the TransformationMapping API implementation could not handle the new implementation. That's why I had to customize the cleanup to ensure their cleanup.
An aside, It is a little odd that validation on a single sub-item will destroy the entire tree above it IMO, its not obvious to me that this would happen.
Another aside, if you did tm.destroy
with proper cascade delete you shouldn't have to individually .delete
the child records. Calling just .delete
also has the high likelihood of leaving orphaned records. When in doubt use .destroy
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replaced tm.delete with tm.destroy and removed the explicit cleanup method. Could not test the new implementation using the WebUI and byebug
and relied on testing in rails console
using tm = TransformationMapping.find(<ID>)
and then tm.destory
. Veried that all the transformation_mapping_items
in the plan were deleted.
…troy and removed explicit cleanup method.
|
||
validate :source_cluster, :if => -> { source.kind_of?(EmsCluster) } | ||
validate :destination_cluster, :if => -> { destination.kind_of?(EmsCluster) || destination.kind_of?(CloudTenant) } | ||
|
||
after_create :validate_source_datastore, :if => -> { source.kind_of?(Storage) } | ||
after_create :validate_destination_datastore, :if => -> { destination.kind_of?(Storage) || destination.kind_of?(CloudVolume) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@thearifismail can you share the workflow or reproduce in specs the situation where you don't have access to the transformation_mapping
in the validate_source_datastore
or validate_destination_datastore
methods when being used as validate
instead of after_create
?
I tested this in specs which is the create the transformation mapping, then create the individual items
as well as the initializing without creating the transformation_mapping and all the items then .save!
the top level which saves the associations in one transaction.
Both worked as expected, I had access to the transformation_mapping from the validation method just in the latter case it didn't have an ID yet but when the validation failed it rolled back the TransformationMapping record.
|
||
unless src_cluster_storages.include?(source_storage) | ||
errors.add(:source, "Source cluster storages must include source storage: #{source_storage}") | ||
tm.destroy |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still think it is weird that a validation on a sub-item will destroy the whole tree
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A transformation map has three pairs of source and destination items. They are EmsClusters, Storages, and Lans. For a transformation map to work, all six have to be valid. A single invalid item renders all others useless. Say clusters, storages, and source lan validation was successful but the destination lan was bad, all the previous 5 items become useless and have to be purged. That's why I have implemented this way. If you can suggest a more elegant way, please share with me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you can suggest a more elegant way, please share with me.
Absolutely, if you make this a validation instead of an after_create and you save all in one transaction then:
>> vc = ManageIQ::Providers::Vmware::InfraManager.first
>> rhv = ManageIQ::Providers::Redhat::InfraManager.first
>> source_cluster = vc.ems_clusters.first
>> dest_cluster = rhv.ems_clusters.first
>> source_lan = vc.lans.first
>> dest_lan = rhv.lans.first
>> source_storage = Storage.create!(:name => "unattached storage")
>> dest_storage = rhv.storages.first
>> tm = TransformationMapping.new(:name => "my transformation")
>> tm.transformation_mapping_items.new(:source => source_cluster, :destination => dest_cluster)
>> tm.transformation_mapping_items.new(:source => source_lan, :destination => dest_lan)
>> tm.transformation_mapping_items.new(:source => source_storage, :destination => dest_storage)
>> tm.save!
(0.4ms) BEGIN
(0.3ms) ROLLBACK
ActiveRecord::RecordInvalid (Validation failed: TransformationMapping: Transformation mapping items is invalid)
So any validation failures prevent the entire mapping from being save:
>> TransformationMapping.first
=> nil
If you can't save the whole mapping&items in one transaction then I recommend having the caller catch the exception and destroy the mapping:
>> tm = TransformationMapping.create!(:name => "my transformation")
>> tm.transformation_mapping_items.create!(:source => source_cluster, :destination => dest_cluster)
>> tm.transformation_mapping_items.create!(:source => source_lan, :destination => dest_lan)
>> begin
>> tm.transformation_mapping_items.create!(:source => source_storage, :destination => dest_storage)
>> rescue ActiveRecord::RecordInvalid
>> tm.destroy!
>> end
@agrare Looks like we need an update on the api side: ManageIQ/manageiq-api#664 |
That's an interesting way to do it, you could also use the association to create it. So instead of effectively doing:
This is equivalent to
|
@agrare Multiple ways to skin a cat I guess, I just went with what was already there. :) |
Yeah, but definitely if you didn't give it any context of the mapping then it wouldn't be available in the validation method. So given that can we change this to |
@agrare Oh, maybe I wasn't sure what you were responding to. Once the api change is made, we should be able to change this back to a standard validation. |
👍 sounds good |
And yeah wasn't saying your PR was weird, just the way it was done already was strange |
@thearifismail looking good, couple small comments and can you add specs covering the case where the the mapping item is invalid |
…or invalid mapping items
Checked commits thearifismail/manageiq@31081c8~...50c0ca0 with ruby 2.4.6, rubocop 0.69.0, haml-lint 0.20.0, and yamllint 1.10.0 |
[V2V] Datastore validation in Transformation Mapping
Thanks @thearifismail ! There are going to be some other dependencies to get this backported to hammer cleanly, can you list the other PRs that need to be backported as well for @simaishi |
[V2V] Datastore validation in Transformation Mapping (cherry picked from commit 3ea5480) https://bugzilla.redhat.com/show_bug.cgi?id=1768517
Ivanchuk backport details:
|
This PR validates that the source and target datastores (storages) are associated with the parent EmsCluster. This time around the validation is done as a part of
after_create
step. This was necessary to work with the transformation_mapping APITarget Datastores
RHV: Class Storage, Filters: Must belong to target cluster
OSP: Class CloudVolume, Filters: Must belong to target CloudTenant's ExtManagementSystem
Source Datastores
VMW: Class Storage, Filters: Must belong to one of the source clusters
https://trello.com/c/1x31IdD0/241-spike-mapping-and-plan-api-validation-of-input
https://bugzilla.redhat.com/show_bug.cgi?id=1666840