Skip to content

Commit

Permalink
Introduce Request and Task for firmware update
Browse files Browse the repository at this point in the history
We're introducing a new operation on physical server: firmware update.
We want to use state machine-driven Request for it because it's a long
running procedure and we want to be doing it on more than one server
with a single click (and then track its progress).

That being said - the PR contains a new Request class and a new
Task class (with internal state machine). The actual state machine
implementation is done in provider codebase - depending on class of
the selected physical server.

Signed-off-by: Miha Pleško <miha.plesko@xlab.si>
  • Loading branch information
miha-plesko committed May 23, 2019
1 parent 344a3b3 commit 88f0eb0
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 2 deletions.
4 changes: 4 additions & 0 deletions app/models/manageiq/providers/physical_infra_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ def console_url
raise MiqException::Error, _("Console not supported")
end

def self.firmware_update_class
self::FirmwareUpdate
end

def self.display_name(number = 1)
n_('Physical Infrastructure Manager', 'Physical Infrastructure Managers', number)
end
Expand Down
3 changes: 3 additions & 0 deletions app/models/miq_request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ class MiqRequest < ApplicationRecord
:PhysicalServerProvisionRequest => {
:provision_physical_server => N_("Physical Server Provision")
},
:PhysicalServerFirmwareUpdateRequest => {
:physical_server_firmware_update => N_("Physical Server Firmware Update")
},
:ServiceRetireRequest => {
:service_retire => N_("Service Retire")
},
Expand Down
16 changes: 16 additions & 0 deletions app/models/physical_server_firmware_update_request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class PhysicalServerFirmwareUpdateRequest < PhysicalServerProvisionRequest
TASK_DESCRIPTION = 'Physical Server Firmware Update'.freeze

def description
'Physical Server Firmware Update'
end

def self.request_task_class
PhysicalServerFirmwareUpdateTask
end

def self.new_request_task(attribs)
source = source_physical_server(attribs[:source_id])
source.ext_management_system.class.firmware_update_class.new(attribs)
end
end
21 changes: 21 additions & 0 deletions app/models/physical_server_firmware_update_task.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
class PhysicalServerFirmwareUpdateTask < MiqProvisionTask
include_concern 'StateMachine'

AUTOMATE_DRIVES = false

def description
'Physical Server Firmware Update'
end

def self.base_model
PhysicalServerFirmwareUpdateTask
end

def self.request_class
PhysicalServerFirmwareUpdateRequest
end

def model_class
PhysicalServer
end
end
36 changes: 36 additions & 0 deletions app/models/physical_server_firmware_update_task/state_machine.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
module PhysicalServerFirmwareUpdateTask::StateMachine
def run_provision
raise MiqException::MiqProvisionError, "Unable to find #{model_class} with id #{source_id.inspect}" if source.blank?

dump_obj(options, "MIQ(#{self.class.name}##{__method__}) options: ", $log, :info)
signal :start_firmware_update
end

def start_firmware_update
# Implement firmware update in subclass, user-defined values are stored in options field.
raise NotImplementedError, 'Must be implemented in subclass and signal :done_firmware_update when done'
end

def done_firmware_update
update_and_notify_parent(:message => msg('done updating firmware'))
signal :mark_as_completed
end

def mark_as_completed
update_and_notify_parent(:state => 'provisioned', :message => msg('firmware update completed'))
MiqEvent.raise_evm_event(source, 'generic_task_finish', :message => "Done updating firmware on PhysicalServer")
signal :finish
end

def finish
if status != 'Error'
_log.info("Executing provision task: [#{description}]... Complete")
else
_log.info("Executing provision task: [#{description}]... Errored")
end
end

def msg(txt)
"Updating firmware on PhysicalServer id=#{source.id}, name=#{source.name}, ems_ref=#{source.ems_ref}: #{txt}"
end
end
3 changes: 2 additions & 1 deletion spec/factories/miq_request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
factory :miq_provision_request, :class => "MiqProvisionRequest" do
source { create(:miq_template) }
end
factory :physical_server_provision_request, :class => "PhysicalServerProvisionRequest"
factory :physical_server_provision_request, :class => "PhysicalServerProvisionRequest"
factory :physical_server_firmware_update_request, :class => "PhysicalServerFirmwareUpdateRequest"

factory :service_template_transformation_plan_request, :class => "ServiceTemplateTransformationPlanRequest" do
source { create(:service_template_transformation_plan) }
Expand Down
3 changes: 2 additions & 1 deletion spec/factories/miq_request_task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
factory :miq_provision_openstack, :parent => :miq_provision_cloud, :class => "ManageIQ::Providers::Openstack::CloudManager::Provision"

# Physical Infrastructure
factory :physical_server_provision_task, :parent => :miq_provision, :class => "PhysicalServerProvisionTask"
factory :physical_server_provision_task, :parent => :miq_provision, :class => "PhysicalServerProvisionTask"
factory :physical_server_firmware_update_task, :parent => :miq_provision, :class => "PhysicalServerFirmwareUpdateTask"

# Automate
factory :automation_task, :parent => :miq_request_task, :class => "AutomationTask"
Expand Down
51 changes: 51 additions & 0 deletions spec/models/physical_server_firmware_update_request_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
describe PhysicalServerFirmwareUpdateRequest do
it '.TASK_DESCRIPTION' do
expect(described_class::TASK_DESCRIPTION).to eq('Physical Server Firmware Update')
end

it '.SOURCE_CLASS_NAME' do
expect(described_class::SOURCE_CLASS_NAME).to eq('PhysicalServer')
end

it '.request_task_class' do
expect(described_class.request_task_class).to eq(PhysicalServerFirmwareUpdateTask)
end

it '#description' do
expect(subject.description).to eq('Physical Server Firmware Update')
end

it '#my_role' do
expect(subject.my_role).to eq('ems_operations')
end

describe '.new_request_task' do
before do
allow(ems.class).to receive(:firmware_update_class).and_return(task)
end

let(:server) { FactoryBot.create(:physical_server, :ext_management_system => ems) }
let(:ems) { FactoryBot.create(:ems_physical_infra) }
let(:task) { double('TASK') }

context 'when source is ok' do
it do
expect(task).to receive(:new).with(:source_id => server.id)
described_class.new_request_task(:source_id => server.id)
end
end

context 'when source is missing' do
it do
expect { described_class.new_request_task(:source_id => 'missing') }.to raise_error(MiqException::MiqProvisionError)
end
end

context 'when source is lacking EMS' do
before { server.update!(:ext_management_system => nil) }
it do
expect { described_class.new_request_task(:source_id => server.id) }.to raise_error(MiqException::MiqProvisionError)
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
describe PhysicalServerFirmwareUpdateTask do
let(:server) { FactoryBot.create(:physical_server) }

subject { described_class.new(:source => server) }

describe '#run_provision' do
context 'when missing source' do
let(:server) { nil }
it do
expect { subject.run_provision }.to raise_error(MiqException::MiqProvisionError)
end
end

context 'when ok' do
it do
expect(subject).to receive(:signal).with(:start_firmware_update)
subject.run_provision
end
end
end

it '#done_firmware_update' do
expect(subject).to receive(:signal).with(:mark_as_completed)
expect(subject).to receive(:update_and_notify_parent)
subject.done_firmware_update
end

it '#mark_as_completed' do
expect(subject).to receive(:signal).with(:finish)
expect(subject).to receive(:update_and_notify_parent)
subject.mark_as_completed
end

describe '#finish' do
before { allow(subject).to receive(:_log).and_return(log) }

let(:log) { double('LOG') }

context 'when task has errored' do
before { subject.update_attribute(:status, 'Error') }
it do
expect(log).to receive(:info).with(satisfy { |msg| msg.include?('Errored') })
subject.finish
end
end

context 'when task has completed' do
before { subject.update_attribute(:status, 'Ok') }
it do
expect(log).to receive(:info).with(satisfy { |msg| msg.include?('... Complete') })
subject.finish
end
end
end
end
13 changes: 13 additions & 0 deletions spec/models/physical_server_firmware_update_task_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
describe PhysicalServerFirmwareUpdateTask do
it '#description' do
expect(subject.description).to eq('Physical Server Firmware Update')
end

it '#model_class' do
expect(subject.model_class).to eq(PhysicalServer)
end

it '.request_class' do
expect(described_class.request_class).to eq(PhysicalServerFirmwareUpdateRequest)
end
end

0 comments on commit 88f0eb0

Please sign in to comment.