diff --git a/app/models/miq_request.rb b/app/models/miq_request.rb index 03f57cf437b..352236e606c 100644 --- a/app/models/miq_request.rb +++ b/app/models/miq_request.rb @@ -4,6 +4,11 @@ class MiqRequest < ApplicationRecord ACTIVE_STATES = %w(active queued) REQUEST_UNIQUE_KEYS = %w(id state status created_on updated_on type).freeze + CANCEL_STATUS_REQUESTED = "cancel_requested".freeze + CANCEL_STATUS_PROCESSING = "canceling".freeze + CANCEL_STATUS_FINISHED = "canceled".freeze + CANCEL_STATUS = [CANCEL_STATUS_REQUESTED, CANCEL_STATUS_PROCESSING, CANCEL_STATUS_FINISHED].freeze + belongs_to :source, :polymorphic => true belongs_to :destination, :polymorphic => true belongs_to :requester, :class_name => "User" @@ -26,6 +31,10 @@ class MiqRequest < ApplicationRecord validates_inclusion_of :approval_state, :in => %w(pending_approval approved denied), :message => "should be 'pending_approval', 'approved' or 'denied'" validates_inclusion_of :status, :in => %w(Ok Warn Error Timeout Denied) + validates :cancelation_status, :inclusion => { :in => CANCEL_STATUS, + :allow_nil => true, + :message => "should be one of #{CANCEL_STATUS.join(", ")}" } + validate :validate_class, :validate_request_type include TenancyMixin @@ -54,6 +63,7 @@ class MiqRequest < ApplicationRecord scope :created_recently, ->(days_ago) { where("miq_requests.created_on > ?", days_ago.days.ago) } scope :with_approval_state, ->(state) { where(:approval_state => state) } + scope :with_cancel_status, ->(status) { where(:cancelation_status => status) } scope :with_type, ->(type) { where(:type => type) } scope :with_request_type, ->(type) { where(:request_type => type) } scope :with_requester, ->(id) { where(:requester_id => User.with_same_userid(id).collect(&:id)) } @@ -432,6 +442,11 @@ def execute end def create_request_tasks + if cancel_requested? + do_cancel + return + end + # Quota denial will result in automate_event_failed? being true return if automate_event_failed?("request_starting") @@ -589,12 +604,30 @@ def cancel raise _("Cancel operation is not supported for #{self.class.name}") end + def cancel_requested? + cancelation_status == CANCEL_STATUS_REQUESTED + end + def canceling? - false + cancelation_status == CANCEL_STATUS_PROCESSING + end + + def canceled? + cancelation_status == CANCEL_STATUS_FINISHED end private + def do_cancel + update_attributes(:cancelation_status => CANCEL_STATUS_PROCESSING) + cancel_cleanup + update_attributes(:cancelation_status => CANCEL_STATUS_FINISHED, :request_state => "finished", :status => "Error", :message => "Request is canceled by user.") + _log.info("Request #{description} is canceled by user.") + end + + def cancel_cleanup + end + def clean_up_keys_for_request_task req_task_attributes = attributes.dup (req_task_attributes.keys - MiqRequestTask.column_names + REQUEST_UNIQUE_KEYS).each { |key| req_task_attributes.delete(key) } diff --git a/app/models/miq_request_task.rb b/app/models/miq_request_task.rb index d09f0747c47..9270c775e32 100644 --- a/app/models/miq_request_task.rb +++ b/app/models/miq_request_task.rb @@ -25,6 +25,15 @@ class MiqRequestTask < ApplicationRecord include MiqRequestMixin include TenancyMixin + CANCEL_STATUS_REQUESTED = "cancel_requested".freeze + CANCEL_STATUS_PROCESSING = "canceling".freeze + CANCEL_STATUS_FINISHED = "canceled".freeze + CANCEL_STATUS = [CANCEL_STATUS_REQUESTED, CANCEL_STATUS_PROCESSING, CANCEL_STATUS_FINISHED].freeze + + validates :cancelation_status, :inclusion => { :in => CANCEL_STATUS, + :allow_nil => true, + :message => "should be one of #{CANCEL_STATUS.join(", ")}" } + def approved? if miq_request.class.name.include?('Template') && miq_request_task miq_request_task.miq_request.approved? @@ -208,8 +217,16 @@ def cancel raise _("Cancel operation is not supported for #{self.class.name}") end + def cancel_requested? + cancelation_status == MiqRequestTask::CANCEL_STATUS_REQUESTED + end + def canceling? - false + cancelation_status == MiqRequestTask::CANCEL_STATUS_PROCESSING + end + + def canceled? + cancelation_status == MiqRequestTask::CANCEL_STATUS_FINISHED end private diff --git a/app/models/service_template_provision_request.rb b/app/models/service_template_provision_request.rb index 87fedbf22b6..7ccf8f06266 100644 --- a/app/models/service_template_provision_request.rb +++ b/app/models/service_template_provision_request.rb @@ -25,6 +25,11 @@ class ServiceTemplateProvisionRequest < MiqRequest include MiqProvisionQuotaMixin def process_service_order + if cancel_requested? + do_cancel + return + end + case options[:cart_state] when ServiceOrder::STATE_ORDERED ServiceOrder.order_immediately(self, requester) diff --git a/app/models/service_template_transformation_plan_request.rb b/app/models/service_template_transformation_plan_request.rb index 2026f136a4a..53d22b034f7 100644 --- a/app/models/service_template_transformation_plan_request.rb +++ b/app/models/service_template_transformation_plan_request.rb @@ -25,12 +25,7 @@ def approve_vm(vm_id) end def cancel - options['cancel_requested'] = true - save! + update_attributes(:cancelation_status => MiqRequest::CANCEL_STATUS_REQUESTED) miq_request_tasks.each(&:cancel) end - - def canceling? - options['cancel_requested'] - end end diff --git a/app/models/service_template_transformation_plan_task.rb b/app/models/service_template_transformation_plan_task.rb index ba3db479472..000ba171a93 100644 --- a/app/models/service_template_transformation_plan_task.rb +++ b/app/models/service_template_transformation_plan_task.rb @@ -103,12 +103,15 @@ def transformation_log_queue(userid = nil) end def cancel - options['cancel_requested'] = true - save! + update_attributes(:cancelation_status => MiqRequestTask::CANCEL_STATUS_REQUESTED) end - def canceling? - options['cancel_requested'] + def canceling + update_attributes(:cancelation_status => MiqRequestTask::CANCEL_STATUS_PROCESSING) + end + + def canceled + update_attributes(:cancelation_status => MiqRequestTask::CANCEL_STATUS_FINISHED) end private diff --git a/spec/models/service_template_transformation_plan_request_spec.rb b/spec/models/service_template_transformation_plan_request_spec.rb index 69887b9b657..d9c718958dd 100644 --- a/spec/models/service_template_transformation_plan_request_spec.rb +++ b/spec/models/service_template_transformation_plan_request_spec.rb @@ -38,4 +38,23 @@ expect(ServiceResource.find_by(:resource => vms[0]).status).to eq(ServiceResource::STATUS_APPROVED) end end + + context "when request gets canceled" do + before { request.cancel } + + it "cancelation_status is set to requested" do + expect(request.cancelation_status).to eq(MiqRequest::CANCEL_STATUS_REQUESTED) + expect(request.cancel_requested?).to be_truthy + expect(request.canceling?).to be_falsey + expect(request.canceled?).to be_falsey + end + + it "marks request as finished in error" do + request.send(:do_cancel) + expect(request.cancelation_status).to eq(MiqRequest::CANCEL_STATUS_FINISHED) + expect(request.request_state).to eq('finished') + expect(request.status).to eq('Error') + expect(request.message).to eq('Request is canceled by user.') + end + end end diff --git a/spec/models/service_template_transformation_plan_task_spec.rb b/spec/models/service_template_transformation_plan_task_spec.rb index 12130dcdf10..f379db74cfb 100644 --- a/spec/models/service_template_transformation_plan_task_spec.rb +++ b/spec/models/service_template_transformation_plan_task_spec.rb @@ -196,5 +196,13 @@ expect(vm).to be_is_tagged_with("migrated", :ns => "/managed", :cat => "transformation_status") end end + + describe '#cancel' do + it 'catches cancel state' do + task.cancel + expect(task.cancelation_status).to eq(MiqRequestTask::CANCEL_STATUS_REQUESTED) + expect(task.cancel_requested?).to be_truthy + end + end end end