smart_proxy_ansible 2.0.3 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/lib/smart_proxy_ansible.rb +3 -0
- data/lib/smart_proxy_ansible/api.rb +24 -14
- data/lib/smart_proxy_ansible/exception.rb +40 -0
- data/lib/smart_proxy_ansible/plugin.rb +10 -16
- data/lib/smart_proxy_ansible/roles_reader.rb +65 -0
- data/lib/smart_proxy_ansible/variables_extractor.rb +33 -0
- data/lib/smart_proxy_ansible/version.rb +3 -1
- metadata +34 -12
- data/lib/foreman_ansible_core.rb +0 -37
- data/lib/foreman_ansible_core/actions.rb +0 -17
- data/lib/foreman_ansible_core/command_creator.rb +0 -44
- data/lib/foreman_ansible_core/exception.rb +0 -35
- data/lib/foreman_ansible_core/playbook_runner.rb +0 -98
- data/lib/foreman_ansible_core/remote_execution_core/ansible_runner.rb +0 -42
- data/lib/foreman_ansible_core/remote_execution_core/settings_override.rb +0 -18
- data/lib/foreman_ansible_core/roles_reader.rb +0 -61
- data/lib/foreman_ansible_core/version.rb +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ce925932dfc2ddf578d2ae15b293ee1a223b8808
|
4
|
+
data.tar.gz: 54f6dd7b4d48229be33aef44b7445d8284a7d734
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a88b68d82b25d7baeff478651fdf159c512be92262d2c7c5ed5af5ed204915043ab08dbe3d7b3200a7261226f0ece19f2e803be10d3e25e105caa3f6d46bc97e
|
7
|
+
data.tar.gz: 0c57bf4282a7f4e9699b9108e3e89817a2e2ded17ef448d8d51ae5eeede0a93d8947673d3df034d775fee2238fdb5c2fc40a707c1acc77aa5044953c062c0e97
|
data/lib/smart_proxy_ansible.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
require 'smart_proxy_dynflow'
|
2
2
|
|
3
3
|
module Proxy
|
4
|
+
# Basic requires for this plugin
|
4
5
|
module Ansible
|
5
6
|
require 'smart_proxy_ansible/version'
|
6
7
|
require 'smart_proxy_ansible/plugin'
|
8
|
+
require 'smart_proxy_ansible/roles_reader'
|
9
|
+
require 'smart_proxy_ansible/variables_extractor'
|
7
10
|
end
|
8
11
|
end
|
@@ -1,24 +1,34 @@
|
|
1
|
-
require 'foreman_ansible_core'
|
2
|
-
|
3
1
|
module Proxy
|
4
2
|
module Ansible
|
3
|
+
# API endpoints. Most of the code should be calling other classes,
|
4
|
+
# please keep the actual implementation of the endpoints outside
|
5
|
+
# of this class.
|
5
6
|
class Api < Sinatra::Base
|
6
7
|
get '/roles' do
|
7
|
-
|
8
|
+
RolesReader.list_roles.to_json
|
9
|
+
end
|
10
|
+
|
11
|
+
get '/roles/variables' do
|
12
|
+
variables = {}
|
13
|
+
RolesReader.list_roles.each do |role_name|
|
14
|
+
variables[role_name] = extract_variables(role_name)[role_name]
|
15
|
+
end
|
16
|
+
variables.to_json
|
8
17
|
end
|
9
18
|
|
10
19
|
get '/roles/:role_name/variables' do |role_name|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
20
|
+
extract_variables(role_name).to_json
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def extract_variables(role_name)
|
26
|
+
variables = {}
|
27
|
+
RolesReader.roles_path.split(':').each do |path|
|
28
|
+
variables[role_name] = VariablesExtractor
|
29
|
+
.extract_variables("#{path}/#{role_name}")
|
30
|
+
end
|
31
|
+
variables
|
22
32
|
end
|
23
33
|
end
|
24
34
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Proxy
|
4
|
+
module Ansible
|
5
|
+
# Taken from Foreman core, this class creates an error code for any
|
6
|
+
# exception
|
7
|
+
class Exception < ::StandardError
|
8
|
+
def initialize(message, *params)
|
9
|
+
@message = message
|
10
|
+
@params = params
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.calculate_error_code(classname, message)
|
14
|
+
return 'ERF00-0000' if classname.nil? || message.nil?
|
15
|
+
basename = classname.split(':').last
|
16
|
+
class_hash = Zlib.crc32(basename) % 100
|
17
|
+
msg_hash = Zlib.crc32(message) % 10_000
|
18
|
+
format 'ERF%02d-%04d', class_hash, msg_hash
|
19
|
+
end
|
20
|
+
|
21
|
+
def code
|
22
|
+
@code ||= Exception.calculate_error_code(self.class.name, @message)
|
23
|
+
@code
|
24
|
+
end
|
25
|
+
|
26
|
+
def message
|
27
|
+
# make sure it works without gettext too
|
28
|
+
translated_msg = @message % @params
|
29
|
+
"#{code} [#{self.class.name}]: #{translated_msg}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_s
|
33
|
+
message
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class ReadConfigFileException < Proxy::Ansible::Exception; end
|
38
|
+
class ReadRolesException < Proxy::Ansible::Exception; end
|
39
|
+
end
|
40
|
+
end
|
@@ -1,20 +1,14 @@
|
|
1
|
-
module Proxy
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
module Proxy
|
2
|
+
module Ansible
|
3
|
+
# Calls for the smart-proxy API to register the plugin
|
4
|
+
class Plugin < Proxy::Plugin
|
5
|
+
http_rackup_path File.expand_path('http_config.ru',
|
6
|
+
File.expand_path('../', __FILE__))
|
7
|
+
https_rackup_path File.expand_path('http_config.ru',
|
8
|
+
File.expand_path('../', __FILE__))
|
5
9
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
after_activation do
|
10
|
-
begin
|
11
|
-
require 'smart_proxy_dynflow_core'
|
12
|
-
require 'foreman_ansible_core'
|
13
|
-
ForemanAnsibleCore.initialize_settings(Proxy::Ansible::Plugin.settings.to_h)
|
14
|
-
rescue LoadError => _
|
15
|
-
# Dynflow core is not available in the proxy, will be handled
|
16
|
-
# by standalone Dynflow core
|
17
|
-
end
|
10
|
+
settings_file 'ansible.yml'
|
11
|
+
plugin :ansible, Proxy::Ansible::VERSION
|
18
12
|
end
|
19
13
|
end
|
20
14
|
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require_relative 'exception'
|
2
|
+
|
3
|
+
module Proxy
|
4
|
+
module Ansible
|
5
|
+
# Implements the logic needed to read the roles and associated information
|
6
|
+
class RolesReader
|
7
|
+
class << self
|
8
|
+
DEFAULT_CONFIG_FILE = '/etc/ansible/ansible.cfg'.freeze
|
9
|
+
DEFAULT_ROLES_PATH = '/etc/ansible/roles'.freeze
|
10
|
+
|
11
|
+
def list_roles
|
12
|
+
roles_path.split(':').map { |path| read_roles(path) }.flatten
|
13
|
+
end
|
14
|
+
|
15
|
+
def roles_path(roles_line = roles_path_from_config)
|
16
|
+
# Default to /etc/ansible/roles if none found
|
17
|
+
return DEFAULT_ROLES_PATH if roles_line.empty?
|
18
|
+
roles_path_key = roles_line.first.split('=').first.strip
|
19
|
+
# In case of commented roles_path key "#roles_path", return default
|
20
|
+
return DEFAULT_ROLES_PATH unless roles_path_key == 'roles_path'
|
21
|
+
roles_line.first.split('=').last.strip
|
22
|
+
end
|
23
|
+
|
24
|
+
def logger
|
25
|
+
# Return a different logger depending on where ForemanAnsibleCore is
|
26
|
+
# running from
|
27
|
+
if defined?(::Foreman::Logging)
|
28
|
+
::Foreman::Logging.logger('foreman_ansible')
|
29
|
+
else
|
30
|
+
::Proxy::LogBuffer::Decorator.instance
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def read_roles(roles_path)
|
37
|
+
rescue_and_raise_file_exception ReadRolesException,
|
38
|
+
roles_path, 'roles' do
|
39
|
+
Dir.glob("#{roles_path}/*").map do |path|
|
40
|
+
path.split('/').last
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def roles_path_from_config
|
46
|
+
rescue_and_raise_file_exception ReadConfigFileException,
|
47
|
+
DEFAULT_CONFIG_FILE, 'config file' do
|
48
|
+
File.readlines(DEFAULT_CONFIG_FILE).select do |line|
|
49
|
+
line =~ /^\s*roles_path/
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def rescue_and_raise_file_exception(exception, path, type)
|
55
|
+
yield
|
56
|
+
rescue Errno::ENOENT, Errno::EACCES => e
|
57
|
+
logger.debug(e.backtrace)
|
58
|
+
exception_message = "Could not read Ansible #{type} "\
|
59
|
+
"#{path} - #{e.message}"
|
60
|
+
raise exception.new(exception_message), exception_message
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Proxy
|
2
|
+
module Ansible
|
3
|
+
# Implements the logic needed to read the roles and associated information
|
4
|
+
class VariablesExtractor
|
5
|
+
class << self
|
6
|
+
def extract_variables(role_path)
|
7
|
+
role_files = Dir.glob("#{role_path}/defaults/**/*.yml") +
|
8
|
+
Dir.glob("#{role_path}/defaults/**/*.yaml")
|
9
|
+
# not anything matching item, }}, {{, ansible_hostname or 'if'
|
10
|
+
variables = role_files.map do |role_file|
|
11
|
+
candidates = File.read(role_file)
|
12
|
+
.scan(/{{(.*?)}}/).select do |param|
|
13
|
+
param.first.scan(/item/) == [] && param.first.scan(/if/) == []
|
14
|
+
end.flatten
|
15
|
+
# Sometimes inside the {{ }} there's a OR condition. In such a case,
|
16
|
+
# let's split and choose possible variables (variables cannot
|
17
|
+
# contain parenthesis)
|
18
|
+
|
19
|
+
candidates.map do |variable|
|
20
|
+
variable.split('|').map(&:strip).select do |var|
|
21
|
+
!var.include?('(') && # variables are not parenthesis
|
22
|
+
!var.include?('[') && # variables are not arrays
|
23
|
+
!var.include?('.') && # variables are not objects
|
24
|
+
!var.include?("'") # variables are not plain strings
|
25
|
+
end
|
26
|
+
end unless candidates.nil?
|
27
|
+
end.compact.flatten.uniq.map(&:strip)
|
28
|
+
variables
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
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.1.0
|
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-12-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -109,6 +109,34 @@ dependencies:
|
|
109
109
|
- - '='
|
110
110
|
- !ruby/object:Gem::Version
|
111
111
|
version: 0.32.1
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: logger
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: smart_proxy
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: '0'
|
133
|
+
type: :development
|
134
|
+
prerelease: false
|
135
|
+
version_requirements: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
112
140
|
- !ruby/object:Gem::Dependency
|
113
141
|
name: smart_proxy_dynflow
|
114
142
|
requirement: !ruby/object:Gem::Requirement
|
@@ -137,19 +165,13 @@ files:
|
|
137
165
|
- README.md
|
138
166
|
- bin/json_inventory.sh
|
139
167
|
- bundler.plugins.d/smart_proxy_ansible.rb
|
140
|
-
- lib/foreman_ansible_core.rb
|
141
|
-
- lib/foreman_ansible_core/actions.rb
|
142
|
-
- lib/foreman_ansible_core/command_creator.rb
|
143
|
-
- lib/foreman_ansible_core/exception.rb
|
144
|
-
- lib/foreman_ansible_core/playbook_runner.rb
|
145
|
-
- lib/foreman_ansible_core/remote_execution_core/ansible_runner.rb
|
146
|
-
- lib/foreman_ansible_core/remote_execution_core/settings_override.rb
|
147
|
-
- lib/foreman_ansible_core/roles_reader.rb
|
148
|
-
- lib/foreman_ansible_core/version.rb
|
149
168
|
- lib/smart_proxy_ansible.rb
|
150
169
|
- lib/smart_proxy_ansible/api.rb
|
170
|
+
- lib/smart_proxy_ansible/exception.rb
|
151
171
|
- lib/smart_proxy_ansible/http_config.ru
|
152
172
|
- lib/smart_proxy_ansible/plugin.rb
|
173
|
+
- lib/smart_proxy_ansible/roles_reader.rb
|
174
|
+
- lib/smart_proxy_ansible/variables_extractor.rb
|
153
175
|
- lib/smart_proxy_ansible/version.rb
|
154
176
|
- settings.d/ansible.yml.example
|
155
177
|
homepage: https://github.com/theforeman/smart_proxy_ansible
|
@@ -172,7 +194,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
172
194
|
version: '0'
|
173
195
|
requirements: []
|
174
196
|
rubyforge_project:
|
175
|
-
rubygems_version: 2.
|
197
|
+
rubygems_version: 2.6.8
|
176
198
|
signing_key:
|
177
199
|
specification_version: 4
|
178
200
|
summary: Smart-Proxy Ansible plugin
|
data/lib/foreman_ansible_core.rb
DELETED
@@ -1,37 +0,0 @@
|
|
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
|
@@ -1,17 +0,0 @@
|
|
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
|
@@ -1,44 +0,0 @@
|
|
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
|
@@ -1,35 +0,0 @@
|
|
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
|
@@ -1,98 +0,0 @@
|
|
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
|
@@ -1,42 +0,0 @@
|
|
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
|
@@ -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
|