From 7a5407ab6937d5fa72e9e3126e554a3fdab8f8ff Mon Sep 17 00:00:00 2001 From: Patrik Komanek Date: Thu, 6 Feb 2020 14:23:35 +0100 Subject: [PATCH 1/2] Refactoring vmware_customizerequest automate method. --- .../__methods__/vmware_customizerequest.rb | 348 +++++++++--------- 1 file changed, 180 insertions(+), 168 deletions(-) diff --git a/content/automate/ManageIQ/Infrastructure/VM/Provisioning/StateMachines/Methods.class/__methods__/vmware_customizerequest.rb b/content/automate/ManageIQ/Infrastructure/VM/Provisioning/StateMachines/Methods.class/__methods__/vmware_customizerequest.rb index 9e77e15a7..597714493 100644 --- a/content/automate/ManageIQ/Infrastructure/VM/Provisioning/StateMachines/Methods.class/__methods__/vmware_customizerequest.rb +++ b/content/automate/ManageIQ/Infrastructure/VM/Provisioning/StateMachines/Methods.class/__methods__/vmware_customizerequest.rb @@ -3,175 +3,187 @@ # Customization mapping for VMware and VMWare PXE provisioning # -def log(level, msg, update_message = false) - $evm.log(level, "#{msg}") - $evm.root['miq_provision'].message = "#{@method} - #{msg}" if $evm.root['miq_provision'] && update_message -end - -def set_customspec(prov, spec) - prov.set_customization_spec(spec, true) rescue nil - log(:info, "Provisioning object updated {:sysprep_custom_spec => #{prov.get_option(:sysprep_custom_spec).inspect rescue nil}}") - log(:info, "Provisioning object updated {:sysprep_spec_override => #{prov.get_option(:sysprep_spec_override)}}") -end - -# process_vmware -def process_vmware(mapping, prov, template, product, provider) -# if prov.get_option(:sysprep_custom_spec) || product.include?("other") || prov.provision_type.include?("clone_to_template") - if product.include?("other") || prov.provision_type.include?("clone_to_template") - mapping = 0 - end - - case mapping - - when 0 - # Skip mapping - - when 1 - # Automatic customization specification mapping if template is RHEL,Suse or Windows - if product.include?("red hat") || product.include?("suse") || product.include?("windows") - spec = prov.vm_template.name # to match the template name - set_customspec(prov, spec) - - # Set linux hostname stuff here - prov.set_option(:linux_host_name, prov.get_option(:vm_target_name)) - log(:info, "Provisioning object updated {:linux_host_name => #{prov.get_option(:linux_host_name)}}") - prov.set_option(:vm_target_hostname, prov.get_option(:vm_target_name)) - log(:info, "Provisioning object updated {:hostname => #{prov.get_option(:vm_target_hostname)}}") - end - - when 2 - # Use this option to use a combination of product name and bitness to select your customization specification - spec = nil # unknown type - - if product.include?("2003") - spec = "W2K3R2-Entx64" # Windows Server 2003 - elsif product.include?("2008") - spec = "vmware_windows" # Windows Server 2008 - elsif product.include?("windows 7") - spec = "vmware_windows" # Windows7 - elsif product.include?("suse") - spec = "vmware_suse" # Suse - elsif product.include?("red hat") - spec = "vmware_rhel" # RHEL - end - log(:info, "VMware Customization Specification: #{spec}") - - # Set values in provisioning object - set_customspec(prov, spec) unless spec.nil? - when 3 - # - # Enter your own VMware custom mapping here - else - # Skip mapping - end # end case -end # end process_vmware - -# process_vmware_pxe -def process_vmware_pxe(mapping, prov, template, product, provider) - case mapping - - when 0 - # No mapping - - when 1 - if product.include?("windows") - # find the windows image that matches the template name if a PXE Image was NOT chosen in the dialog - if prov.get_option(:pxe_image_id).nil? - - log(:info, "Inspecting Eligible Windows Images: #{prov.eligible_windows_images.inspect rescue nil}") - pxe_image = prov.eligible_windows_images.detect { |pi| pi.name.casecmp(template.name) == 0 } - if pxe_image.nil? - log(:error, "Failed to find matching PXE Image", true) - raise - else - log(:info, "Found matching Windows PXE Image ID: #{pxe_image.id} Name: #{pxe_image.name} Description: #{pxe_image.description}") - end - prov.set_windows_image(pxe_image) - log(:info, "Provisioning object updated {:pxe_image_id => #{prov.get_option(:pxe_image_id).inspect}}") - end - # Find the first customization template that matches the template name if none was chosen in the dialog - if prov.get_option(:customization_template_id).nil? - log(:info, "Inspecting Eligible Customization Templates: #{prov.eligible_customization_templates.inspect rescue nil}") - cust_temp = prov.eligible_customization_templates.detect { |ct| ct.name.casecmp(template.name) == 0 } - if cust_temp.nil? - log(:error, "Failed to find matching PXE Image", true) - raise +module ManageIQ + module Automate + module Infrastructure + module VM + module Provisioning + module StateMachines + module Methods + class VMwareCustomizeRequest + def initialize(handle = $evm) + @handle = handle + @mapping = 0 + end + + def main + log(:info, "Provision:<#{prov.id}> Request:<#{prov.miq_provision_request.id}> Type:<#{prov.type}>") + log(:info, "Template: #{template.name} Provider: #{provider.name} Vendor: #{template.vendor} Product: #{product}") + + # Build case statement to determine which type of processing is required + case prov.type + when 'ManageIQ::Providers::Vmware::InfraManager::Provision' + ########################################################## + # VMware Customization Specification Mapping + # + # Possible values: + # 0 - (Default No Mapping) This option is automatically chosen if it finds a customization + # specification mapping chosen from the dialog + # + # 1 - CFME will look for a customization specification with + # the exact name as the template name + # + # 2 - Use this option to use a combination of product name and bitness to + # select your customization specification + ########################################################## + # @mapping = 0 + process_vmware + + when 'ManageIQ::Providers::Vmware::InfraManager::ProvisionViaPxe' + ########################################################## + # VMware PXE Customization Specification Mapping + # + # Possible values: + # 0 - (DEFAULT No Mapping) This option skips the mapping of pxe images and customization templates + # + # 1 - CFME will look for a pxe image and a customization template with + # the exact name as the template name if none were chosen from the provisioning dialog + ########################################################## + # @mapping = 0 + process_vmware_pxe + + else + log(:info, "Provisioning Type: #{prov.type} does not match, skipping processing") + end + end + + private + + def prov + @prov ||= @handle.root["miq_provision"].tap do |req| + raise 'miq_provision not specified' if req.nil? + end + end + + def template + @template ||= prov.vm_template.tap do |req| + raise 'vm_template not specified' if req.nil? + end + end + + def provider + @provider ||= template.ext_management_system.tap do |ems| + raise 'ext_management_system not specified' if ems.nil? + end + end + + def product + @product ||= template.operating_system&.product_name&.downcase + end + + def log(level, msg, update_message = false) + @handle.log(level, msg) + @handle.root['miq_provision'].message = "#{@method} - #{msg}" if @handle.root['miq_provision'] && update_message + end + + def set_customspec(prov, spec) + prov.set_customization_spec(spec, true) rescue nil + log(:info, "Provisioning object updated {:sysprep_custom_spec => #{prov.get_option(:sysprep_custom_spec).inspect}}") + log(:info, "Provisioning object updated {:sysprep_spec_override => #{prov.get_option(:sysprep_spec_override)}}") + end + + def process_vmware + # if prov.get_option(:sysprep_custom_spec) || product.include?("other") || prov.provision_type.include?("clone_to_template") + if product.include?("other") || prov.provision_type.include?("clone_to_template") + @mapping = 0 + end + + if @mapping == 1 + # Automatic customization specification mapping if template is RHEL,Suse or Windows + if product.include?("red hat") || product.include?("suse") || product.include?("windows") + spec = prov.vm_template.name # to match the template name + set_customspec(prov, spec) + + # Set linux hostname stuff here + prov.set_option(:linux_host_name, prov.get_option(:vm_target_name)) + log(:info, "Provisioning object updated {:linux_host_name => #{prov.get_option(:linux_host_name)}}") + prov.set_option(:vm_target_hostname, prov.get_option(:vm_target_name)) + log(:info, "Provisioning object updated {:hostname => #{prov.get_option(:vm_target_hostname)}}") + end + elsif @mapping == 2 + # Use this option to use a combination of product name and bitness to select your customization specification + spec = nil # unknown type + + if product.include?("2003") + spec = "W2K3R2-Entx64" # Windows Server 2003 + elsif product.include?("2008") + spec = "vmware_windows" # Windows Server 2008 + elsif product.include?("windows 7") + spec = "vmware_windows" # Windows7 + elsif product.include?("suse") + spec = "vmware_suse" # Suse + elsif product.include?("red hat") + spec = "vmware_rhel" # RHEL + end + log(:info, "VMware Customization Specification: #{spec}") + + # Set values in provisioning object + set_customspec(prov, spec) unless spec.nil? + end + end + + def process_vmware_pxe + if product.include?("windows") && @mapping == 1 + # find the windows image that matches the template name if a PXE Image was NOT chosen in the dialog + if prov.get_option(:pxe_image_id).nil? + + log(:info, "Inspecting Eligible Windows Images: #{prov.eligible_windows_images.inspect}") + pxe_image = prov.eligible_windows_images.detect { |pi| pi.name.casecmp(template.name) == 0 } + if pxe_image.nil? + log(:error, "Failed to find matching PXE Image", true) + raise "Failed to find matching PXE Image" + else + log(:info, "Found matching Windows PXE Image ID: #{pxe_image.id} Name: #{pxe_image.name} Description: #{pxe_image.description}") + end + prov.set_windows_image(pxe_image) + log(:info, "Provisioning object updated {:pxe_image_id => #{prov.get_option(:pxe_image_id).inspect}}") + end + # Find the first customization template that matches the template name if none was chosen in the dialog + if prov.get_option(:customization_template_id).nil? + log(:info, "Inspecting Eligible Customization Templates: #{prov.eligible_customization_templates.inspect}") + cust_temp = prov.eligible_customization_templates.detect { |ct| ct.name.casecmp(template.name) == 0 } + if cust_temp.nil? + log(:error, "Failed to find matching PXE Image", true) + raise "Failed to find matching PXE Image" + end + log(:info, "Found mathcing Windows Customization Template ID: #{cust_temp.id} Name: #{cust_temp.name} Description: #{cust_temp.description}") + prov.set_customization_template(cust_temp) + log(:info, "Provisioning object updated {:customization_template_id => #{prov.get_option(:customization_template_id).inspect}}") + end + elsif @mapping == 1 + # find the first PXE Image that matches the template name if NOT chosen in the dialog + if prov.get_option(:pxe_image_id).nil? + pxe_image = prov.eligible_pxe_images.detect { |pi| pi.name.casecmp(template.name) == 0 } + log(:info, "Found Linux PXE Image ID: #{pxe_image.id} Name: #{pxe_image.name} Description: #{pxe_image.description}") + prov.set_pxe_image(pxe_image) + log(:info, "Provisioning object updated {:pxe_image_id => #{prov.get_option(:pxe_image_id).inspect}}") + end + # Find the first Customization Template that matches the template name if NOT chosen in the dialog + if prov.get_option(:customization_template_id).nil? + cust_temp = prov.eligible_customization_templates.detect { |ct| ct.name.casecmp(template.name) == 0 } + log(:info, "Found Customization Template ID: #{cust_temp.id} Name: #{cust_temp.name} Description: #{cust_temp.description}") + prov.set_customization_template(cust_temp) + log(:info, "Provisioning object updated {:customization_template_id => #{prov.get_option(:customization_template_id).inspect}}") + end + end + end + end + end + end end - log(:info, "Found mathcing Windows Customization Template ID: #{cust_temp.id} Name: #{cust_temp.name} Description: #{cust_temp.description}") - prov.set_customization_template(cust_temp) - log(:info, "Provisioning object updated {:customization_template_id => #{prov.get_option(:customization_template_id).inspect}}") - end - else - # find the first PXE Image that matches the template name if NOT chosen in the dialog - if prov.get_option(:pxe_image_id).nil? - pxe_image = prov.eligible_pxe_images.detect { |pi| pi.name.casecmp(template.name) == 0 } - log(:info, "Found Linux PXE Image ID: #{pxe_image.id} Name: #{pxe_image.name} Description: #{pxe_image.description}") - prov.set_pxe_image(pxe_image) - log(:info, "Provisioning object updated {:pxe_image_id => #{prov.get_option(:pxe_image_id).inspect}}") - end - # Find the first Customization Template that matches the template name if NOT chosen in the dialog - if prov.get_option(:customization_template_id).nil? - cust_temp = prov.eligible_customization_templates.detect { |ct| ct.name.casecmp(template.name) == 0 } - log(:info, "Found Customization Template ID: #{cust_temp.id} Name: #{cust_temp.name} Description: #{cust_temp.description}") - prov.set_customization_template(cust_temp) - log(:info, "Provisioning object updated {:customization_template_id => #{prov.get_option(:customization_template_id).inspect}}") end end - when 3 - # - # Enter your own VMware PXE custom mapping here - else - # Skip mapping - end # end case -end # end process_vmware_pxe - -# Get provisioning object -prov = $evm.root["miq_provision"] - -log(:info, "Provision:<#{prov.id}> Request:<#{prov.miq_provision_request.id}> Type:<#{prov.type}>") - -template = prov.vm_template -provider = template.ext_management_system -product = template.operating_system['product_name'].downcase rescue nil -log(:info, "Template: #{template.name} Provider: #{provider.name} Vendor: #{template.vendor} Product: #{product}") - -# Build case statement to determine which type of processing is required -case prov.type - -when 'ManageIQ::Providers::Vmware::InfraManager::Provision' -########################################################## -# VMware Customization Specification Mapping -# -# Possible values: -# 0 - (Default No Mapping) This option is automatically chosen if it finds a customization -# specification mapping chosen from the dialog -# -# 1 - CFME will look for a customization specification with -# the exact name as the template name -# -# 2 - Use this option to use a combination of product name and bitness to -# select your customization specification -# -# 3 - Include your own custom mapping logic here -########################################################## - mapping = 0 - process_vmware(mapping, prov, template, product, provider) - -when 'ManageIQ::Providers::Vmware::InfraManager::ProvisionViaPxe' -########################################################## -# VMware PXE Customization Specification Mapping -# -# Possible values: -# 0 - (DEFAULT No Mapping) This option skips the mapping of pxe images and customization templates -# -# 1 - CFME will look for a pxe image and a customization template with -# the exact name as the template name if none were chosen from the provisioning dialog -# -# 2 - Include your own custom mapping logic here -########################################################## - mapping = 0 - process_vmware_pxe(mapping, prov, template, product, provider) - -else - log(:info, "Provisioning Type: #{prov.type} does not match, skipping processing") + end end + +ManageIQ::Automate::Infrastructure::VM::Provisioning::StateMachines::Methods::VMwareCustomizeRequest.new.main From 461ab89001e8dead323b752711b10063350eb094 Mon Sep 17 00:00:00 2001 From: Patrik Komanek Date: Thu, 6 Feb 2020 15:14:38 +0100 Subject: [PATCH 2/2] Tests for the vmware_customizerequest automate method. --- .../vmware_customizerequest_spec.rb | 174 ++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 spec/content/automate/ManageIQ/Infrastructure/VM/Provisioning/StateMachines/Methods.class/__methods__/vmware_customizerequest_spec.rb diff --git a/spec/content/automate/ManageIQ/Infrastructure/VM/Provisioning/StateMachines/Methods.class/__methods__/vmware_customizerequest_spec.rb b/spec/content/automate/ManageIQ/Infrastructure/VM/Provisioning/StateMachines/Methods.class/__methods__/vmware_customizerequest_spec.rb new file mode 100644 index 000000000..98cc2edea --- /dev/null +++ b/spec/content/automate/ManageIQ/Infrastructure/VM/Provisioning/StateMachines/Methods.class/__methods__/vmware_customizerequest_spec.rb @@ -0,0 +1,174 @@ +require_domain_file + +describe ManageIQ::Automate::Infrastructure::VM::Provisioning::StateMachines::Methods::VMwareCustomizeRequest do + let(:prov_type) { :miq_provision_vmware } + let(:ems) { FactoryBot.create(:ems_redhat_with_authentication) } + let(:os) { FactoryBot.create(:operating_system, :product_name => 'suse') } + let(:template) { FactoryBot.create(:miq_template, :ext_management_system => ems, :operating_system => os) } + let(:prov_req) { FactoryBot.create(:miq_provision_request, :options => {:src_vm_id => template.id}) } + let(:prov) { FactoryBot.create(prov_type, :miq_provision_request => prov_req, :request_type => 'template', :options => {:src_vm_id => template.id}) } + let(:svc_prov) { MiqAeMethodService::MiqAeServiceMiqProvision.find(prov.id) } + let(:root_hash) { {'miq_provision' => svc_prov} } + let(:root_object) { Spec::Support::MiqAeMockObject.new(root_hash) } + let(:customization_template) { FactoryBot.create(:customization_template) } + let(:ae_service) do + Spec::Support::MiqAeMockService.new(root_object).tap do |service| + current_object = Spec::Support::MiqAeMockObject.new + current_object.parent = root_object + service.object = current_object + end + end + + def run_method_with_mapping(level = 1) + described_class.new(ae_service).instance_eval do + @mapping = level + main + end + end + + context 'no mapping' do + let(:prov_type) { :miq_provision } + + it 'skips processing' do + method = described_class.new(ae_service) + log = [] + allow(method).to receive(:log) { |level, msg| log << [level, msg] } + expect(method).not_to receive(:process_vmware) + expect(method).not_to receive(:process_vmware_pxe) + method.main + expect(log.last).to eq([:info, "Provisioning Type: #{prov.type} does not match, skipping processing"]) + end + end + + context 'VMware' do + context '#mapping=1' do + let(:customization_template) { FactoryBot.create(:customization_template) } + + it 'sets customization spec' do + allow(svc_prov).to receive(:set_customization_spec).with(template.name, true) + expect(svc_prov).to receive(:set_customization_spec).with(template.name, true) + expect(svc_prov).to receive(:set_option).with(:linux_host_name, prov.get_option(:vm_target_name)) + expect(svc_prov).to receive(:set_option).with(:vm_target_hostname, prov.get_option(:vm_target_name)) + + run_method_with_mapping + end + + it 'skips mapping for other provision type' do + os.product_name = '....other....' + expect(svc_prov).not_to receive(:set_customization_spec) + + run_method_with_mapping + end + end + + context '#mapping=2' do + def validate_final_custom_spec(name, spec) + os.product_name = name + allow(svc_prov).to receive(:set_customization_spec).with(spec, true) + expect(svc_prov).to receive(:set_customization_spec).with(spec, true) + + run_method_with_mapping(2) + end + + it 'sets spec for Windows Server 2003' do + validate_final_custom_spec('....2003...', 'W2K3R2-Entx64') + end + + it 'sets spec for Windows Server 2008' do + validate_final_custom_spec('....2008...', 'vmware_windows') + end + + it 'sets spec for Windows7' do + validate_final_custom_spec('....windows 7...', 'vmware_windows') + end + + it 'sets spec for Suse' do + validate_final_custom_spec('....suse....', 'vmware_suse') + end + + it 'sets spec for RHEL' do + validate_final_custom_spec('....red hat....', 'vmware_rhel') + end + end + end + + context 'VMware via Pxe' do + let(:prov_type) { :miq_provision_vmware_via_pxe } + + context '#mapping' do + let(:pxe) { FactoryBot.create(:pxe_image, :name => "Test PXE Image") } + let(:customization_template) { FactoryBot.create(:customization_template) } + + it 'finds pxe image by name' do + pxe.name = template.name + allow(svc_prov).to receive(:eligible_pxe_images).and_return([pxe]) + expect(svc_prov).to receive(:set_pxe_image).with(pxe) + svc_prov.options[:customization_template_id] = customization_template.id + + run_method_with_mapping + end + + it 'finds customization template by name' do + svc_prov.options[:pxe_image_id] = pxe.id + customization_template.name = template.name + allow(svc_prov).to receive(:eligible_customization_templates).and_return([customization_template]) + expect(svc_prov).to receive(:set_customization_template).with(customization_template) + + run_method_with_mapping + end + + context '#windows_template' do + let(:os) { FactoryBot.create(:operating_system, :product_name => 'Microsoft Windows') } + + it 'finds pxe image by name' do + pxe.name = template.name + allow(svc_prov).to receive(:eligible_windows_images).and_return([pxe]) + expect(svc_prov).to receive(:set_windows_image).with(pxe) + svc_prov.options[:customization_template_id] = customization_template.id + + run_method_with_mapping + end + + it 'fails to find iso image' do + pxe.name = template.name + allow(svc_prov).to receive(:eligible_windows_images).and_return([]) + svc_prov.options[:customization_template_id] = customization_template.id + + expect { run_method_with_mapping }.to raise_error('Failed to find matching PXE Image') + end + + it 'finds customization template by name' do + svc_prov.options[:pxe_image_id] = pxe.id + customization_template.name = template.name + allow(svc_prov).to receive(:eligible_customization_templates).and_return([customization_template]) + expect(svc_prov).to receive(:set_customization_template).with(customization_template) + + run_method_with_mapping + end + + it 'fails to find customization template' do + svc_prov.options[:pxe_image_id] = pxe.id + allow(svc_prov).to receive(:eligible_customization_templates).and_return([]) + customization_template.name = template.name + + expect { run_method_with_mapping }.to raise_error('Failed to find matching PXE Image') + end + end + end + end + + it 'raises error for missing miq_provision' do + ae_service.root["miq_provision"] = nil + expect { described_class.new(ae_service).main }.to raise_error('miq_provision not specified') + end + + it 'raises error for missing vm_template' do + allow(ae_service.root["miq_provision"]).to receive(:vm_template).and_return(nil) + expect { described_class.new(ae_service).main }.to raise_error('vm_template not specified') + end + + it 'raises error for missing ext_management_system' do + allow_any_instance_of(MiqAeMethodService::MiqAeServiceMiqTemplate).to receive(:ext_management_system).and_return(nil) + expect { described_class.new(ae_service).main }.to raise_error('ext_management_system not specified') + end +end