smart_proxy_ansible 2.0.1 → 2.0.2

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: 420694bc9e01ed41e40bfccadba84b8457956c26
4
- data.tar.gz: fef11955d696cb2419a2946399d461bbe1a37d5c
2
+ SHA256:
3
+ metadata.gz: 23fef38e05c69af60a7c0d04af33bd49412e60507e869836ff2febcba0bfbad9
4
+ data.tar.gz: b42a49e027b08e77ebf55098b59f8a627ece791c8200d9e3c63eb68f6597002f
5
5
  SHA512:
6
- metadata.gz: 0b9063f73310b470484dfb5d7bcafd9cbc29ede72df76ab3ba8de46e680622ead09f5209d2d5d735e3b2f81e859a779e317b5bb4159d9e7a2ec09d0a805ab2e1
7
- data.tar.gz: 332a065c6ae3a3f150a50f281ea1a5e15a9d693351933690974583dd58159fddc4acd007bcd1fc80345cf3f28278aa9e8e54bcd5a42bc92ff43c694e338769b8
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
@@ -0,0 +1,3 @@
1
+ module ForemanAnsibleCore
2
+ VERSION = '2.0.2'.freeze
3
+ 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|
@@ -1,5 +1,5 @@
1
1
  module Proxy
2
2
  module Ansible
3
- VERSION = '2.0.1'
3
+ VERSION = '2.0.2'
4
4
  end
5
5
  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.1
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-02-12 00:00:00.000000000 Z
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.6.14
174
+ rubygems_version: 2.7.3
166
175
  signing_key:
167
176
  specification_version: 4
168
177
  summary: Smart-Proxy Ansible plugin