Skip to content

Commit

Permalink
Merge pull request #16082 from lfu/service_resource_linking
Browse files Browse the repository at this point in the history
Add Service resource linking.
  • Loading branch information
gmcculloug authored Oct 12, 2017
2 parents 4f568e4 + 4146352 commit d4da91c
Show file tree
Hide file tree
Showing 5 changed files with 263 additions and 0 deletions.
1 change: 1 addition & 0 deletions app/models/service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class Service < ApplicationRecord

include_concern 'RetirementManagement'
include_concern 'Aggregation'
include_concern 'ResourceLinking'

virtual_column :has_parent, :type => :boolean
virtual_column :power_state, :type => :string
Expand Down
80 changes: 80 additions & 0 deletions app/models/service/linking_workflow.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
class Service
class LinkingWorkflow < ManageIQ::Providers::EmsRefreshWorkflow
def load_transitions
self.state ||= 'initialize'

{
:initializing => {'initialize' => 'waiting_to_start'},
:start => {'waiting_to_start' => 'running'},
:refresh => {'running' => 'refreshing'},
:poll_refresh => {'refreshing' => 'refreshing'},
:post_refresh => {
'running' => 'post_refreshing',
'refreshing' => 'post_refreshing'
},
:finish => {'*' => 'finished'},
:abort_job => {'*' => 'aborting'},
:cancel => {'*' => 'canceling'},
:error => {'*' => '*'}
}
end

def run_native_op
_log.info("Enter")

unless linking_service
msg = "Job [%{id}] [%{name}] aborted: didn't find service ID: [%{service_id}] to link to" % {
:id => id, :name => name, :service_id => options[:service_id]
}
_log.error(msg)
signal(:abort, msg, 'error')
end

unless target_entity
msg = "Job [%{id}] [%{name}] aborted: didn't find provider class: [%{target_class}] ID: [%{target_id}] to refresh" % {
:id => id, :name => name, :target_class => target_class, :target_id => target_id
}
_log.error(msg)
signal(:abort, msg, 'error')
end

if find_all_targets?
set_status("all VMs are found in DB")
signal(:post_refresh)
else
set_status("calling refresh")
queue_signal(:refresh)
end
end
alias_method :start, :run_native_op

def post_refresh
_log.info("Enter")

found_vms = linking_targets
not_found_vms = options[:uid_ems_array] - found_vms.pluck(:uid_ems)
_log.warn("VMs not found for linking to service ID [#{service.id}], name [#{service.name}]: #{not_found_vms}") unless not_found_vms.blank?

service = linking_service
found_vms.each { |vm| service.add_resource!(vm) }
signal(:finish, "linking VMs to service is complete", "ok")
rescue => err
_log.log_backtrace(err)
signal(:abort, err.message, "error")
end

private

def find_all_targets?
linking_targets.length == options[:uid_ems_array].length
end

def linking_targets
@linking_targets ||= VmOrTemplate.where(:uid_ems => options[:uid_ems_array], :ems_id => target_id)
end

def linking_service
@linking_service ||= Service.find_by(:id => options[:service_id])
end
end
end
28 changes: 28 additions & 0 deletions app/models/service/resource_linking.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
module Service::ResourceLinking
extend ActiveSupport::Concern

def add_provider_vms(provider, uid_ems_array)
vm_uid_array = Array(uid_ems_array).compact.uniq
raise _("no uid_ems_array defined for linking to service") if vm_uid_array.blank?

options = {
:target_class => provider.class.name,
:target_id => provider.id,
:uid_ems_array => vm_uid_array,
:name => "Linking VMs to service #{name} ID: [#{id}]",
:userid => evm_owner.userid,
:sync_key => guid,
:service_id => id,
:zone => my_zone
}

_log.info("NAME [#{options[:name]}] for user #{evm_owner.userid}")

Service::LinkingWorkflow.create_job(options).tap do |job|
job.signal(:start)
end
rescue => err
_log.log_backtrace(err)
raise
end
end
135 changes: 135 additions & 0 deletions spec/models/service/linking_workflow_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
describe Service::LinkingWorkflow do
let(:service) { FactoryGirl.create(:service) }
let(:provider) { FactoryGirl.create(:ems_vmware) }
let(:uid_ems_array) { ["423c9963-378c-813f-1dbd-630e464d59d4", "423cf3e2-e319-3953-993f-fd8513db951d"] }
let(:options) do
{
:target_class => provider.class.name,
:target_id => provider.id,
:uid_ems_array => uid_ems_array,
:service_id => service.id
}
end
let(:job) { described_class.create_job(options) }

context 'run_native_op' do
subject { job.run_native_op }

it 'raises an error if service is not found' do
options[:service_id] = 999
expect(job).to receive(:signal).with(:abort, "Job [#{job.id}] [#{job.name}] aborted: didn't find service ID: [999] to link to", "error")
subject
end

it 'raises an error if provider is not found' do
options[:target_id] = 999
msg = "Job [#{job.id}] [#{job.name}] aborted: didn't find provider class: [#{provider.class.name}] ID: [999] to refresh"
expect(job).to receive(:signal).with(:abort, msg, "error")
subject
end

it 'calls refresh if not all VMs found in DB' do
expect(job).to receive(:queue_signal).with(:refresh)
subject
end

it 'calls post_refresh if all VMs found in DB' do
uid_ems_array.each { |uid| FactoryGirl.create(:vm_vmware, :uid_ems => uid, :ems_id => provider.id) }
expect(job).to receive(:signal).with(:post_refresh)
subject
end
end

context 'post_refresh' do
subject { job.post_refresh }

it 'links found VMs to service' do
uid_ems_array.each { |uid| FactoryGirl.create(:vm_vmware, :uid_ems => uid, :ems_id => provider.id) }
subject
expect(service.vms.count).to eq(2)
end
end

context 'state transitions' do
%w(start refresh poll_refresh post_refresh finish abort_job cancel error).each do |signal|
shared_examples_for "allows #{signal} signal" do
it signal.to_s do
expect(job).to receive(signal.to_sym)
job.signal(signal.to_sym)
end
end
end

%w(start refresh poll_refresh post_refresh).each do |signal|
shared_examples_for "doesn't allow #{signal} signal" do
it signal.to_s do
expect { job.signal(signal.to_sym) }.to raise_error(RuntimeError, /#{signal} is not permitted at state #{job.state}/)
end
end
end

context 'waiting_to_start' do
before do
job.state = 'waiting_to_start'
end

it_behaves_like 'allows start signal'
it_behaves_like 'allows finish signal'
it_behaves_like 'allows abort_job signal'
it_behaves_like 'allows cancel signal'
it_behaves_like 'allows error signal'

it_behaves_like 'doesn\'t allow refresh signal'
it_behaves_like 'doesn\'t allow poll_refresh signal'
it_behaves_like 'doesn\'t allow post_refresh signal'
end

context 'running' do
before do
job.state = 'running'
end

it_behaves_like 'allows refresh signal'
it_behaves_like 'allows post_refresh signal'
it_behaves_like 'allows finish signal'
it_behaves_like 'allows abort_job signal'
it_behaves_like 'allows cancel signal'
it_behaves_like 'allows error signal'

it_behaves_like 'doesn\'t allow start signal'
it_behaves_like 'doesn\'t allow poll_refresh signal'
end

context 'refreshing' do
before do
job.state = 'refreshing'
end

it_behaves_like 'allows poll_refresh signal'
it_behaves_like 'allows post_refresh signal'
it_behaves_like 'allows finish signal'
it_behaves_like 'allows abort_job signal'
it_behaves_like 'allows cancel signal'
it_behaves_like 'allows error signal'

it_behaves_like 'doesn\'t allow start signal'
it_behaves_like 'doesn\'t allow refresh signal'
end

context 'post_refreshing' do
before do
job.state = 'post_refreshing'
end

it_behaves_like 'allows finish signal'
it_behaves_like 'allows abort_job signal'
it_behaves_like 'allows cancel signal'
it_behaves_like 'allows error signal'

it_behaves_like 'doesn\'t allow start signal'
it_behaves_like 'doesn\'t allow refresh signal'
it_behaves_like 'doesn\'t allow poll_refresh signal'
it_behaves_like 'doesn\'t allow post_refresh signal'
end
end
end
19 changes: 19 additions & 0 deletions spec/models/service/resource_linking_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
describe Service do
describe '#add_provider_vms' do
let(:service) { FactoryGirl.create(:service, :evm_owner => FactoryGirl.create(:user)) }
let(:provider) { FactoryGirl.create(:ems_vmware) }
let(:uid_ems_array) { ["423c9963-378c-813f-1dbd-630e464d59d4", "423cf3e2-e319-3953-993f-fd8513db951d"] }

it 'raises an error if uid_ems_array is not passed in' do
expect { service.add_provider_vms(provider, []) }.to raise_error(RuntimeError, "no uid_ems_array defined for linking to service")
end

it 'creates a Service::LinkingWorkflow job' do
expect(Service::LinkingWorkflow).to receive(:create_job) do |args|
expect(args).to match(hash_including(:target_class => provider.class.name, :target_id => provider.id))
expect(args).to match(hash_including(:uid_ems_array => array_including(uid_ems_array)))
end.and_return(double(:signal => :start))
service.add_provider_vms(provider, uid_ems_array)
end
end
end

0 comments on commit d4da91c

Please sign in to comment.