foreman_ansible_core 2.1.1 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: c69f61506d641e18d27a22a32514cdb6221174dd
4
- data.tar.gz: 9afe8696e6723ad9ab66adf416b457e8fa9dfec8
2
+ SHA256:
3
+ metadata.gz: 3a2a8033b0b49e4265b56fa56a0376ca02cde2f293a57c618039c60087014381
4
+ data.tar.gz: 98166355302fab2ab9d5624699cd6cfe6d880d6161b304b689f6410a1006c0f1
5
5
  SHA512:
6
- metadata.gz: c40433c92f4c4f3d617562cad983a0b116b8240830bb14d6d641aa17187845dc1649a3e4da75f81af6485b66efffc69f8fc2a431212e938989c10fa54604ef3b
7
- data.tar.gz: 7080dd69b55873db5305ba237dc7d5e7331cecd1e23c2969125eaf553e115573663205bf0fa46b5331686661330150c44a7dec6fc5278b5f9070498f87e83c5e
6
+ metadata.gz: 4ec94ef02708b8dd9403e630abf2f3f82a65503124dbcef17ee4f3ca9e78e663d9dd8b2ce89ea43d37641ec13c5b81cd2c96d25dbf7bf3b13b8fbad5d189631e
7
+ data.tar.gz: 199d21b8a88c256ece9fccc356f580f46d2334fbd87b7310f39043ebdce7560e37d10338ef73b34f4c7af24ed8940b556314faeba74c5206e8fa453a0ce7542c
@@ -1,39 +1,34 @@
1
- begin
2
- require 'foreman_tasks_core'
3
- require 'foreman_remote_execution_core'
4
- # rubocop:disable Lint/HandleExceptions
5
- rescue LoadError
6
- # These gems are not available in a proxy SCLed context
7
- # puts 'Running Foreman Ansible Core in non-SCL context'
8
- end
9
- # rubocop:enable Lint/HandleExceptions
1
+ # frozen_string_literal: true
2
+
3
+ require 'foreman_tasks_core'
4
+ require 'foreman_remote_execution_core'
10
5
 
11
6
  # Core actions for Foreman Ansible, used by both Foreman and Foreman proxy
12
7
  # This comprises running playbooks for the moment
13
8
  module ForemanAnsibleCore
14
9
  require 'foreman_ansible_core/exception'
15
- require 'foreman_ansible_core/roles_reader'
16
10
  require 'foreman_ansible_core/version'
17
11
 
18
- if defined? ForemanTasksCore
19
- extend ForemanTasksCore::SettingsLoader
20
- register_settings(:ansible, :ansible_dir => Dir.home,
21
- :working_dir => nil)
12
+ extend ForemanTasksCore::SettingsLoader
13
+ register_settings(:ansible, :ansible_dir => Dir.home,
14
+ :working_dir => nil)
22
15
 
23
- if ForemanTasksCore.dynflow_present?
24
- require 'foreman_tasks_core/runner'
25
- require 'foreman_ansible_core/playbook_runner'
26
- require 'foreman_ansible_core/actions'
27
- end
16
+ if ForemanTasksCore.dynflow_present?
17
+ require 'foreman_tasks_core/runner'
18
+ require 'foreman_ansible_core/runner/playbook'
19
+ require 'foreman_ansible_core/runner/ansible_runner'
20
+ require 'foreman_ansible_core/actions'
28
21
  end
29
22
 
30
- if defined? ForemanTasksCore
31
- require 'foreman_remote_execution_core/actions'
32
- require 'foreman_ansible_core/remote_execution_core/ansible_runner'
33
- require 'foreman_ansible_core/remote_execution_core/settings_override'
34
- ForemanRemoteExecutionCore::Actions::RunScript.send(
35
- :prepend,
36
- ForemanAnsibleCore::RemoteExecutionCore::SettingsOverride
37
- )
23
+ require 'foreman_remote_execution_core/actions'
24
+ require 'foreman_ansible_core/remote_execution_core/ansible_runner'
25
+ require 'foreman_ansible_core/task_launcher/playbook'
26
+ require 'foreman_ansible_core/task_launcher/ansible_runner'
27
+
28
+ if defined?(SmartProxyDynflowCore)
29
+ SmartProxyDynflowCore::TaskLauncherRegistry.register('ansible-runner',
30
+ ForemanAnsibleCore::TaskLauncher::AnsibleRunner)
31
+ SmartProxyDynflowCore::TaskLauncherRegistry.register('ansible-playbook',
32
+ ForemanAnsibleCore::TaskLauncher::Playbook)
38
33
  end
39
34
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'foreman_tasks_core/shareable_action'
2
4
 
3
5
  module ForemanAnsibleCore
@@ -6,7 +8,7 @@ module ForemanAnsibleCore
6
8
  # to execute the playbook run
7
9
  class RunPlaybook < ForemanTasksCore::Runner::Action
8
10
  def initiate_runner
9
- ForemanAnsibleCore::PlaybookRunner.new(
11
+ ForemanAnsibleCore::Runner::Playbook.new(
10
12
  input[:inventory],
11
13
  input[:playbook],
12
14
  input[:options]
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ForemanAnsibleCore
2
4
  # Taken from Foreman core, this class creates an error code for any exception
3
5
  class Exception < ::StandardError
@@ -1,19 +1,24 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ForemanAnsibleCore
2
4
  module RemoteExecutionCore
3
5
  # Takes an inventory and runs it through REXCore CommandRunner
4
6
  class AnsibleRunner < ::ForemanTasksCore::Runner::CommandRunner
5
7
  DEFAULT_REFRESH_INTERVAL = 1
8
+ CONNECTION_PROMPT = 'Are you sure you want to continue connecting (yes/no)? '
6
9
 
7
- def initialize(options)
8
- super(options)
9
- @playbook_runner = ForemanAnsibleCore::PlaybookRunner.new(
10
+ def initialize(options, suspended_action:)
11
+ super(options, :suspended_action => suspended_action)
12
+ @playbook_runner = ForemanAnsibleCore::Runner::Playbook.new(
10
13
  options['ansible_inventory'],
11
14
  options['script'],
12
- options
15
+ options,
16
+ :suspended_action => suspended_action
13
17
  )
14
18
  end
15
19
 
16
20
  def start
21
+ @playbook_runner.logger = logger
17
22
  @playbook_runner.start
18
23
  rescue StandardError => e
19
24
  logger.error(
@@ -36,6 +41,21 @@ module ForemanAnsibleCore
36
41
  @command_in = @playbook_runner.command_in
37
42
  @command_pid = @playbook_runner.command_pid
38
43
  super
44
+ kill if unknown_host_key_fingerprint?
45
+ end
46
+
47
+ def kill
48
+ publish_exit_status(1)
49
+ ::Process.kill('SIGTERM', @command_pid)
50
+ close
51
+ end
52
+
53
+ private
54
+
55
+ def unknown_host_key_fingerprint?
56
+ last_output = @continuous_output.raw_outputs.last
57
+ return if last_output.nil?
58
+ last_output['output']&.lines&.last == CONNECTION_PROMPT
39
59
  end
40
60
  end
41
61
  end
@@ -0,0 +1,143 @@
1
+ module ForemanAnsibleCore
2
+ module Runner
3
+ class AnsibleRunner < ForemanTasksCore::Runner::Parent
4
+ include ForemanTasksCore::Runner::Command
5
+
6
+ def initialize(input, suspended_action:)
7
+ super input, :suspended_action => suspended_action
8
+ @inventory = rebuild_inventory(input)
9
+ @playbook = input.values.first[:input][:action_input][:script]
10
+ @root = working_dir
11
+ end
12
+
13
+ def start
14
+ prepare_directory_structure
15
+ write_inventory
16
+ write_playbook
17
+ start_ansible_runner
18
+ end
19
+
20
+ def refresh
21
+ return unless super
22
+ @counter ||= 1
23
+ @uuid ||= File.basename(Dir["#{@root}/artifacts/*"].first)
24
+ job_event_dir = File.join(@root, 'artifacts', @uuid, 'job_events')
25
+ loop do
26
+ files = Dir["#{job_event_dir}/*.json"].map do |file|
27
+ num = File.basename(file)[/\A\d+/].to_i unless file.include?('partial')
28
+ [file, num]
29
+ end
30
+ files_with_nums = files.select { |(_, num)| num && num >= @counter }.sort_by(&:last)
31
+ break if files_with_nums.empty?
32
+ logger.debug("[foreman_ansible] - processing event files: #{files_with_nums.map(&:first).inspect}}")
33
+ files_with_nums.map(&:first).each { |event_file| handle_event_file(event_file) }
34
+ @counter = files_with_nums.last.last + 1
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def handle_event_file(event_file)
41
+ logger.debug("[foreman_ansible] - parsing event file #{event_file}")
42
+ begin
43
+ event = JSON.parse(File.read(event_file))
44
+ if (hostname = event['event_data']['host'])
45
+ handle_host_event(hostname, event)
46
+ else
47
+ handle_broadcast_data(event)
48
+ end
49
+ true
50
+ rescue JSON::ParserError => e
51
+ logger.error("[foreman_ansible] - Error parsing runner event at #{event_file}: #{e.class}: #{e.message}")
52
+ logger.debug(e.backtrace.join("\n"))
53
+ end
54
+ end
55
+
56
+ def handle_host_event(hostname, event)
57
+ log_event("for host: #{hostname.inspect}", event)
58
+ publish_data_for(hostname, event['stdout'] + "\n", 'stdout') if event['stdout']
59
+ case event['event']
60
+ when 'runner_on_ok'
61
+ publish_exit_status_for(hostname, 0) if @exit_statuses[hostname].nil?
62
+ when 'runner_on_unreachable'
63
+ publish_exit_status_for(hostname, 1)
64
+ when 'runner_on_failed'
65
+ publish_exit_status_for(hostname, 2) if event['ignore_errors'].nil?
66
+ end
67
+ end
68
+
69
+ def handle_broadcast_data(event)
70
+ log_event("broadcast", event)
71
+ if event['event'] == 'playbook_on_stats'
72
+ header, *rows = event['stdout'].strip.lines.map(&:chomp)
73
+ @outputs.keys.select { |key| key.is_a? String }.each do |host|
74
+ line = rows.find { |row| row =~ /#{host}/ }
75
+ publish_data_for(host, [header, line].join("\n"), 'stdout')
76
+ end
77
+ else
78
+ broadcast_data(event['stdout'] + "\n", 'stdout')
79
+ end
80
+ end
81
+
82
+ def write_inventory
83
+ inventory_script = <<~INVENTORY_SCRIPT
84
+ #!/bin/sh
85
+ cat <<-EOS
86
+ #{JSON.dump(@inventory)}
87
+ EOS
88
+ INVENTORY_SCRIPT
89
+ path = File.join(@root, 'inventory', 'hosts')
90
+ File.write(path, inventory_script)
91
+ File.chmod(0o0755, path)
92
+ end
93
+
94
+ def write_playbook
95
+ File.write(File.join(@root, 'project', 'playbook.yml'), @playbook)
96
+ end
97
+
98
+ def start_ansible_runner
99
+ command = ['ansible-runner', 'run', @root, '-p', 'playbook.yml']
100
+ initialize_command(*command)
101
+ logger.debug("[foreman_ansible] - Running command '#{command.join(' ')}'")
102
+ end
103
+
104
+ def prepare_directory_structure
105
+ inner = %w[inventory project].map { |part| File.join(@root, part) }
106
+ ([@root] + inner).each do |path|
107
+ FileUtils.mkdir_p path
108
+ end
109
+ end
110
+
111
+ def log_event(description, event)
112
+ # TODO: replace this ugly code with block variant once https://github.com/Dynflow/dynflow/pull/323
113
+ # arrives in production
114
+ logger.debug("[foreman_ansible] - handling event #{description}: #{JSON.pretty_generate(event)}") if logger.level <= ::Logger::DEBUG
115
+ end
116
+
117
+ # Each per-host task has inventory only for itself, we must
118
+ # collect all the partial inventories into one large inventory
119
+ # containing all the hosts.
120
+ def rebuild_inventory(input)
121
+ action_inputs = input.values.map { |hash| hash[:input][:action_input] }
122
+ hostnames = action_inputs.map { |hash| hash[:name] }
123
+ inventories = action_inputs.map { |hash| JSON.parse(hash[:ansible_inventory]) }
124
+ host_vars = inventories.map { |i| i['_meta']['hostvars'] }.reduce(&:merge)
125
+
126
+ { '_meta' => { 'hostvars' => host_vars },
127
+ 'all' => { 'hosts' => hostnames,
128
+ 'vars' => inventories.first['all']['vars'] } }
129
+ end
130
+
131
+ def working_dir
132
+ return @root if @root
133
+ dir = ForemanAnsibleCore.settings[:working_dir]
134
+ @tmp_working_dir = true
135
+ if dir.nil?
136
+ Dir.mktmpdir
137
+ else
138
+ Dir.mktmpdir(nil, File.expand_path(dir))
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ForemanAnsibleCore
2
4
  # Creates the actual command to be passed to foreman_tasks_core to run
3
5
  class CommandCreator
@@ -32,7 +34,7 @@ module ForemanAnsibleCore
32
34
  end
33
35
 
34
36
  def json_inventory_script
35
- File.expand_path('../../bin/json_inventory.sh', File.dirname(__FILE__))
37
+ File.expand_path('../../../bin/json_inventory.sh', File.dirname(__FILE__))
36
38
  end
37
39
 
38
40
  def setup_verbosity
@@ -48,7 +50,7 @@ module ForemanAnsibleCore
48
50
  verbosity_level = @options[:verbosity_level]
49
51
  # rubocop:disable Rails/Present
50
52
  !verbosity_level.nil? && !verbosity_level.empty? &&
51
- verbosity_level.to_i > 0
53
+ verbosity_level.to_i.positive?
52
54
  # rubocop:enable Rails/Present
53
55
  end
54
56
 
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'foreman_tasks_core/runner/command_runner'
4
+ require_relative 'command_creator'
5
+ require 'tmpdir'
6
+ require 'net/ssh'
7
+
8
+ module ForemanAnsibleCore
9
+ module Runner
10
+ # Implements ForemanTasksCore::Runner::Base interface for running
11
+ # Ansible playbooks, used by the Foreman Ansible plugin and Ansible proxy
12
+ class Playbook < ForemanTasksCore::Runner::CommandRunner
13
+ attr_reader :command_out, :command_in, :command_pid
14
+
15
+ def initialize(inventory, playbook, options = {}, suspended_action:)
16
+ super :suspended_action => suspended_action
17
+ @inventory = inventory
18
+ unknown_hosts.each do |host|
19
+ add_to_known_hosts(host)
20
+ end
21
+ @playbook = playbook
22
+ @options = options
23
+ initialize_dirs
24
+ end
25
+
26
+ def start
27
+ write_inventory
28
+ write_playbook
29
+ command = CommandCreator.new(inventory_file,
30
+ playbook_file,
31
+ @options).command
32
+ logger.debug('[foreman_ansible] - Initializing Ansible Runner')
33
+ Dir.chdir(@ansible_dir) do
34
+ initialize_command(*command)
35
+ logger.debug("[foreman_ansible] - Running command '#{command.join(' ')}'")
36
+ end
37
+ end
38
+
39
+ def kill
40
+ publish_data('== TASK ABORTED BY USER ==', 'stdout')
41
+ publish_exit_status(1)
42
+ ::Process.kill('SIGTERM', @command_pid)
43
+ close
44
+ end
45
+
46
+ def close
47
+ super
48
+ FileUtils.remove_entry(@working_dir) if @tmp_working_dir
49
+ end
50
+
51
+ private
52
+
53
+ def write_inventory
54
+ ensure_directory(File.dirname(inventory_file))
55
+ File.write(inventory_file, @inventory)
56
+ end
57
+
58
+ def write_playbook
59
+ ensure_directory(File.dirname(playbook_file))
60
+ File.write(playbook_file, @playbook)
61
+ end
62
+
63
+ def inventory_file
64
+ File.join(@working_dir, 'foreman-inventories', id)
65
+ end
66
+
67
+ def playbook_file
68
+ File.join(@working_dir, "foreman-playbook-#{id}.yml")
69
+ end
70
+
71
+ def events_dir
72
+ File.join(@working_dir, 'events', id.to_s)
73
+ end
74
+
75
+ def ensure_directory(path)
76
+ if File.exist?(path)
77
+ raise "#{path} expected to be a directory" unless File.directory?(path)
78
+ else
79
+ FileUtils.mkdir_p(path)
80
+ end
81
+ path
82
+ end
83
+
84
+ def initialize_dirs
85
+ settings = ForemanAnsibleCore.settings
86
+ initialize_working_dir(settings[:working_dir])
87
+ initialize_ansible_dir(settings[:ansible_dir])
88
+ end
89
+
90
+ def initialize_working_dir(working_dir)
91
+ if working_dir.nil?
92
+ @working_dir = Dir.mktmpdir
93
+ @tmp_working_dir = true
94
+ else
95
+ @working_dir = File.expand_path(working_dir)
96
+ end
97
+ end
98
+
99
+ def initialize_ansible_dir(ansible_dir)
100
+ raise "Ansible dir #{ansible_dir} does not exist" unless
101
+ !ansible_dir.nil? && File.exist?(ansible_dir)
102
+ @ansible_dir = ansible_dir
103
+ end
104
+
105
+ def unknown_hosts
106
+ JSON.parse(@inventory)['all']['hosts'].select do |host|
107
+ Net::SSH::KnownHosts.search_for(host).empty?
108
+ end
109
+ end
110
+
111
+ def add_to_known_hosts(host)
112
+ logger.warn("[foreman_ansible] - Host #{host} not found in known_hosts")
113
+ Net::SSH::Transport::Session.new(host).host_keys.each do |host_key|
114
+ Net::SSH::KnownHosts.add(host, host_key)
115
+ end
116
+ logger.warn("[foreman_ansible] - Added host key #{host} to known_hosts")
117
+ rescue StandardError => e
118
+ logger.error('[foreman_ansible] - Failed to save host key for '\
119
+ "#{host}: #{e}")
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,38 @@
1
+ require 'fileutils'
2
+
3
+ module ForemanAnsibleCore
4
+ module TaskLauncher
5
+ class AnsibleRunner < ForemanTasksCore::TaskLauncher::AbstractGroup
6
+ def runner_input(input)
7
+ super(input).reduce({}) do |acc, (_id, data)|
8
+ acc.merge(data[:input]['action_input']['name'] => data)
9
+ end
10
+ end
11
+
12
+ def operation
13
+ 'ansible-runner'
14
+ end
15
+
16
+ def self.runner_class
17
+ Runner::AnsibleRunner
18
+ end
19
+
20
+ # def self.input_format
21
+ # {
22
+ # $UUID => {
23
+ # :execution_plan_id => $EXECUTION_PLAN_UUID,
24
+ # :run_step_id => Integer,
25
+ # :input => {
26
+ # :action_class => Class,
27
+ # :action_input => {
28
+ # "ansible_inventory"=> String,
29
+ # "hostname"=>"127.0.0.1",
30
+ # "script"=>"---\n- hosts: all\n tasks:\n - shell: |\n true\n register: out\n - debug: var=out"
31
+ # }
32
+ # }
33
+ # }
34
+ # }
35
+ # end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,23 @@
1
+ module ForemanAnsibleCore
2
+ module TaskLauncher
3
+ class Playbook < ForemanTasksCore::TaskLauncher::Batch
4
+ class PlaybookRunnerAction < ForemanTasksCore::Runner::Action
5
+ def initiate_runner
6
+ additional_options = {
7
+ :step_id => run_step_id,
8
+ :uuid => execution_plan_id
9
+ }
10
+ ::ForemanAnsibleCore::RemoteExecutionCore::AnsibleRunner.new(
11
+ input.merge(additional_options),
12
+ :suspended_action => suspended_action
13
+ )
14
+ end
15
+ end
16
+
17
+ def child_launcher(parent)
18
+ ForemanTasksCore::TaskLauncher::Single.new(world, callback, :parent => parent,
19
+ :action_class_override => PlaybookRunnerAction)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ForemanAnsibleCore
2
- VERSION = '2.1.1'.freeze
4
+ VERSION = '3.0.1'
3
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman_ansible_core
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.1
4
+ version: 3.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Lobato Garcia
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-07-19 00:00:00.000000000 Z
11
+ date: 2019-09-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubocop
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0.1'
33
+ version: 0.3.2
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0.1'
40
+ version: 0.3.2
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: foreman_remote_execution_core
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -56,16 +56,16 @@ dependencies:
56
56
  name: net-ssh
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: '4.0'
61
+ version: '0'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - "~>"
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: '4.0'
68
+ version: '0'
69
69
  description: |2
70
70
  Ansible integration with Foreman - core parts for dealing with Ansible concepts,
71
71
  usable by foreman_ansible or smart_proxy_ansible to delegate the execution.
@@ -79,12 +79,13 @@ files:
79
79
  - bin/json_inventory.sh
80
80
  - lib/foreman_ansible_core.rb
81
81
  - lib/foreman_ansible_core/actions.rb
82
- - lib/foreman_ansible_core/command_creator.rb
83
82
  - lib/foreman_ansible_core/exception.rb
84
- - lib/foreman_ansible_core/playbook_runner.rb
85
83
  - lib/foreman_ansible_core/remote_execution_core/ansible_runner.rb
86
- - lib/foreman_ansible_core/remote_execution_core/settings_override.rb
87
- - lib/foreman_ansible_core/roles_reader.rb
84
+ - lib/foreman_ansible_core/runner/ansible_runner.rb
85
+ - lib/foreman_ansible_core/runner/command_creator.rb
86
+ - lib/foreman_ansible_core/runner/playbook.rb
87
+ - lib/foreman_ansible_core/task_launcher/ansible_runner.rb
88
+ - lib/foreman_ansible_core/task_launcher/playbook.rb
88
89
  - lib/foreman_ansible_core/version.rb
89
90
  homepage: https://github.com/theforeman/foreman_ansible
90
91
  licenses:
@@ -105,8 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
105
106
  - !ruby/object:Gem::Version
106
107
  version: '0'
107
108
  requirements: []
108
- rubyforge_project:
109
- rubygems_version: 2.6.8
109
+ rubygems_version: 3.0.3
110
110
  signing_key:
111
111
  specification_version: 4
112
112
  summary: 'Ansible integration with Foreman (theforeman.org): core bits'
@@ -1,119 +0,0 @@
1
- require 'foreman_tasks_core/runner/command_runner'
2
- require_relative 'command_creator'
3
- require 'tmpdir'
4
- require 'net/ssh'
5
-
6
- module ForemanAnsibleCore
7
- # Implements ForemanTasksCore::Runner::Base interface for running
8
- # Ansible playbooks, used by the Foreman Ansible plugin and Ansible proxy
9
- class PlaybookRunner < ForemanTasksCore::Runner::CommandRunner
10
- attr_reader :command_out, :command_in, :command_pid
11
-
12
- def initialize(inventory, playbook, options = {})
13
- super
14
- @inventory = inventory
15
- unknown_hosts.each do |host|
16
- add_to_known_hosts(host)
17
- end
18
- @playbook = playbook
19
- @options = options
20
- initialize_dirs
21
- end
22
-
23
- def start
24
- write_inventory
25
- write_playbook
26
- command = CommandCreator.new(inventory_file,
27
- playbook_file,
28
- @options).command
29
- logger.debug('[foreman_ansible] - Initializing Ansible Runner')
30
- Dir.chdir(@ansible_dir) do
31
- initialize_command(*command)
32
- logger.debug("[foreman_ansible] - Running command #{command}")
33
- end
34
- end
35
-
36
- def kill
37
- publish_data('== TASK ABORTED BY USER ==', 'stdout')
38
- publish_exit_status(1)
39
- ::Process.kill('SIGTERM', @command_pid)
40
- close
41
- end
42
-
43
- def close
44
- super
45
- FileUtils.remove_entry(@working_dir) if @tmp_working_dir
46
- end
47
-
48
- private
49
-
50
- def write_inventory
51
- ensure_directory(File.dirname(inventory_file))
52
- File.write(inventory_file, @inventory)
53
- end
54
-
55
- def write_playbook
56
- ensure_directory(File.dirname(playbook_file))
57
- File.write(playbook_file, @playbook)
58
- end
59
-
60
- def inventory_file
61
- File.join(@working_dir, 'foreman-inventories', id)
62
- end
63
-
64
- def playbook_file
65
- File.join(@working_dir, "foreman-playbook-#{id}.yml")
66
- end
67
-
68
- def events_dir
69
- File.join(@working_dir, 'events', id.to_s)
70
- end
71
-
72
- def ensure_directory(path)
73
- if File.exist?(path)
74
- raise "#{path} expected to be a directory" unless File.directory?(path)
75
- else
76
- FileUtils.mkdir_p(path)
77
- end
78
- path
79
- end
80
-
81
- def initialize_dirs
82
- settings = ForemanAnsibleCore.settings
83
- initialize_working_dir(settings[:working_dir])
84
- initialize_ansible_dir(settings[:ansible_dir])
85
- end
86
-
87
- def initialize_working_dir(working_dir)
88
- if working_dir.nil?
89
- @working_dir = Dir.mktmpdir
90
- @tmp_working_dir = true
91
- else
92
- @working_dir = File.expand_path(working_dir)
93
- end
94
- end
95
-
96
- def initialize_ansible_dir(ansible_dir)
97
- raise "Ansible dir #{ansible_dir} does not exist" unless
98
- !ansible_dir.nil? && File.exist?(ansible_dir)
99
- @ansible_dir = ansible_dir
100
- end
101
-
102
- def unknown_hosts
103
- JSON.parse(@inventory)['all']['hosts'].select do |host|
104
- Net::SSH::KnownHosts.search_for(host).empty?
105
- end
106
- end
107
-
108
- def add_to_known_hosts(host)
109
- logger.warn("[foreman_ansible] - Host #{host} not found in known_hosts")
110
- Net::SSH::Transport::Session.new(host).host_keys.each do |host_key|
111
- Net::SSH::KnownHosts.add(host, host_key)
112
- end
113
- logger.warn("[foreman_ansible] - Added host key #{host} to known_hosts")
114
- rescue StandardError => e
115
- logger.error('[foreman_ansible] - Failed to save host key for '\
116
- "#{host}: #{e}")
117
- end
118
- end
119
- end
@@ -1,18 +0,0 @@
1
- module ForemanAnsibleCore
2
- module RemoteExecutionCore
3
- # Ensure the Ansible provider is used whenever a JobTemplate using this
4
- # provider is called.
5
- module SettingsOverride
6
- def initiate_runner
7
- return super unless input['ansible_inventory']
8
- additional_options = {
9
- :step_id => run_step_id,
10
- :uuid => execution_plan_id
11
- }
12
- ::ForemanAnsibleCore::RemoteExecutionCore::AnsibleRunner.new(
13
- input.merge(additional_options)
14
- )
15
- end
16
- end
17
- end
18
- end
@@ -1,61 +0,0 @@
1
- module ForemanAnsibleCore
2
- # Implements the logic needed to read the roles and associated information
3
- class RolesReader
4
- class << self
5
- DEFAULT_CONFIG_FILE = '/etc/ansible/ansible.cfg'.freeze
6
- DEFAULT_ROLES_PATH = '/etc/ansible/roles'.freeze
7
-
8
- def list_roles
9
- roles_path.split(':').map { |path| read_roles(path) }.flatten
10
- end
11
-
12
- def roles_path(roles_line = roles_path_from_config)
13
- # Default to /etc/ansible/roles if none found
14
- return DEFAULT_ROLES_PATH if roles_line.empty?
15
- roles_path_key = roles_line.first.split('=').first.strip
16
- # In case of commented roles_path key "#roles_path", return default
17
- return DEFAULT_ROLES_PATH unless roles_path_key == 'roles_path'
18
- roles_line.first.split('=').last.strip
19
- end
20
-
21
- def logger
22
- # Return a different logger depending on where ForemanAnsibleCore is
23
- # running from
24
- if defined?(::Foreman::Logging)
25
- ::Foreman::Logging.logger('foreman_ansible')
26
- else
27
- ::Proxy::LogBuffer::Decorator.instance
28
- end
29
- end
30
-
31
- private
32
-
33
- def read_roles(roles_path)
34
- rescue_and_raise_file_exception ReadRolesException,
35
- roles_path, 'roles' do
36
- Dir.glob("#{roles_path}/*").map do |path|
37
- path.split('/').last
38
- end
39
- end
40
- end
41
-
42
- def roles_path_from_config
43
- rescue_and_raise_file_exception ReadConfigFileException,
44
- DEFAULT_CONFIG_FILE, 'config file' do
45
- File.readlines(DEFAULT_CONFIG_FILE).select do |line|
46
- line =~ /roles_path/
47
- end
48
- end
49
- end
50
-
51
- def rescue_and_raise_file_exception(exception, path, type)
52
- yield
53
- rescue Errno::ENOENT, Errno::EACCES => e
54
- logger.debug(e.backtrace)
55
- exception_message = "Could not read Ansible #{type} "\
56
- "#{path} - #{e.message}"
57
- raise exception.new(exception_message)
58
- end
59
- end
60
- end
61
- end