smart_proxy_ansible 2.0.1 → 2.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/lib/foreman_ansible_core.rb +37 -0
- data/lib/foreman_ansible_core/actions.rb +17 -0
- data/lib/foreman_ansible_core/command_creator.rb +44 -0
- data/lib/foreman_ansible_core/exception.rb +35 -0
- data/lib/foreman_ansible_core/playbook_runner.rb +98 -0
- data/lib/foreman_ansible_core/remote_execution_core/ansible_runner.rb +42 -0
- data/lib/foreman_ansible_core/remote_execution_core/settings_override.rb +18 -0
- data/lib/foreman_ansible_core/roles_reader.rb +61 -0
- data/lib/foreman_ansible_core/version.rb +3 -0
- data/lib/smart_proxy_ansible/api.rb +4 -2
- data/lib/smart_proxy_ansible/version.rb +1 -1
- metadata +12 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 23fef38e05c69af60a7c0d04af33bd49412e60507e869836ff2febcba0bfbad9
|
4
|
+
data.tar.gz: b42a49e027b08e77ebf55098b59f8a627ece791c8200d9e3c63eb68f6597002f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b30a91e5065922bb76c0f71e5b3be3762738442848bcbaad9f49ff1cd8ec4cb957fe23db7f6606d98be55019ef909c9a2ab87cca1efc611d6582252356894e20
|
7
|
+
data.tar.gz: f6be9a1240b73ac6dc9872fb32a4ffbe24769a54d57d566e4697ab4d81bf6060c34544972d9699de482e67c68aa5d15de1ec8296b3400bdfee929fda8b39d065
|
@@ -0,0 +1,37 @@
|
|
1
|
+
begin
|
2
|
+
require 'foreman_tasks_core'
|
3
|
+
require 'foreman_remote_execution_core'
|
4
|
+
rescue LoadError
|
5
|
+
# These gems are not available in a proxy SCLed context
|
6
|
+
puts 'Running Foreman Ansible Core in non-SCL context'
|
7
|
+
end
|
8
|
+
|
9
|
+
# Core actions for Foreman Ansible, used by both Foreman and Foreman proxy
|
10
|
+
# This comprises running playbooks for the moment
|
11
|
+
module ForemanAnsibleCore
|
12
|
+
require 'foreman_ansible_core/exception'
|
13
|
+
require 'foreman_ansible_core/roles_reader'
|
14
|
+
require 'foreman_ansible_core/version'
|
15
|
+
|
16
|
+
if defined? ForemanTasksCore
|
17
|
+
extend ForemanTasksCore::SettingsLoader
|
18
|
+
register_settings(:ansible, :ansible_dir => '/etc/ansible',
|
19
|
+
:working_dir => nil)
|
20
|
+
|
21
|
+
if ForemanTasksCore.dynflow_present?
|
22
|
+
require 'foreman_tasks_core/runner'
|
23
|
+
require 'foreman_ansible_core/playbook_runner'
|
24
|
+
require 'foreman_ansible_core/actions'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
if defined? ForemanTasksCore
|
29
|
+
require 'foreman_remote_execution_core/actions'
|
30
|
+
require 'foreman_ansible_core/remote_execution_core/ansible_runner'
|
31
|
+
require 'foreman_ansible_core/remote_execution_core/settings_override'
|
32
|
+
ForemanRemoteExecutionCore::Actions::RunScript.send(
|
33
|
+
:prepend,
|
34
|
+
ForemanAnsibleCore::RemoteExecutionCore::SettingsOverride
|
35
|
+
)
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'foreman_tasks_core/shareable_action'
|
2
|
+
|
3
|
+
module ForemanAnsibleCore
|
4
|
+
module Actions
|
5
|
+
# Action that can be run both on Foreman or Foreman proxy side
|
6
|
+
# to execute the playbook run
|
7
|
+
class RunPlaybook < ForemanTasksCore::Runner::Action
|
8
|
+
def initiate_runner
|
9
|
+
ForemanAnsibleCore::PlaybookRunner.new(
|
10
|
+
input[:inventory],
|
11
|
+
input[:playbook],
|
12
|
+
input[:options]
|
13
|
+
)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module ForemanAnsibleCore
|
2
|
+
# Creates the actual command to be passed to foreman_tasks_core to run
|
3
|
+
class CommandCreator
|
4
|
+
attr_reader :command
|
5
|
+
|
6
|
+
def initialize(inventory_file, playbook_file, options = {})
|
7
|
+
@options = options
|
8
|
+
@command = [{ 'JSON_INVENTORY_FILE' => inventory_file }]
|
9
|
+
@command << 'ansible-playbook'
|
10
|
+
@command = command_options(@command)
|
11
|
+
@command << playbook_file
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def command_options(command)
|
17
|
+
command.concat(['-i', json_inventory_script])
|
18
|
+
command.concat([setup_verbosity]) if verbose?
|
19
|
+
command.concat(['-T', @options[:timeout]]) unless @options[:timeout].nil?
|
20
|
+
command
|
21
|
+
end
|
22
|
+
|
23
|
+
def json_inventory_script
|
24
|
+
File.expand_path('../../bin/json_inventory.sh', File.dirname(__FILE__))
|
25
|
+
end
|
26
|
+
|
27
|
+
def setup_verbosity
|
28
|
+
verbosity_level = @options[:verbosity_level].to_i
|
29
|
+
verbosity = '-'
|
30
|
+
verbosity_level.times do
|
31
|
+
verbosity += 'v'
|
32
|
+
end
|
33
|
+
verbosity
|
34
|
+
end
|
35
|
+
|
36
|
+
def verbose?
|
37
|
+
verbosity_level = @options[:verbosity_level]
|
38
|
+
# rubocop:disable Rails/Present
|
39
|
+
!verbosity_level.nil? && !verbosity_level.empty? &&
|
40
|
+
verbosity_level.to_i > 0
|
41
|
+
# rubocop:enable Rails/Present
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module ForemanAnsibleCore
|
2
|
+
# Taken from Foreman core, this class creates an error code for any exception
|
3
|
+
class Exception < ::StandardError
|
4
|
+
def initialize(message, *params)
|
5
|
+
@message = message
|
6
|
+
@params = params
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.calculate_error_code(classname, message)
|
10
|
+
return 'ERF00-0000' if classname.nil? || message.nil?
|
11
|
+
basename = classname.split(':').last
|
12
|
+
class_hash = Zlib.crc32(basename) % 100
|
13
|
+
msg_hash = Zlib.crc32(message) % 10_000
|
14
|
+
format 'ERF%02d-%04d', class_hash, msg_hash
|
15
|
+
end
|
16
|
+
|
17
|
+
def code
|
18
|
+
@code ||= Exception.calculate_error_code(self.class.name, @message)
|
19
|
+
@code
|
20
|
+
end
|
21
|
+
|
22
|
+
def message
|
23
|
+
# make sure it works without gettext too
|
24
|
+
translated_msg = @message % @params
|
25
|
+
"#{code} [#{self.class.name}]: #{translated_msg}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
message
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class ReadConfigFileException < ForemanAnsibleCore::Exception; end
|
34
|
+
class ReadRolesException < ForemanAnsibleCore::Exception; end
|
35
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'foreman_tasks_core/runner/command_runner'
|
2
|
+
require_relative 'command_creator'
|
3
|
+
require 'tmpdir'
|
4
|
+
|
5
|
+
module ForemanAnsibleCore
|
6
|
+
# Implements ForemanTasksCore::Runner::Base interface for running
|
7
|
+
# Ansible playbooks, used by the Foreman Ansible plugin and Ansible proxy
|
8
|
+
class PlaybookRunner < ForemanTasksCore::Runner::CommandRunner
|
9
|
+
attr_reader :command_out, :command_in, :command_pid
|
10
|
+
|
11
|
+
def initialize(inventory, playbook, options = {})
|
12
|
+
super
|
13
|
+
@inventory = inventory
|
14
|
+
@playbook = playbook
|
15
|
+
@options = options
|
16
|
+
initialize_dirs
|
17
|
+
end
|
18
|
+
|
19
|
+
def start
|
20
|
+
write_inventory
|
21
|
+
write_playbook
|
22
|
+
command = CommandCreator.new(inventory_file,
|
23
|
+
playbook_file,
|
24
|
+
@options).command
|
25
|
+
logger.debug('[foreman_ansible] - Initializing Ansible Runner')
|
26
|
+
Dir.chdir(@ansible_dir) do
|
27
|
+
initialize_command(*command)
|
28
|
+
logger.debug("[foreman_ansible] - Running command #{command}")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def kill
|
33
|
+
publish_data('== TASK ABORTED BY USER ==', 'stdout')
|
34
|
+
publish_exit_status(1)
|
35
|
+
::Process.kill('SIGTERM', @command_pid)
|
36
|
+
close
|
37
|
+
end
|
38
|
+
|
39
|
+
def close
|
40
|
+
super
|
41
|
+
FileUtils.remove_entry(@working_dir) if @tmp_working_dir
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def write_inventory
|
47
|
+
ensure_directory(File.dirname(inventory_file))
|
48
|
+
File.write(inventory_file, @inventory)
|
49
|
+
end
|
50
|
+
|
51
|
+
def write_playbook
|
52
|
+
ensure_directory(File.dirname(playbook_file))
|
53
|
+
File.write(playbook_file, @playbook)
|
54
|
+
end
|
55
|
+
|
56
|
+
def inventory_file
|
57
|
+
File.join(@working_dir, 'foreman-inventories', id)
|
58
|
+
end
|
59
|
+
|
60
|
+
def playbook_file
|
61
|
+
File.join(@working_dir, "foreman-playbook-#{id}.yml")
|
62
|
+
end
|
63
|
+
|
64
|
+
def events_dir
|
65
|
+
File.join(@working_dir, 'events', id.to_s)
|
66
|
+
end
|
67
|
+
|
68
|
+
def ensure_directory(path)
|
69
|
+
if File.exist?(path)
|
70
|
+
raise "#{path} expected to be a directory" unless File.directory?(path)
|
71
|
+
else
|
72
|
+
FileUtils.mkdir_p(path)
|
73
|
+
end
|
74
|
+
path
|
75
|
+
end
|
76
|
+
|
77
|
+
def initialize_dirs
|
78
|
+
settings = ForemanAnsibleCore.settings
|
79
|
+
initialize_working_dir(settings[:working_dir])
|
80
|
+
initialize_ansible_dir(settings[:ansible_dir])
|
81
|
+
end
|
82
|
+
|
83
|
+
def initialize_working_dir(working_dir)
|
84
|
+
if working_dir.nil?
|
85
|
+
@working_dir = Dir.mktmpdir
|
86
|
+
@tmp_working_dir = true
|
87
|
+
else
|
88
|
+
@working_dir = File.expand_path(working_dir)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def initialize_ansible_dir(ansible_dir)
|
93
|
+
raise "Ansible dir #{ansible_dir} does not exist" unless
|
94
|
+
!ansible_dir.nil? && File.exist?(ansible_dir)
|
95
|
+
@ansible_dir = ansible_dir
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module ForemanAnsibleCore
|
2
|
+
module RemoteExecutionCore
|
3
|
+
# Takes an inventory and runs it through REXCore CommandRunner
|
4
|
+
class AnsibleRunner < ::ForemanTasksCore::Runner::CommandRunner
|
5
|
+
DEFAULT_REFRESH_INTERVAL = 1
|
6
|
+
|
7
|
+
def initialize(options)
|
8
|
+
super(options)
|
9
|
+
@playbook_runner = ForemanAnsibleCore::PlaybookRunner.new(
|
10
|
+
options['ansible_inventory'],
|
11
|
+
options['script'],
|
12
|
+
options
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
def start
|
17
|
+
@playbook_runner.start
|
18
|
+
rescue StandardError => e
|
19
|
+
logger.error(
|
20
|
+
'error while initalizing command'\
|
21
|
+
" #{e.class} #{e.message}:\n #{e.backtrace.join("\n")}"
|
22
|
+
)
|
23
|
+
publish_exception('Error initializing command', e)
|
24
|
+
end
|
25
|
+
|
26
|
+
def fill_continuous_output(continuous_output)
|
27
|
+
delegated_output.fetch('result', []).each do |raw_output|
|
28
|
+
continuous_output.add_raw_output(raw_output)
|
29
|
+
end
|
30
|
+
rescue StandardError => e
|
31
|
+
continuous_output.add_exception(_('Error loading data from proxy'), e)
|
32
|
+
end
|
33
|
+
|
34
|
+
def refresh
|
35
|
+
@command_out = @playbook_runner.command_out
|
36
|
+
@command_in = @playbook_runner.command_in
|
37
|
+
@command_pid = @playbook_runner.command_pid
|
38
|
+
super
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,18 @@
|
|
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
|
@@ -0,0 +1,61 @@
|
|
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
|
@@ -1,14 +1,16 @@
|
|
1
|
+
require 'foreman_ansible_core'
|
2
|
+
|
1
3
|
module Proxy
|
2
4
|
module Ansible
|
3
5
|
class Api < Sinatra::Base
|
4
6
|
get '/roles' do
|
5
|
-
ForemanAnsibleCore::RolesReader.list_roles.to_json
|
7
|
+
::ForemanAnsibleCore::RolesReader.list_roles.to_json
|
6
8
|
end
|
7
9
|
|
8
10
|
get '/roles/:role_name/variables' do |role_name|
|
9
11
|
# not anything matching item, }}, {{, ansible_hostname or 'if'
|
10
12
|
ansible_config = '/etc/ansible/ansible.cfg'
|
11
|
-
roles_path = ForemanAnsibleCore::RolesReader.roles_path(ansible_config)
|
13
|
+
roles_path = ::ForemanAnsibleCore::RolesReader.roles_path(ansible_config)
|
12
14
|
role_files = Dir.glob("#{roles_path}/#{role_name}/**/*.yml")
|
13
15
|
variables = role_files.map do |role_file|
|
14
16
|
File.read(role_file).scan(/{{(.*?)}}/).select do |param|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smart_proxy_ansible
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ivan Nečas
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2018-
|
12
|
+
date: 2018-03-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -136,6 +136,15 @@ files:
|
|
136
136
|
- LICENSE
|
137
137
|
- README.md
|
138
138
|
- bundler.plugins.d/smart_proxy_ansible.rb
|
139
|
+
- lib/foreman_ansible_core.rb
|
140
|
+
- lib/foreman_ansible_core/actions.rb
|
141
|
+
- lib/foreman_ansible_core/command_creator.rb
|
142
|
+
- lib/foreman_ansible_core/exception.rb
|
143
|
+
- lib/foreman_ansible_core/playbook_runner.rb
|
144
|
+
- lib/foreman_ansible_core/remote_execution_core/ansible_runner.rb
|
145
|
+
- lib/foreman_ansible_core/remote_execution_core/settings_override.rb
|
146
|
+
- lib/foreman_ansible_core/roles_reader.rb
|
147
|
+
- lib/foreman_ansible_core/version.rb
|
139
148
|
- lib/smart_proxy_ansible.rb
|
140
149
|
- lib/smart_proxy_ansible/api.rb
|
141
150
|
- lib/smart_proxy_ansible/http_config.ru
|
@@ -162,7 +171,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
162
171
|
version: '0'
|
163
172
|
requirements: []
|
164
173
|
rubyforge_project:
|
165
|
-
rubygems_version: 2.
|
174
|
+
rubygems_version: 2.7.3
|
166
175
|
signing_key:
|
167
176
|
specification_version: 4
|
168
177
|
summary: Smart-Proxy Ansible plugin
|