-
Notifications
You must be signed in to change notification settings - Fork 69
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #629 from lfu/content_library_service
Service for VMware content library item deployment.
- Loading branch information
Showing
6 changed files
with
356 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
140 changes: 140 additions & 0 deletions
140
app/models/manageiq/providers/vmware/infra_manager/ovf_service.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
class ManageIQ::Providers::Vmware::InfraManager::OvfService < ServiceGeneric | ||
delegate :ovf_template, :manager, :to => :service_template, :allow_nil => true | ||
|
||
# A chance for taking options from automate script to override options from a service dialog | ||
def preprocess(action, new_options = {}) | ||
return unless action == ResourceAction::PROVISION | ||
|
||
if new_options.present? | ||
_log.info("Override with new options:\n#{new_options}") | ||
end | ||
|
||
save_action_options(action, new_options) | ||
end | ||
|
||
def execute(action) | ||
return unless action == ResourceAction::PROVISION | ||
|
||
deploy_library_item_queue | ||
end | ||
|
||
def deploy_library_item_queue | ||
task_options = {:action => "Deploying VMware Content Library Item", :userid => "system"} | ||
queue_options = { | ||
:class_name => self.class.name, | ||
:instance_id => id, | ||
:method_name => "deploy_library_item", | ||
:args => {}, | ||
:role => "ems_operations", | ||
:queue_name => manager.queue_name_for_ems_operations, | ||
:zone => manager.my_zone | ||
} | ||
|
||
task_id = MiqTask.generic_action_with_callback(task_options, queue_options) | ||
update(:options => options.merge(:deploy_task_id => task_id)) | ||
end | ||
|
||
def deploy_library_item(_options) | ||
_log.info("OVF template provisioning with template ID: [#{ovf_template.id}] name:[#{ovf_template.name}] was initiated.") | ||
opts = provision_options | ||
_log.info("VMware Content Library OVF Tempalte provisioning with options:\n#{opts}") | ||
|
||
@deploy_response = ovf_template.deploy(opts).to_hash | ||
_log.info("Content Library request response: #{@deploy_response}") | ||
update(:options => options.merge(:deploy_response => @deploy_response)) | ||
rescue VSphereAutomation::ApiError => e | ||
_log.error("Failed to deploy content library template(#{ovf_template.name}), error: #{e}") | ||
raise MiqException::MiqOrchestrationProvisionError, "Content library OVF template deployment failed: #{e}" | ||
end | ||
|
||
def check_completed(action) | ||
return [true, 'not supported'] unless action == ResourceAction::PROVISION | ||
return [false, nil] if deploy_task.state != "Finished" | ||
|
||
message = deploy_response&.dig(:value, :succeeded) ? nil : deploy_response&.dig(:value, :error).to_json || deploy_response&.to_json || deploy_task.message | ||
[true, message] | ||
end | ||
|
||
def refresh(_action) | ||
end | ||
|
||
def check_refreshed(action) | ||
return [true, nil] unless deploy_response&.dig(:value, :succeeded) | ||
|
||
dest = find_destination_in_vmdb | ||
if dest | ||
add_resource!(dest, :name => action) | ||
|
||
if dest.kind_of?(ResourcePool) | ||
dest.vms.each { |vm| add_resource!(vm, :name => action) } | ||
end | ||
|
||
[true, nil] | ||
else | ||
[false, nil] | ||
end | ||
end | ||
|
||
private | ||
|
||
def deploy_response | ||
@deploy_response ||= options[:deploy_response] | ||
end | ||
|
||
def deploy_task | ||
@deploy_task ||= MiqTask.find_by(:id => options[:deploy_task_id]) | ||
end | ||
|
||
def find_destination_in_vmdb | ||
target_model_class.find_by(:ems_id => manager.id, :ems_ref => deploy_response.dig(:value, :resource_id, :id)) | ||
end | ||
|
||
def target_model_class | ||
case deploy_response.dig(:value, :resource_id, :type) | ||
when "VirtualMachine" | ||
manager.class::Vm | ||
when "VirtualApp" | ||
manager.class::ResourcePool | ||
end | ||
end | ||
|
||
def get_action_options(action) | ||
options[action_option_key(action)] | ||
end | ||
|
||
def provision_options | ||
@provision_options ||= get_action_options(ResourceAction::PROVISION) | ||
end | ||
|
||
def save_action_options(action, overrides) | ||
return unless action == ResourceAction::PROVISION | ||
|
||
action_options = options.fetch_path(:config_info, action.downcase.to_sym).with_indifferent_access | ||
%w[resource_pool ems_folder host].each do |r| | ||
next if action_options[r].blank? | ||
|
||
action_options["#{r}_id"] = action_options.delete(r).split.first.to_i | ||
end | ||
|
||
action_options.deep_merge!(parse_dialog_options) | ||
action_options.deep_merge!(overrides) | ||
|
||
options[action_option_key(action)] = action_options | ||
save! | ||
end | ||
|
||
def parse_dialog_options | ||
dialog_options = options[:dialog] || {} | ||
options = {:vm_name => dialog_options['dialog_vm_name']} | ||
options[:accept_all_EULA] = dialog_options['dialog_accept_all_EULA'] == 't' | ||
|
||
%w[resource_pool ems_folder host storage].each do |r| | ||
options["#{r}_id"] = dialog_options["dialog_#{r}"].split.first.to_i if dialog_options["dialog_#{r}"].present? | ||
end | ||
options | ||
end | ||
|
||
def action_option_key(action) | ||
"#{action.downcase}_options".to_sym | ||
end | ||
end |
81 changes: 81 additions & 0 deletions
81
app/models/manageiq/providers/vmware/infra_manager/ovf_service_template.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
class ManageIQ::Providers::Vmware::InfraManager::OvfServiceTemplate < ServiceTemplateGeneric | ||
def self.default_provisioning_entry_point(_service_type) | ||
'/Service/Generic/StateMachines/GenericLifecycle/provision' | ||
end | ||
|
||
# create ServiceTemplate and supporting ServiceResources and ResourceActions | ||
# options | ||
# :name | ||
# :description | ||
# :service_template_catalog_id | ||
# :config_info | ||
# :provision | ||
# :dialog_id or :dialog | ||
# :ovf_template_id or :ovf_template | ||
# | ||
def self.create_catalog_item(options, _auth_user) | ||
options = options.merge(:service_type => 'atomic', :prov_type => 'generic_ovf_template') | ||
config_info = validate_config_info(options[:config_info]) | ||
enhanced_config = config_info.deep_merge( | ||
:provision => { | ||
:configuration_template => ovf_template_from_config_info(config_info) | ||
} | ||
) | ||
|
||
transaction do | ||
create_from_options(options).tap do |service_template| | ||
service_template.create_resource_actions(enhanced_config) | ||
end | ||
end | ||
end | ||
|
||
def self.validate_config_info(info) | ||
info[:provision][:fqname] ||= default_provisioning_entry_point(SERVICE_TYPE_ATOMIC) if info.key?(:provision) | ||
|
||
# TODO: Add more validation for required fields | ||
info | ||
end | ||
private_class_method :validate_config_info | ||
|
||
def self.ovf_template_from_config_info(info) | ||
ovf_template_id = info[:provision][:ovf_template_id] | ||
ovf_template_id ? OrchestrationTemplate.find(ovf_template_id) : info[:provision][:ovf_template] | ||
end | ||
private_class_method :ovf_template_from_config_info | ||
|
||
def ovf_template | ||
@ovf_template ||= resource_actions.find_by(:action => "Provision").try(:configuration_template) | ||
end | ||
|
||
def manager | ||
@manager ||= ovf_template.try(:ext_management_system) | ||
end | ||
|
||
def update_catalog_item(options) | ||
config_info = validate_update_config_info(options) | ||
config_info[:provision][:configuration_template] ||= ovf_template_from_config_info(config_info) if config_info.key?(:provision) | ||
options[:config_info] = config_info | ||
|
||
super | ||
end | ||
|
||
private | ||
|
||
def ovf_template_from_config_info(info) | ||
self.class.send(:ovf_template_from_config_info, info) | ||
end | ||
|
||
def validate_update_config_info(options) | ||
opts = super | ||
self.class.send(:validate_config_info, opts) | ||
end | ||
|
||
def update_service_resources(_config_info, _auth_user = nil) | ||
# do nothing since no service resources for this template | ||
end | ||
|
||
def update_from_options(params) | ||
options[:config_info] = Hash[params[:config_info].collect { |k, v| [k, v.except(:configuration_template)] }] | ||
update!(params.except(:config_info)) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
FactoryBot.define do | ||
factory :service_ovf, :class => "ManageIQ::Providers::Vmware::InfraManager::OvfService", :parent => :service | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
FactoryBot.define do | ||
factory :service_template_ovf, :class => "ManageIQ::Providers::Vmware::InfraManager::OvfServiceTemplate", :parent => :service_template | ||
end |
128 changes: 128 additions & 0 deletions
128
spec/models/manageiq/providers/vmware/infra_manager/ovf_service_spec.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
require 'vsphere-automation-vcenter' | ||
|
||
describe(ManageIQ::Providers::Vmware::InfraManager::OvfService) do | ||
let(:action) { ResourceAction::PROVISION } | ||
let(:ovf_template) { FactoryBot.create(:orchestration_template_vmware_infra) } | ||
|
||
let(:ems) { FactoryBot.create(:ems_vmware) } | ||
|
||
let(:service) do | ||
FactoryBot.create(:service_ovf, :options => config_info_options.merge(dialog_options)).tap do |svc| | ||
allow(svc).to receive(:manager).and_return(ems) | ||
end | ||
end | ||
|
||
let(:loaded_service) do | ||
service_template = FactoryBot.create(:service_template_ovf).tap do |st| | ||
allow(st).to receive(:manager).and_return(ems) | ||
end | ||
|
||
FactoryBot.create(:service_ovf, | ||
:options => {:provision_options => provision_options}.merge(config_info_options), | ||
:service_template => service_template).tap do |svc| | ||
allow(svc).to receive(:ovf_template).and_return(ovf_template) | ||
end | ||
end | ||
|
||
let(:dialog_options) do | ||
{ | ||
:dialog => { | ||
"dialog_vm_name" => "dialog_vm_name", | ||
"dialog_resource_pool" => "5 test resource pool", | ||
"dialog_ems_folder" => "30 lucy", | ||
} | ||
} | ||
end | ||
|
||
let(:config_info_options) do | ||
{ | ||
:config_info => { | ||
:provision => { | ||
:dialog_id => "2", | ||
:ovf_template_id => ovf_template.id, | ||
:vm_name => "template_vm_name", | ||
:accept_all_EULA => true, | ||
:resource_pool => "2 Default for Cluster dev-vc67-cluster", | ||
:ems_folder => "3 test_folder", | ||
:host => "1 test_host" | ||
} | ||
} | ||
} | ||
end | ||
|
||
let(:override_options) { {:vm_name => 'override_vm_name'} } | ||
|
||
let(:provision_options) do | ||
{ | ||
"ovf_template_id" => ovf_template.id, | ||
"dialog_id" => "2", | ||
"vm_name" => "dialog_vm_name", | ||
"accept_all_EULA" => false, | ||
"resource_pool_id" => 5, | ||
"ems_folder_id" => 30 | ||
} | ||
end | ||
|
||
let(:failed_response ) { {:value => {:succeeded => false, :error=>{:errors=>[{:category=>"SERVER", :error=>{:@class=>"com.vmware.vapi.std.errors.already_exists", :messages=>[{:args=>["VirtualMachine", "lucy-api-vm-2"], :default_message=>"An object of type \"VirtualMachine\" named \"lucy-api-vm-2\" already exists.", :id=>"com.vmware.vdcs.util.duplicate_name"}]}}], :warnings=>[], :information=>[]}}} } | ||
|
||
let(:deploy_task) { FactoryBot.create(:miq_task, :state => "Active")} | ||
|
||
describe '#preprocess' do | ||
it 'prepares job options from dialog' do | ||
service.preprocess(action) | ||
expect(service.options[:provision_options]).to match a_hash_including(provision_options) | ||
end | ||
|
||
it 'prepares job options combined from dialog and overrides' do | ||
service.preprocess(action, override_options) | ||
expect(service.options[:provision_options]).to match a_hash_including( | ||
"vm_name" => override_options[:vm_name] | ||
) | ||
end | ||
end | ||
|
||
describe '#deploy_library_item' do | ||
it 'Provisions with an ovf template' do | ||
expect(ovf_template).to receive(:deploy) do |options| | ||
expect(options).to eq(provision_options) | ||
failed_response | ||
end | ||
loaded_service.deploy_library_item(action) | ||
end | ||
end | ||
|
||
describe '#check_completed' do | ||
it 'created VM ends in VMDB' do | ||
deploy_task.update(:state => "Finished") | ||
loaded_service.update(:options => loaded_service.options.merge(:deploy_task_id => deploy_task.id)) | ||
loaded_service.update(:options => loaded_service.options.merge(:deploy_response => failed_response)) | ||
expect(loaded_service.check_completed(action)).to eq([true, failed_response.dig(:value, :error).to_json]) | ||
end | ||
|
||
it 'created VM not ends in VMDB yet' do | ||
loaded_service.update(:options => loaded_service.options.merge(:deploy_task_id => deploy_task.id)) | ||
expect(loaded_service.check_completed(action)).to eq([false, nil]) | ||
end | ||
end | ||
|
||
describe '#check_refreshed' do | ||
it 'successful deployment response ' do | ||
response = {:value => {:succeeded => true, :resource_id=>{:type=>"VirtualMachine", :id=>"vm-934"}}} | ||
loaded_service.update(:options => loaded_service.options.merge(:deploy_response => response)) | ||
expect(loaded_service.check_refreshed(action)).to eq([false, nil]) | ||
|
||
FactoryBot.create(:vm_vmware, :ems_ref_type => "VirtualMachine", :ems_ref => "vm-934", :ems_id => ems.id) | ||
expect(loaded_service.check_refreshed(action)).to eq([true, nil]) | ||
end | ||
|
||
it 'no successful deployment response ' do | ||
response = {:value => {:succeeded => false}} | ||
loaded_service.update(:options => loaded_service.options.merge(:deploy_response => response)) | ||
expect(loaded_service.check_refreshed(action)).to eq([true, nil]) | ||
end | ||
|
||
it 'no deployment response' do | ||
expect(loaded_service.check_refreshed(action)).to eq([true, nil]) | ||
end | ||
end | ||
end |