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

Bulk tag multiple VMs #13581

Merged
merged 5 commits into from
Jan 30, 2017
Merged

Bulk tag multiple VMs #13581

merged 5 commits into from
Jan 30, 2017

Conversation

jntullo
Copy link

@jntullo jntullo commented Jan 19, 2017

This enhancement to the API gives the ability to tag multiple VMs with multiple tags with one call. Sample request:

POST /api/vms
{ 
  "action" : "assign_tags",
  "resources" : [
    { "href" : "http://localhost:3000/vms/11", "tags" : [ { "category" : "department", "name" : "finance" } ]} 
    { "href" : "http://localhost:3000/vms/12", "tags" : [ { "category" : "department", "name" : "finance" }]}
  ]
}

It will also accept the ID or HREF of a tag.

Sample response:

{"results"=>
  [{"11"=>
     [{"success"=>true,
       "message"=>"Assigning Tag: category:'department' name:'finance'",
       "href"=>"http://www.example.com/api/vms/",
       "tag_category"=>"department",
       "tag_name"=>"finance"}]},
   {"12"=>
     [{"success"=>true, "message"=>"Assigning Tag: category:'department' name:'finance'", "href"=>"http://www.example.com/api/vms/", "tag_category"=>"department", "tag_name"=>"finance"}]}]}

Each individual VM resource will return an array of results for each tag resource passed to it.

Additional PRs will be added to create bulk unassign and bulk assigning on services.

cc: @imtayadeway
@miq-bot add_label enhancement, api, euwe/no
@miq-bot assign @abellotti

Links

@@ -85,7 +85,7 @@ def parse_tag(data)

def parse_tag_from_href(data)
href = data["href"]
tag = if href && href.match(%r{^.*/tags/#{CID_OR_ID_MATCHER}$})
tag = if href && href.match(%r{^.*/tags/#{ArRegion::CID_OR_ID_MATCHER}$})
Copy link
Member

Choose a reason for hiding this comment

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

why was this needed ?

Copy link
Author

Choose a reason for hiding this comment

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

getting uninitialized constant Api::Subcollections::Tags::CID_OR_ID_MATCHER without it in the tests

Copy link
Member

Choose a reason for hiding this comment

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

wow!! so better code coverage with the tests you've added 😍

Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like there's a few of these around....perhaps this could be addressed in separate PR so it could be backported if needed? I wonder if it regressed when this became a mixin?

Copy link
Author

Choose a reason for hiding this comment

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

@imtayadeway sounds good, will do!

{ id => tag_results }
rescue => err
{ id => action_result(false, err.to_s) }
end
Copy link
Member

Choose a reason for hiding this comment

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

See if this can't be generalized, will be easier to implement assign_tags/unassign_tags to all other collections that support the tags subcollection.

Copy link
Member

Choose a reason for hiding this comment

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

Looking at the api.yml, I see bulk assign_tags and unassign_tags could be implemented to the following collections for completion:

blueprints
clusters
data_stores
groups
hosts
providers
resource_pools
service_templates
services
templates
tenants
users

so generalizing assign_tags_resource and unassign_tags_resource, maybe as a mixin would be a 👍

Copy link
Author

Choose a reason for hiding this comment

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

@abellotti sounds good to me! will do 👍

@@ -1890,6 +1890,8 @@
:identifier: vm_scan
- :name: delete
:identifier: vm_delete
- :name: assign_tags
:identifier: vm_tag
Copy link
Member

Choose a reason for hiding this comment

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

I take it unassign_tags will be a separate PR ?

Copy link
Author

Choose a reason for hiding this comment

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

correct 👍

}
expect(response).to have_http_status(:ok)
expect(response.parsed_body).to include(expected)
end
Copy link
Member

Choose a reason for hiding this comment

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

I would prefer action results similar to when assigning/unassigning tags on a
resource i.e. vms/72/tags, i.e. even though we can target multiple resources,
it's still an action performed (thus result) per tag.

so instead of:

{
  "results": [
    {   
      "72": [
        {   
          "success": true,
          "message": "Assigning Tag: category:'department' name:'finance'",
          "href": "http://localhost:3000/api/vms/",
          "tag_category": "department",
          "tag_name": "finance"
        },  
        {   
          "success": true,
          "message": "Assigning Tag: category:'location' name:'ny'",
          "href": "http://localhost:3000/api/vms/",
          "tag_category": "location",
          "tag_name": "ny"
        }   
      ],  
      "75": [
        {   
          "success": true,
          "message": "Assigning Tag: category:'department' name:'finance'",
          "href": "http://localhost:3000/api/vms/",
          "tag_category": "department",
          "tag_name": "finance"
        }   
      ]   
    }   
  ]
}

I'd rather get:

{
  "results": [
    {   
      "success": true,
      "message": "Assigning Tag: category:'department' name:'finance'",
      "href": "http://localhost:3000/api/vms/72",
      "tag_category": "department",
      "tag_name": "finance"
    },
    {
      "success": true,
      "message": "Assigning Tag: category:'location' name:'ny'",
      "href": "http://localhost:3000/api/vms/72",
      "tag_category": "location",
      "tag_name": "ny"
    },
    {
      "success": true,
      "message": "Assigning Tag: category:'department' name:'finance'",
      "href": "http://localhost:3000/api/vms/75",
      "tag_category": "department",
      "tag_name": "finance"
    }
  ]
}

klass = collection_class(:tags)
klass.find(from_cid(href.split('/').last))
end
href = parse_href(data['href'])
Copy link
Author

Choose a reason for hiding this comment

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

still some work left to do on this PR but I saw some room for refactoring / reuse which would negate the need for #13597. Thoughts on this approach @abellotti @imtayadeway?

Copy link
Contributor

Choose a reason for hiding this comment

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

LGTM, although I can't tell from the context if we're saying here that this needs to be a tag url. @abellotti ?

Copy link
Member

Choose a reason for hiding this comment

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

this code should only be defining tag if the href is indeed for a tag, so we can't just simply use parse_href. the code can be generalized if this is needed somewhere where where we send in the collection of interest, (i.e. :tags) then that drives the generalization of the old lines 88-91. Not sure where else we do this though.

@@ -478,7 +478,7 @@ def expect_resource_has_tags(resource, tag_names)

run_post("#{blueprints_url(blueprint.id)}/tags",
:action => "assign",
:catagory => tag1[:category],
:category => tag1[:category],
Copy link
Author

Choose a reason for hiding this comment

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

The refactoring above effort also shed light on the typos in these specs

@@ -140,7 +140,7 @@ def update_multiple_collections(is_subcollection, target, type, resources)
update_one_collection(is_subcollection, target, type, rid, r)
end
raise BadRequestError, "No #{type} resources were specified for the #{action} action" if processed == 0
{"results" => results}
{"results" => results.flatten}
Copy link
Contributor

Choose a reason for hiding this comment

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

Interesting. How was this failing before?

Copy link
Author

Choose a reason for hiding this comment

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

@imtayadeway it wasn't failing - I believe this is the first action where it is returning an array of results per resource, so the results were being returned in a nested array ([[]]) rather than how they are now, but .flatten fixes that. Does that make sense?

Copy link
Member

Choose a reason for hiding this comment

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

I wonder if the flattening shouldn't be in lower level, maybe assign_tags_resource.

Copy link
Author

Choose a reason for hiding this comment

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

@abellotti it can't be placed there, because it calls assign_tags_resource for each resource. So even though that is flattened, it is still returning a single array, which results in the results being an array of an array

Copy link
Author

Choose a reason for hiding this comment

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

I just double checked my logic just to be sure and am not sure my explanation above made sense, so here is the full results if the flatten is moved up:

{"results"=>
  [[{"success"=>true,
     "message"=>"Assigning Tag: category:'department' name:'finance'",
     "href"=>"http://www.example.com/api/vms/",
     "tag_category"=>"department",
     "tag_name"=>"finance",
     "tag_href"=>"http://www.example.com/api/tags/10000000002666"}],
   [{"success"=>true,
     "message"=>"Assigning Tag: category:'cc' name:'001'",
     "href"=>"http://www.example.com/api/vms/",
     "tag_category"=>"cc",
     "tag_name"=>"001",
     "tag_href"=>"http://www.example.com/api/tags/10000000002670"}]]}

@abellotti
Copy link
Member

@jntullo #13671 was merged, can you update here to reference via BaseController::CID_OR_ID_MATCHER ? Thanks.

@miq-bot
Copy link
Member

miq-bot commented Jan 27, 2017

This pull request is not mergeable. Please rebase and repush.

@@ -89,7 +89,7 @@ def update_raw_power_state(state, *vms)
context "Vm software subcollection" do
let(:sw1) { FactoryGirl.create(:guest_application, :vm_or_template_id => vm.id, :name => "Word") }
let(:sw2) { FactoryGirl.create(:guest_application, :vm_or_template_id => vm.id, :name => "Excel") }
let(:vm_software_url) { "#{vms_url(vm.id)}/software" }
Copy link
Contributor

Choose a reason for hiding this comment

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

Although I approve of this change, it doesn't seem related to the rest of this PR

Copy link
Author

Choose a reason for hiding this comment

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

@imtayadeway sorry - an accidental commit

@miq-bot
Copy link
Member

miq-bot commented Jan 30, 2017

Checked commits jntullo/manageiq@ae60628~...aac19cb with ruby 2.2.6, rubocop 0.47.1, and haml-lint 0.20.0
3 files checked, 0 offenses detected
Everything looks good. ⭐

@abellotti abellotti added this to the Sprint 53 Ending Jan 30, 2017 milestone Jan 30, 2017
@abellotti
Copy link
Member

LGTM!!

@abellotti abellotti merged commit a8de3d5 into ManageIQ:master Jan 30, 2017
@jntullo jntullo deleted the enhancement/bulk_tag_vms branch November 28, 2017 19:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants