Fixes #38416 - Hosts bulk action : Assign organization/ location#10538
Fixes #38416 - Hosts bulk action : Assign organization/ location#10538lfu wants to merge 1 commit into
Conversation
874862d to
1d2dcb6
Compare
f2d8412 to
d224847
Compare
|
Could we have a review here? |
There was a problem hiding this comment.
When I reassign hosts out of my organization but have my org selected in org context (e.g. not "Any Organization") the hosts are reassigned but I get this weird success message about 0 hosts:
Then I tried to assign the test2 hosts back to Default Organization and got this:

Even though I had said "Fix on mismatch" so there should be no mismatch in settings.
Reassigning with "Any Organization" selected seems to work OK.
Side note: It might be nice to be able to add "Organization" and "Location" columns to the new All Hosts page.
Another problem: If a host is already registered, I should not be able to change its organization.
I should get an error similar to this one:

But I was able to reassign org for my registered hosts without error here. edit: Oops, didn't see the Katello PR. 😳
There was a problem hiding this comment.
| api :PUT, "/hosts/bulk/assign_taxonomy", N_("Assign organization/ location") | |
| api :PUT, "/hosts/bulk/assign_taxonomy", N_("Assign organization/location") |
There was a problem hiding this comment.
| param :optimistic_import, :bool, N_("Fix organization/ location on mismatch") | |
| param :optimistic_import, :bool, N_("Fix organization/location on mismatch") |
There was a problem hiding this comment.
You can't use JS template strings (${taxonomy} etc.) with __, unfortunately. Need to use a <FormattedMessage> instead, for the translation to work correctly.
There was a problem hiding this comment.
| 'Select organization/ location to add systems to. This change may affect all your selected systems. Some hosts may already be in your chosen group.' | |
| 'Select organization/location to add systems to. This change may affect all your selected systems. Some hosts may already be in your chosen group.' |
There was a problem hiding this comment.
I see the tooltips next to "Select organization." But that implies that it's explaining the text "Select organization," which is self-explanatory. I would expect the tooltip to be next to "Fix on mismatch," which is what it's explaining.
There was a problem hiding this comment.
"Some hosts may already be in your chosen group" was about hostgroups.
We are not adding additional; rather, we are assigning.
We call them "hosts" and not "systems" everywhere in Satellite, so this should be consistent.
| 'Select organization/ location to add systems to. This change may affect all your selected systems. Some hosts may already be in your chosen group.' | |
| 'Select organization/location to assign hosts to. This change may affect all your selected hosts.' |
There was a problem hiding this comment.
Those messages come from the provided mock page.
I could use your suggestion as well.
There was a problem hiding this comment.
I vote for my suggestion 😇 but will also tag @MariSvirik in case she thinks more argument is needed :)
There was a problem hiding this comment.
👍 for what @jeremylenz said because Foreman uses hosts, not systems. (Katello used to have systems, but thankfully it's now aligned on hosts).
|
d224847 to
9471bac
Compare
b6bcda0 to
3b4d838
Compare
You should be able to use |
80cd367 to
f4f8308
Compare
|
Updated. |
There was a problem hiding this comment.
This looks like an unrelated change. If it was intentional, it still needs its own PR.
There was a problem hiding this comment.
It's intentional to fix the typo.
There was a problem hiding this comment.
Should it then be fixed in the description too?
There was a problem hiding this comment.
| msg = tax_type == 'organization' ? _("organizatoin %s") % tax.name : _("location %s") % tax.name | |
| msg = tax_type == 'organization' ? _("organization %s") % tax.name : _("location %s") % tax.name |
There was a problem hiding this comment.
It is a string.
This code is just got refactored here. Shared by both API v1 and v2 code to reassign taxonomy.
There was a problem hiding this comment.
You can introduce a proper API in the new ome. Then in the old controller compare it to yes to make it a boolean
There was a problem hiding this comment.
Count is fairly expensive and !any? should be cheaper.
| elsif taxonomy.need_to_be_selected_ids.count == 0 | |
| elsif !taxonomy.need_to_be_selected_ids.any? |
There was a problem hiding this comment.
This refactor code is really complicated. I'd like to not touch it at all.
But this change seems simple. So here it is.
There was a problem hiding this comment.
Looks like this is a HashWithIndifferentAccess so it's not a query as I assumed. That means it probably doesn't matter all that much.
Feel it is not straightforward to read
!my_array.any?. Look what Google says.
For Hash is not redefined and will fallback to Enumerable#count, which means its complexity will be O(n) as all key-values will be traversed.For Hash is not redefined and will fallback to Enumerable#count, which means its complexity will be O(n) as all key-values will be traversed.
Note your screenshot states:
collections with a count property (e.g., Arrays)
But Hash is not such a collection if my quick research is correct.
Note that Hash implements enumerable so you can also write:
| elsif taxonomy.need_to_be_selected_ids.count == 0 | |
| elsif taxonomy.need_to_be_selected_ids.none? |
Overall, I think that should win on both performance and readability.
There was a problem hiding this comment.
The space after the / is really jarring to me. I also question assign. We don't have that for Host group so why do we have it here? Would this be better?
| {__('Assign organization/ location')} | |
| {__('Change organization and/or location')} |
There was a problem hiding this comment.
I debated that as well, but there is one potential benefit: if the location belongs to another org you can do it in a single operation. It may otherwise not be possible.
Another option is to go another route and actually combine all bulk changes into 1: also include the host group into the dialog. Then the menu item is simple (a single button). Host groups may also belong to another org so it may actually be common to perform that in a single operation.
There was a problem hiding this comment.
It is a multiple operation to change the hosts if both organization and location are selected. Hosts are modified in separate calls.
It's not just two attributes. There is lots of checking there for a list of things that might mismatch.
Without enough knowledge in that area, I think it's better just keep it as it is.
There was a problem hiding this comment.
I don't consider host group an affiliation. Can we avoid that word? Overall I'm wondering if a fly out menu is a good idea for 2 items. Perhaps just list them both as their own?
There was a problem hiding this comment.
How about this..
Change associations >
Host group
Host collections
Owner
Location/organization
Disassociate compute resource
There was a problem hiding this comment.
I think that could work. Or, as I suggested in #10538 (comment), group more together in the real dialog.
There was a problem hiding this comment.
I think taxonomy itself needs to be translated as well. Otherwise you'll end up with an English word in a non-English sentence.
There was a problem hiding this comment.
Please don't parse error messages. HTTP responses have a code.
| if (message.includes('Request failed with status code 422')) { | |
| message = __('Update failed because of mismatch in settings'); | |
| } else if (message.includes('Request failed with status code 400')) { | |
| if (response.status == 422) { | |
| message = __('Update failed because of mismatch in settings'); | |
| } else if (response.status == 400) { |
Having said that, please make sure the HTTP response contains a proper message you can display instead of coming up with your own. Server side you often know much better why something failed.
There was a problem hiding this comment.
Could I have more details about HTTP response? How to set it up and where?
There was a problem hiding this comment.
handleError calls APIActions.put which is also in Foreman. I think a good start would be to go where that's defined and do some console logs.
There was a problem hiding this comment.
Thanks to @ekohl for catching this and @jeremylenz @parthaa for finding out the issue.
There was a problem hiding this comment.
Regular users do not have access to the server logs so that's not a valid suggestion. They will get stuck.
f4f8308 to
5cfa1fe
Compare
f1e543c to
e5cd7fc
Compare
ekohl
left a comment
There was a problem hiding this comment.
Summary of this review: please modify assign_taxonomy to assign both taxonomies at once and make it return the number of updated hosts. Then return that to the user.
There was a problem hiding this comment.
Isn't this easier?
| if [params[:optimistic_import_organization], params[:optimistic_import_location]].any? { |val| !val } | |
| if !params[:optimistic_import_organization] || !params[:optimistic_import_location] |
But effectively that means both options are mandatory. Why present them as options at all then?
Another thing to consider: is it mandatory to assign both a location and organization? From reading the code (Taxonomy.where) I think it deals with nil because then targets will not have the entry. That implies you don't need optimistic_import to be true for that type.
And to be clear, I can see benefits in presenting the option so perhaps it's better to just drop this validation.
Now that also makes me think you can return early if neither target_organization_id nor target_location_id was given.
There was a problem hiding this comment.
Nit: you can shorten this
| messages = [] | |
| targets = Taxonomy.where(id: [params[:target_organization_id], params[:target_location_id]]) | |
| targets.each do |tax| | |
| tax_type = tax.type.downcase | |
| BulkHostsManager.new(hosts: @hosts).assign_taxonomy(tax, params["optimistic_import_#{tax_type}"]) | |
| msg = tax_type == 'organization' ? _("Organization is set to %s.") % tax.name : _("Location is set to %s.") % tax.name | |
| messages << msg | |
| end | |
| targets = Taxonomy.where(id: [params[:target_organization_id], params[:target_location_id]]) | |
| messages = targets.map do |tax| | |
| tax_type = tax.type.downcase | |
| BulkHostsManager.new(hosts: @hosts).assign_taxonomy(tax, params["optimistic_import_#{tax_type}"]) | |
| tax_type == 'organization' ? _("Organization is set to %s.") % tax.name : _("Location is set to %s.") % tax.name | |
| end |
There was a problem hiding this comment.
If messages is empty then it didn't update anything. Perhaps that should also be considered?
And I'd still think that assign_taxonomy should return the number of updated hosts instead of just assuming every host was updated. See inline comments there for hints.
There was a problem hiding this comment.
This is the message for a success update. All hosts will be updated in this case.
And no hosts should be updated in case of a failure. That is why i try to bail out earlier by checking the settings.
There was a problem hiding this comment.
Since it's failing, could you update it as well so the failure message is useful?
| assert flash[:error] == "Cannot update location to Location 1 because of mismatch in settings" | |
| assert_equal "Cannot update location to Location 1 because of mismatch in settings", flash[:error] |
There was a problem hiding this comment.
I can change the assert style. But I don't see how it changes anything about the failure message.
There was a problem hiding this comment.
Didn't you see the failure? It said that false wasn't true. With assert_equal it will tell you the exact mismatch: both the expected and actual texts. That makes looking at CI results better.
There was a problem hiding this comment.
You can consistently make it return the number of updated hosts:
| @hosts.update_all("#{tax_type}_id".to_sym => taxonomy.id) | |
| # hosts location needs to be updated before import missing ids | |
| taxonomy.import_missing_ids | |
| updated = @hosts.update_all("#{tax_type}_id".to_sym => taxonomy.id) | |
| # hosts location needs to be updated before import missing ids | |
| taxonomy.import_missing_ids | |
| updated |
Then you can also update the documentation of this method with:
# @return [Integer]
# The number of updated hostsYou can then use that in the bulk controller to accurately state how many hosts were updated.
However, that also presents another problem: you can't simply add the 2 numbers and they may have overlap. That again stresses why it would be better to do both updates in a single transaction and only update the hosts once. I'd expect it to be faster and also gives a more accurate summary to the user.
There was a problem hiding this comment.
updated = @hosts.update_all("#{tax_type}_id".to_sym => taxonomy.id)
Isn't this the same as @hosts.count?
There was a problem hiding this comment.
Currently there is no problem to find out how many hosts are updated accurately. Not sure what the concern is here.
It is really hard to make the both updates in one transaction considering the two different mismatch settings and the support for both API versions. And I'm not sure if taxonomy.import_missing_ids works with updating both organization and location in one transaction or not.
If it really worth to make it one transaction and it is doable, maybe we can do it in a followup.
There was a problem hiding this comment.
Isn't this the same as
@hosts.count?
No, it's not. update_all returns the number of affected rows, not how many you sent in the first place.
So it is at least 0 (in case nothing was updated) or at most @hosts.count (in case all rows were updated).
Currently there is no problem to find out how many hosts are updated accurately. Not sure what the concern is here.
That's because you misunderstood the return value of update_all. There is a problem.
It is really hard to make the both updates in one transaction considering the two different mismatch settings and the support for both API versions. And I'm not sure if
taxonomy.import_missing_idsworks with updating both organization and location in one transaction or not.
The last point is an interesting challenge. But if you can't, then combining both into a single frontend dialog may be wrong to start with.
If it really worth to make it one transaction and it is doable, maybe we can do it in a followup.
I think it is about returning a correct number. If you don't want to show a correct number, then please don't show one at all (like the old UI).
There was a problem hiding this comment.
I will change the message to not display number of hosts.
The last point is an interesting challenge. But if you can't, then combining both into a single frontend dialog may be wrong to start with.
I want to make it right. Do you think it is reasonable to have 2 tasks for change organization and change location instead of 1 task here in this PR? @ekohl @jeremylenz
There was a problem hiding this comment.
I think having separate modals for change org & change location would be fine, especially since it simplifies our implementation here. I can't imagine a lot of users wanting to do both at the same time, but if I am wrong, it's a rare-enough task that it's worth the tradeoff.
There was a problem hiding this comment.
Today it's also separate, so it's the most straight forward migration for the user.
12e2fe8 to
7a0a850
Compare
|
Close this one in favor of #10591. |




Add
Assign organization/locationfor new hosts page.To test
Select hosts - Assign organization/location from Kebab menu.
Some test cases.
Prepare a testing environment with hosts, both registered and unregistered. Multiple organizations and locations.
Repeat above tests with Any Organization and Any Location context.
Registered hosts are not allowed to change organization. All other cases should work.
Tests for registered hosts need Katello/katello#11396.