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

Use approval workflow for instance resize operation #16044

Merged
merged 1 commit into from Oct 25, 2017

Conversation

sseago
Copy link
Contributor

@sseago sseago commented Sep 26, 2017

@gmcculloug
Copy link
Member

@sseago Is there a UI you can add to the description?

Is this reconfigure for Cloud? If so we may want to consider how to use the existing workflow instead of creating a new, and likely confusing, duplication of the reconfigure logic.

@sseago
Copy link
Contributor Author

sseago commented Sep 28, 2017

@gmcculloug There's no new UI here. This is the openstack "resize" operation which is exposed in the UI for openstack-related VMs as "reconfigure". The functionality already exists as "resize" in the models -- it's distinct from reconfigure since the inputs and validation (and really everything) is different. Basically for OpenStack instances, resize takes a new flavor and the underlying OpenStack API call accepts an instance ID and a new flavor ID. The difference is instead of the UI action calling the model itself, we create a VMResizeRequest object which in turn calls resize on the model. Trying to rewrite the existing reconfigure (both model and request workflow) to use the same API would be even more complex and confusing and, in any case, seems as if it would be out of scope for the task of "do what we do now but with approval workflow" since it would be a fairly substantial rework of "what we do now" for both resize and reconfigure.

The development task here is basically "use approval rather than making a direct model call", and the existing VMReconfigureRequest has nothing in it that's relevant to the resize operation, since in the models resize and reconfigure are completely different operations.

@tzumainn
Copy link
Contributor

tzumainn commented Oct 4, 2017

@gmcculloug Hi! Can you let us know if what scott says makes sense? It made sense to me when he and I first looked at the code, but I don't know if I'm missing something obvious.

@gmcculloug
Copy link
Member

@sseago Where is the "do what we do now but with approval workflow" comment coming from? Is there a reference to an RFE or enhancement request you can link here?

Today is is support approval workflow, but what about quota which is part of the provisioning/reconfigure workflows? Here we are allowing a user to consume more quota without any checking.

I totally agree that it will be a substantial rework of the existing reconfigure logic but this does not feel like we are not addressing the full issue.

@tzumainn
Copy link
Contributor

tzumainn commented Oct 4, 2017

@gmcculloug I can provide some background: we exposed a resize operation for OpenStack instances a while ago, and PM later asked why there wasn't an approval workflow for it. This is an effort to remediate that.

If there's a better way to fix this, please let us know!

@gmcculloug
Copy link
Member

First, it would help if we could determine if they think quota is important here.

@tzumainn
Copy link
Contributor

tzumainn commented Oct 4, 2017

Fair enough! Are there other issues we should talk with PM about? Just want to make sure everything is covered in one swoop!

@gmcculloug
Copy link
Member

I guess the only other thing I would ask is if the interaction with automate, outside of approval, is important. Right now the task would not be processed through automate, which is probably ok.

@gmcculloug
Copy link
Member

I think this helps answer my question about quota: https://bugzilla.redhat.com/show_bug.cgi?id=1500152

Quota can be implemented with the current setup but I still think we should look into refactoring/sub-classing the current VmReconfigure models to handle this instead of adding ambiguous VmResize ones just for cloud.

cc @tinaafitz

@tzumainn
Copy link
Contributor

@gmcculloug haha, yeah, it looks like something we should hopefully be able to fix here. Any guidance would be greatly appreciated if there's another direction you think we should go!

Copy link
Member

@gmcculloug gmcculloug left a comment

Choose a reason for hiding this comment

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

@sseago Added my suggestions on changing to use a class more inline with the existing VmReconfigure request/task. I think we should avoid using Resize in most places and I pointed many of them out, but not all.

There is also outstanding work in the automate model to enable auto-approval logic which is how I would suggest shipping this feature out-of-the-box.
By doing that users will not have to manually approve request, but allows for an admin to enable/disable auto-approval logic. We can discuss when this is a little further along.

@@ -0,0 +1,26 @@
class VmResizeRequest < MiqRequest
Copy link
Member

Choose a reason for hiding this comment

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

My suggestion is to use "VmCloudReconfigure" for this work. So this class name would be VmCloudReconfigureRequest and the task would be VmCloudReconfigureTask.

class VmResizeRequest < MiqRequest
TASK_DESCRIPTION = 'VM Resize'.freeze
SOURCE_CLASS_NAME = 'Vm'.freeze
ACTIVE_STATES = %w(resized) + base_class::ACTIVE_STATES
Copy link
Member

Choose a reason for hiding this comment

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

ACTIVE_STATES should be reconfigured

@@ -107,7 +107,7 @@ def cpu_percent_available?
true
end

def resize_queue(userid, new_flavor)
def resize_queue(userid, new_flavor_id)
Copy link
Member

Choose a reason for hiding this comment

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

The new request_task is being queued to run with the proper role and zone so this method is no longer needed by the reconfigure request/task logic. The task should call the resize method directly.

I am not sure if there are any other callers of this method, if not it can be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This can be removed. I had already wondered whether I could remove this, but I was holding off until I got feedback on whether that was the right thing to do. I'll pull this method out along with the class renaming for the next iteration.

validate :must_have_user

def self.make_request(request, values, requester, auto_approve = false)
values[:request_type] = :vm_resize
Copy link
Member

Choose a reason for hiding this comment

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

:request_type should be vm_cloud_reconfigure. This will be used in automate when processing the request events.

@@ -0,0 +1,45 @@
class VmResizeTask < MiqRequestTask
Copy link
Member

Choose a reason for hiding this comment

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

Class name changes to VmCloudReconfigureTask


def do_request
_log.info("Resizing VM #{vm.id}:#{vm.name} to flavor #{options[:flavor_name]}")
vm.resize_queue(miq_request.userid, options[:flavor_id])
Copy link
Member

Choose a reason for hiding this comment

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

As mentioned before, the task will be running in the zone and role so you do not need to queue here.

vm.resize_queue(miq_request.userid, options[:flavor_id])

if AUTOMATE_DRIVES
update_and_notify_parent(:state => 'resized', :message => "Finished #{request_class::TASK_DESCRIPTION}")
Copy link
Member

Choose a reason for hiding this comment

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

:state => 'reconfigured'

@sseago
Copy link
Contributor Author

sseago commented Oct 11, 2017

@gmcculloug I think I must be missing the big picture here. You're proposing that we use the existing Reconfigure-related classes instead of the existing Resize-related ones (and any additional ones needed to complete the functionality). However, the actual underlying actions aren't at all the same. They do different things. They act on different elements and associations. Quota checking won't be the same either. Unless I'm missing something, trying to shoehorn resize into reconfigure will be significantly more complex and more confusing, since what happens in the action itself isn't the same. Reconfigure actually modifies explicit size-related attributes like RAM, CPUs, disk, etc. -- resize does none of that. Maybe some examples of how you imagine the functionality being merged might help me understand what's being proposed here.

@gmcculloug
Copy link
Member

Sorry, taking a step back from what I said earlier. I am ok with deriving the new request/task models from MiqRequest/MiqRequestTask models, I am not going to push to sub-class them at the moment.

In the long run this still might be something we refactor as most of the request/task logic you include here is directly copied from the VmReconfigureRequest and VmReconfigureTask. Thinking it would end up looking something like this:

                MiqRequest
                    |
               VmReconfigure
               /           \
VmInfraReconfigure       VmCloudReconfigure
                                                     

(Where VmInfraReconfigure replaces the current VmReconfigure model with shared logic for both Infra and Cloud.)

I do not agree with the statement that the "resize" logic is different then what reconfigure is doing. They are both trying to change the hardware configuration of the resource. Looking at the AWS documentation for cloud flavors it is clear that changing an instance flavor impacts the RAM, CPU and disk of the instance. (https://aws.amazon.com/ec2/instance-types)

(The resize method is exposed as "Reconfigure" for cloud already: https://github.com/ManageIQ/manageiq-ui-classic/blob/master/app/controllers/mixins/actions/vm_actions/resize.rb#L32)

image

As for quota, I would suggest that the automate team takes on that work as that feature is currently intertwined in automate methods which we are hoping to extract soon.

@sseago
Copy link
Contributor Author

sseago commented Oct 12, 2017

@gmcculloug I think we're probably focusing on different aspects of this when saying that the two actions are similar vs. different. The high-level logical idea of what's going on is similar, but the implementation as to what actually needs to happen on the back end (and how it's validated) is different. I'd agree with your long-term suggestion that, rather than try to use the existing VmReconfigure code for resize as well that instead the existing VmReconfigure be moved to VmInfraReconfigure and it can share a common base class with resize (we can call it VmCloudReconfigure). It may be that once the quota impl is done it will be clearer how much logic we can pull into the common base classes.

I'll go back into this PR and figure out what can be renamed here. I'm thinking that I can just go ahead and call the MiqRequest subclasses VmCloudReconfigure instead of VmResize, but without messing with trying to create a new common subclass now. Also, I'll probably leave the existing model-side "resize" functionality as-is, since that's not really being changed by this PR.


def do_request
_log.info("Resizing VM #{vm.id}:#{vm.name} to flavor #{options[:flavor_name]}")
vm.resize_queue(miq_request.userid, options[:flavor_id])
Copy link
Member

Choose a reason for hiding this comment

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

Please store the options[:flavor_id] as options[:instance_type] so it matches how the data is stored from the provisioning dialogs.

Example:
https://github.com/ManageIQ/manageiq/blob/master/product/dialogs/miq_dialogs/miq_provision_amazon_dialogs_template.yaml#L290

This will assist in quota validation which shares common logic with provisioning.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ahh. I somehow missed this one. Working on it...

@sseago
Copy link
Contributor Author

sseago commented Oct 12, 2017

@gmcculloug I've made the suggested changes (changing "resize" to "cloud reconfigure", removing resize_queue, etc). See my comment on the associated UI PR about auto-approval, though.

@gmcculloug
Copy link
Member

@sseago I do not see any approval comment on the UI associated PR. Do you have a link?

@gmcculloug
Copy link
Member

Also this comment got lost: #16044 (comment)

Please store the options[:flavor_id] as options[:instance_type] so it matches how the data is stored from the provisioning dialogs.

Example:
https://github.com/ManageIQ/manageiq/blob/master/product/dialogs/miq_dialogs/miq_provision_amazon_dialogs_template.yaml#L290

This will assist in quota validation which shares common logic with provisioning.

@sseago
Copy link
Contributor Author

sseago commented Oct 12, 2017

@gmcculloug Ahh my comments were hidden because I accidentally clicked "review" instead of "comment" -- I think it's visible now: ManageIQ/manageiq-ui-classic#2241 (comment)

end

def do_request
_log.info("Reconfiguring VM #{vm.id}:#{vm.name} to flavor #{options[:flavor_name]}")
Copy link
Member

Choose a reason for hiding this comment

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

Sorry for not mentioning this before but for consistency with the :instance_type naming this fields should be instance_type_name.

Or maybe just store the ID and load the object to get the name so you do not have to pass both around and this naming consistency issue goes away.

else
name = req_obj.source.name
end
flavor = Flavor.find_by(:id => req_obj.options[:instance_type].first)
Copy link
Member

Choose a reason for hiding this comment

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

From ManageIQ/manageiq-ui-classic#2241 it looks like options[:instance_type] is passed as an ID, not an array. If it is an array I would be confused why since you can only select one.

I'm hoping you can drop the .first method. Also, you can protect against getting a nil object returned and raise an error which makes more sense then allowing the process continue with an invalid flavor.
flavor = Flavor.find_by!(:id => req_obj.options[:instance_type])

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Regarding instance_type, it looks like you're correct -- that's passed in as a single ID. I'll fix that. I don't think we should raise an exception in this get_description method, but we certainly should raise an exception in the do_request method below. I'll fix that one.

name = req_obj.source.name
end
flavor = Flavor.find_by(:id => req_obj.options[:instance_type].first)
flavor_name = flavor.nil? ? "" : flavor.name
Copy link
Member

Choose a reason for hiding this comment

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

You can use try here: flavor_name = flavor.try(:name) Since this is only used once you could drop this line and just reference directly below. But I'm confused why flavor would ever be nil here. Is this really needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@gmcculloug On the question of checking for nil vs. raising an exception, we've got 2 places where I"m doing this here. This one is for get_description. This is a method that shouldn't raise an exception if it can't find a flavor, so I think what's happening here is correct. For the second one, in the do_request call, if the flavor isn't found, then we probably should raise an exception -- I'll modify the second one to raise an exception if flavor isn't found in the find.


new_settings = []
new_settings << "Flavor: #{flavor_name}"
"#{request_class::TASK_DESCRIPTION} for: #{name} - #{new_settings.join(", ")}"
Copy link
Member

Choose a reason for hiding this comment

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

We are building an array to call join on an object that we know will only contain one thing so this can be simplified. With the comment above about not needing to protect flavor being nil it would reduce to:

-    new_settings = []
-    new_settings << "Flavor: #{flavor_name}"
-    "#{request_class::TASK_DESCRIPTION} for: #{name} - #{new_settings.join(", ")}"
+ "#{request_class::TASK_DESCRIPTION} for: #{name} - Flavor: #{flavor.name}"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right. The code that this was modeled after had multiple params to join -- when the others were removed, it should have been simplified like this.

def do_request
flavor = Flavor.find_by(:id => options[:instance_type].first)
flavor_name = flavor.nil? ? "" : flavor.name
_log.info("Reconfiguring VM #{vm.id}:#{vm.name} to flavor #{flavor_name}")
Copy link
Member

Choose a reason for hiding this comment

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

Similar to before, it feels weird that we are protecting against flavor being nil but we are willing to pass an invalid ID when we call vm.resize(options[:instance_type]).

Is this needed? Otherwise we probably want to raise an error which you can do using find_by!

So I'm hoping this logic become something like:

def do_request
  flavor = Flavor.find_by!(:id => options[:instance_type])
  _log.info("Reconfiguring VM #{vm.id}:#{vm.name} to flavor #{flavor.name}")
  ...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, this is the right way to do it here.

@miq-bot
Copy link
Member

miq-bot commented Oct 25, 2017

Checked commit sseago@b415823 with ruby 2.3.3, rubocop 0.47.1, and haml-lint 0.20.0
5 files checked, 0 offenses detected
Everything looks fine. 🏆

@jrafanie
Copy link
Member

Kicking the build after merge of #16304

@jrafanie jrafanie closed this Oct 25, 2017
@jrafanie jrafanie reopened this Oct 25, 2017
@gmcculloug gmcculloug merged commit ed35ee7 into ManageIQ:master Oct 25, 2017
@gmcculloug gmcculloug added this to the Sprint 72 Ending Oct 30, 2017 milestone Oct 25, 2017
@gmcculloug
Copy link
Member

@billfitzgerald0120 Please link to this PR when you have a PR in the content repo for approval.

billfitzgerald0120 added a commit to billfitzgerald0120/manageiq-content that referenced this pull request Oct 26, 2017
This will set auto approval for instance resize/reconfiguration.

Depends on core PR ManageIQ/manageiq#16044
Depends on UI PR ManageIQ/manageiq-ui-classic#2241
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.

None yet

7 participants