From 4d57beb91b1065702bd6749c7583a49c8bf93c4c Mon Sep 17 00:00:00 2001 From: Drew Bomhof Date: Tue, 14 Feb 2017 10:20:00 -0500 Subject: [PATCH 1/4] Build Job Template as a queued item This change allows for a Queued refresh after building the JobTemplate in the provider https://www.pivotaltracker.com/story/show/137306185 --- .../service_template_ansible_playbook.rb | 54 ++++++++++++++----- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/app/models/service_template_ansible_playbook.rb b/app/models/service_template_ansible_playbook.rb index 8e843aa248b..5c36c9ac61b 100644 --- a/app/models/service_template_ansible_playbook.rb +++ b/app/models/service_template_ansible_playbook.rb @@ -50,19 +50,49 @@ def self.prepare_job_template_and_dialog(action, service_name, description, conf end private_class_method :prepare_job_template_and_dialog - def self.create_job_template(name, description, info) - playbook = ManageIQ::Providers::AnsibleTower::ConfigurationManager::Playbook.find(info[:playbook_id]) - # tower = playbook.manager - - # params = { - # :name => name, - # :description => description || '', - # :extra_vars => info[:variables] || {}, - # :inventory_id => playbook.inventory_root_group, - # } - # tower.class.create_in_provider(tower, params) + def self.create_job_templates(service_name, description, config_info, auth_user) + [:provision, :retirement, :reconfigure].each_with_object({}) do |action, hash| + next unless config_info[action] + job_template_name = unique_job_template_name(service_name, action) + hash[action] = { :configuration_template => create_job_template(job_template_name.to_s, description, config_info[action], auth_user) } + end + end + + def self.create_job_template(name, description, info, auth_user) + tower, params = build_parameter_list(name, description, info) + + task_id = ManageIQ::Providers::AnsibleTower::AutomationManager::ConfigurationScript.create_in_provider_queue(tower.id, params, auth_user) + task = MiqTask.wait_for_taskid(task_id) + raise task.message unless task.status == "Ok" + task.task_results + end + + def self.build_parameter_list(name, description, info) + playbook = ManageIQ::Providers::AnsibleTower::AutomationManager::Playbook.find(info[:playbook_id]) + tower = playbook.manager + params = { + :name => name, + :description => description || '', + :project => playbook.configuration_script_source.manager_ref, + :playbook => playbook.name, + :inventory => tower.inventory_root_groups.first.ems_ref, + :ask_variables_on_launch => true, + :ask_limit_on_launch => true, + :ask_inventory_on_launch => true, + :ask_credential_on_launch => true + }.merge(info.slice(:extra_vars)) + + [:credential, :cloud_credential, :network_credential].each do |credential| + cred_sym = "#{credential}_id".to_sym + params[credential] = Authentication.find(info[cred_sym]).manager_ref if info[cred_sym] + end + + [tower, params] + end + + def self.unique_job_template_name(service_name, action) + "#{service_name}_#{action}_#{rand(36**8).to_s(36)}" end - private_class_method :create_job_template def self.validate_config_info(options) info = options[:config_info] From 72fcd14e46cc8d5819be57362fb19014dc252b06 Mon Sep 17 00:00:00 2001 From: Drew Bomhof Date: Tue, 14 Feb 2017 10:21:03 -0500 Subject: [PATCH 2/4] Adjust queued methods Accept an auth_user or default to 'system' as a backup Always call my_zone https://www.pivotaltracker.com/story/show/137306185 --- .../automation_manager/configuration_script/api_create.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/models/manageiq/providers/ansible_tower/automation_manager/configuration_script/api_create.rb b/app/models/manageiq/providers/ansible_tower/automation_manager/configuration_script/api_create.rb index 858d14e9fd3..6ab4244d3ec 100644 --- a/app/models/manageiq/providers/ansible_tower/automation_manager/configuration_script/api_create.rb +++ b/app/models/manageiq/providers/ansible_tower/automation_manager/configuration_script/api_create.rb @@ -8,15 +8,15 @@ def create_in_provider(manager_id, params) # Get the record in our database # TODO: This needs to be targeted refresh so it doesn't take too long - EmsRefresh.queue_refresh(manager, nil, true) if !manager.missing_credentials? && manager.authentication_status_ok? + EmsRefresh.queue_refresh(manager, nil, true) if manager.authentication_status_ok? find_by(:manager_id => manager.id, :manager_ref => job_template.id) end - def create_in_provider_queue(manager_id, params) + def create_in_provider_queue(manager_id, params, auth_user = nil) task_opts = { :action => "Creating Ansible Tower Job Template", - :userid => "system" + :userid => auth_user || "system" } manager = ExtManagementSystem.find(manager_id) @@ -27,7 +27,7 @@ def create_in_provider_queue(manager_id, params) :method_name => "create_in_provider", :priority => MiqQueue::HIGH_PRIORITY, :role => "ems_operations", - :zone => manager.zone_id + :zone => manager.my_zone } MiqTask.generic_action_with_callback(task_opts, queue_opts) From f3e8adcae324e21bb024e7e4adc2007e888df1d7 Mon Sep 17 00:00:00 2001 From: Drew Bomhof Date: Tue, 14 Feb 2017 10:21:56 -0500 Subject: [PATCH 3/4] Add specs around queued job_template creation --- .../service_template_ansible_playbook_spec.rb | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 spec/models/service_template_ansible_playbook_spec.rb diff --git a/spec/models/service_template_ansible_playbook_spec.rb b/spec/models/service_template_ansible_playbook_spec.rb new file mode 100644 index 00000000000..f64099fa860 --- /dev/null +++ b/spec/models/service_template_ansible_playbook_spec.rb @@ -0,0 +1,118 @@ +describe ServiceTemplateAnsiblePlaybook do + describe 'building_job_templates' do + let(:user) { FactoryGirl.create(:user_with_group) } + let(:job_template) do + FactoryGirl.create(:configuration_script, + :variables => catalog_item_options.fetch_path(:config_info, :provision, :extra_vars)) + end + let(:auth_one) { FactoryGirl.create(:authentication, :manager_ref => 6) } + let(:auth_two) { FactoryGirl.create(:authentication, :manager_ref => 10) } + let(:user) { FactoryGirl.create(:user_with_group) } + let(:inventory_root_group) { FactoryGirl.create(:inventory_root_group) } + let(:ems) do + FactoryGirl.create(:automation_manager_ansible_tower, :inventory_root_groups => [inventory_root_group]) + end + let(:config_script) { FactoryGirl.create(:configuration_script) } + let(:script_source) { FactoryGirl.create(:configuration_script_source, :manager => ems) } + let(:playbook) do + FactoryGirl.create(:configuration_script_payload, + :configuration_script_source => script_source, + :manager => ems, + :inventory_root_group => inventory_root_group, + :type => 'ManageIQ::Providers::AnsibleTower::AutomationManager::Playbook') + end + let(:service_template_catalog) { FactoryGirl.create(:service_template_catalog) } + let(:catalog_item_options) do + { + :name => 'test_ansible_catalog_item', + :description => 'test ansible', + :service_template_catalog_id => service_template_catalog.id, + :config_info => { + :provision => { + :new_dialog_name => 'test_dialog', + :hosts => 'many', + :credential_id => auth_one.id, + :network_credential_id => auth_two.id, + :playbook_id => playbook.id, + }, + } + } + end + + let(:catalog_item_options_two) do + { + :name => 'playbook service', + :display => 'false', + :description => 'a description', + :config_info => { + :provision => { + :new_dialog_name => 'playbook dialog', + :playbook_id => 1, + :extra_vars => { + 'key1' => 'val1', + 'key2' => 'val2' + } + }, + :reconfigure => { + :new_dialog_name => 'playbook dialog reconfigure', + :playbook_id => 5, + }, + :retirement => { + :new_dialog_name => 'playbook dialog retirement', + :playbook_id => 3, + }, + } + } + end + + it '#create_job_templates' do + expect(described_class).to receive(:create_job_template).exactly(3).times.and_return(job_template) + options_hash = described_class.create_job_templates(catalog_item_options_two[:name], catalog_item_options_two[:description], catalog_item_options_two[:config_info], 'system') + [:provision, :retirement, :reconfigure].each do |action| + expect(options_hash[action.to_sym][:configuration_template]).to eq job_template + end + end + + it '#create_job_template' do + expect(described_class).to receive(:build_parameter_list).and_return([ems, {}]) + expect(ManageIQ::Providers::AnsibleTower::AutomationManager::ConfigurationScript).to receive(:create_in_provider_queue).once.with(ems.id, {}, 'system') + expect(MiqTask).to receive(:wait_for_taskid).with(any_args).once.and_return(instance_double('MiqTask', :task_results => {}, :status => 'Ok')) + + described_class.create_job_template(catalog_item_options[:name], catalog_item_options[:description], catalog_item_options[:config_info], 'system') + end + + it 'create_job_template exception' do + expect(described_class).to receive(:build_parameter_list).and_return([ems, {}]) + expect(ManageIQ::Providers::AnsibleTower::AutomationManager::ConfigurationScript).to receive(:create_in_provider_queue).once.with(ems.id, {}, 'system') + expect(MiqTask).to receive(:wait_for_taskid).with(any_args).once.and_raise(Exception, 'bad job template') + + expect { described_class.create_job_template(catalog_item_options[:name], catalog_item_options[:description], catalog_item_options[:config_info], 'system') }.to raise_error(Exception) + end + + it '#build_parameter_list' do + name = catalog_item_options[:name] + description = catalog_item_options[:description] + info = catalog_item_options[:config_info][:provision] + _tower, params = described_class.build_parameter_list(name, description, info) + + expect(params).to have_attributes( + :name => name, + :description => description, + :extra_vars => nil, + :credential => '6', + :cloud_credential => nil, + :network_credential => '10' + ) + end + + it '#unique_job_template_name' do + [:provision, :retirement, :reconfigure].each do |type| + name = described_class.unique_job_template_name("blah", type) + name_list = name.split('_') + expect(name).to_not eq "blah_#{type}" + expect(name_list.size).to eq 3 + expect(name_list).to include('blah', type.to_s) + end + end + end +end From c81dbcb0d077f67b0a2dbb9913a266c0f3dbad12 Mon Sep 17 00:00:00 2001 From: Drew Bomhof Date: Tue, 14 Feb 2017 11:50:56 -0500 Subject: [PATCH 4/4] If we find :extra_vars set the value to json --- .../service_template_ansible_playbook.rb | 15 ++++---- .../configuration_script/api_create_spec.rb | 2 +- .../service_template_ansible_playbook_spec.rb | 38 +++++++++---------- 3 files changed, 26 insertions(+), 29 deletions(-) diff --git a/app/models/service_template_ansible_playbook.rb b/app/models/service_template_ansible_playbook.rb index 5c36c9ac61b..693ede4251b 100644 --- a/app/models/service_template_ansible_playbook.rb +++ b/app/models/service_template_ansible_playbook.rb @@ -53,10 +53,10 @@ def self.prepare_job_template_and_dialog(action, service_name, description, conf def self.create_job_templates(service_name, description, config_info, auth_user) [:provision, :retirement, :reconfigure].each_with_object({}) do |action, hash| next unless config_info[action] - job_template_name = unique_job_template_name(service_name, action) - hash[action] = { :configuration_template => create_job_template(job_template_name.to_s, description, config_info[action], auth_user) } + hash[action] = { :configuration_template => create_job_template("miq_#{service_name}_#{action}", description, config_info[action], auth_user) } end end + private_class_method :create_job_templates def self.create_job_template(name, description, info, auth_user) tower, params = build_parameter_list(name, description, info) @@ -66,6 +66,7 @@ def self.create_job_template(name, description, info, auth_user) raise task.message unless task.status == "Ok" task.task_results end + private_class_method :create_job_template def self.build_parameter_list(name, description, info) playbook = ManageIQ::Providers::AnsibleTower::AutomationManager::Playbook.find(info[:playbook_id]) @@ -80,19 +81,17 @@ def self.build_parameter_list(name, description, info) :ask_limit_on_launch => true, :ask_inventory_on_launch => true, :ask_credential_on_launch => true - }.merge(info.slice(:extra_vars)) + } + params[:extra_vars] = info[:extra_vars].to_json if info[:extra_vars] [:credential, :cloud_credential, :network_credential].each do |credential| cred_sym = "#{credential}_id".to_sym params[credential] = Authentication.find(info[cred_sym]).manager_ref if info[cred_sym] end - [tower, params] - end - - def self.unique_job_template_name(service_name, action) - "#{service_name}_#{action}_#{rand(36**8).to_s(36)}" + [tower, params.compact] end + private_class_method :build_parameter_list def self.validate_config_info(options) info = options[:config_info] diff --git a/spec/models/manageiq/providers/ansible_tower/automation_manager/configuration_script/api_create_spec.rb b/spec/models/manageiq/providers/ansible_tower/automation_manager/configuration_script/api_create_spec.rb index 0c9def5aeec..2e22d0fc92c 100644 --- a/spec/models/manageiq/providers/ansible_tower/automation_manager/configuration_script/api_create_spec.rb +++ b/spec/models/manageiq/providers/ansible_tower/automation_manager/configuration_script/api_create_spec.rb @@ -45,7 +45,7 @@ :method_name => "create_in_provider", :priority => MiqQueue::HIGH_PRIORITY, :role => "ems_operations", - :zone => manager.zone_id + :zone => manager.my_zone ) end diff --git a/spec/models/service_template_ansible_playbook_spec.rb b/spec/models/service_template_ansible_playbook_spec.rb index f64099fa860..5024eed58e6 100644 --- a/spec/models/service_template_ansible_playbook_spec.rb +++ b/spec/models/service_template_ansible_playbook_spec.rb @@ -41,13 +41,14 @@ let(:catalog_item_options_two) do { - :name => 'playbook service', - :display => 'false', - :description => 'a description', - :config_info => { + :name => 'playbook service', + :display => 'false', + :service_template_catalog_id => service_template_catalog.id, + :description => 'a description', + :config_info => { :provision => { :new_dialog_name => 'playbook dialog', - :playbook_id => 1, + :playbook_id => playbook.id, :extra_vars => { 'key1' => 'val1', 'key2' => 'val2' @@ -67,7 +68,7 @@ it '#create_job_templates' do expect(described_class).to receive(:create_job_template).exactly(3).times.and_return(job_template) - options_hash = described_class.create_job_templates(catalog_item_options_two[:name], catalog_item_options_two[:description], catalog_item_options_two[:config_info], 'system') + options_hash = described_class.send(:create_job_templates, catalog_item_options_two[:name], catalog_item_options_two[:description], catalog_item_options_two[:config_info], 'system') [:provision, :retirement, :reconfigure].each do |action| expect(options_hash[action.to_sym][:configuration_template]).to eq job_template end @@ -78,7 +79,7 @@ expect(ManageIQ::Providers::AnsibleTower::AutomationManager::ConfigurationScript).to receive(:create_in_provider_queue).once.with(ems.id, {}, 'system') expect(MiqTask).to receive(:wait_for_taskid).with(any_args).once.and_return(instance_double('MiqTask', :task_results => {}, :status => 'Ok')) - described_class.create_job_template(catalog_item_options[:name], catalog_item_options[:description], catalog_item_options[:config_info], 'system') + described_class.send(:create_job_template, catalog_item_options[:name], catalog_item_options[:description], catalog_item_options[:config_info], 'system') end it 'create_job_template exception' do @@ -86,33 +87,30 @@ expect(ManageIQ::Providers::AnsibleTower::AutomationManager::ConfigurationScript).to receive(:create_in_provider_queue).once.with(ems.id, {}, 'system') expect(MiqTask).to receive(:wait_for_taskid).with(any_args).once.and_raise(Exception, 'bad job template') - expect { described_class.create_job_template(catalog_item_options[:name], catalog_item_options[:description], catalog_item_options[:config_info], 'system') }.to raise_error(Exception) + expect { described_class.send(:create_job_template, catalog_item_options[:name], catalog_item_options[:description], catalog_item_options[:config_info], 'system') }.to raise_error(Exception) end it '#build_parameter_list' do name = catalog_item_options[:name] + catalog_extra_vars = catalog_item_options_two description = catalog_item_options[:description] info = catalog_item_options[:config_info][:provision] - _tower, params = described_class.build_parameter_list(name, description, info) + _tower, params = described_class.send(:build_parameter_list, name, description, info) + _tower_two, params_two = described_class.send(:build_parameter_list, catalog_extra_vars[:name], catalog_extra_vars[:description], catalog_extra_vars[:config_info][:provision]) expect(params).to have_attributes( :name => name, :description => description, - :extra_vars => nil, :credential => '6', - :cloud_credential => nil, :network_credential => '10' ) - end - it '#unique_job_template_name' do - [:provision, :retirement, :reconfigure].each do |type| - name = described_class.unique_job_template_name("blah", type) - name_list = name.split('_') - expect(name).to_not eq "blah_#{type}" - expect(name_list.size).to eq 3 - expect(name_list).to include('blah', type.to_s) - end + expect(params.keys).to_not include(:extra_vars, :cloud_credentials) + expect(params_two.keys).to include(:extra_vars) + expect(JSON.parse(params_two[:extra_vars])).to have_attributes( + 'key1' => 'val1', + 'key2' => 'val2' + ) end end end