foreman_ansible 1.0 → 1.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of foreman_ansible might be problematic. Click here for more details.

Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +6 -27
  3. data/app/controllers/ansible_roles_controller.rb +55 -0
  4. data/app/controllers/api/v2/ansible_roles_controller.rb +52 -0
  5. data/app/controllers/foreman_ansible/concerns/hosts_controller_extensions.rb +11 -10
  6. data/app/helpers/foreman_ansible/ansible_plugin_helper.rb +8 -0
  7. data/app/helpers/foreman_ansible/ansible_reports_helper.rb +6 -4
  8. data/app/helpers/foreman_ansible/ansible_roles_helper.rb +17 -3
  9. data/app/helpers/foreman_ansible/hosts_helper_extensions.rb +4 -3
  10. data/app/lib/actions/foreman_ansible/play_host_roles.rb +56 -0
  11. data/app/lib/actions/foreman_ansible/play_hosts_roles.rb +26 -0
  12. data/app/lib/proxy_api/ansible.rb +28 -0
  13. data/app/models/ansible_role.rb +8 -0
  14. data/app/models/concerns/foreman_ansible/has_many_ansible_roles.rb +13 -0
  15. data/app/models/concerns/foreman_ansible/host_managed_extensions.rb +5 -1
  16. data/app/models/concerns/foreman_ansible/hostgroup_extensions.rb +19 -0
  17. data/app/models/host_ansible_role.rb +0 -2
  18. data/app/models/hostgroup_ansible_role.rb +8 -0
  19. data/app/overrides/ansible_roles_tab.rb +2 -2
  20. data/app/overrides/hostgroup_ansible_roles_tab.rb +14 -0
  21. data/app/services/foreman_ansible/api_roles_importer.rb +16 -0
  22. data/app/services/foreman_ansible/fact_importer.rb +2 -1
  23. data/app/services/foreman_ansible/inventory_creator.rb +37 -5
  24. data/app/services/foreman_ansible/playbook_creator.rb +3 -4
  25. data/app/services/foreman_ansible/proxy_selector.rb +19 -0
  26. data/app/services/foreman_ansible/roles_importer.rb +42 -15
  27. data/app/services/foreman_ansible/ui_roles_importer.rb +26 -0
  28. data/app/views/ansible_roles/import.html.erb +51 -0
  29. data/app/views/ansible_roles/index.html.erb +30 -0
  30. data/app/views/ansible_roles/welcome.html.erb +14 -0
  31. data/app/views/api/v2/ansible_roles/import.json.rabl +3 -0
  32. data/app/views/api/v2/ansible_roles/index.json.rabl +3 -0
  33. data/app/views/api/v2/ansible_roles/obsolete.json.rabl +3 -0
  34. data/app/views/api/v2/ansible_roles/show.json.rabl +3 -0
  35. data/app/views/foreman_ansible/ansible_roles/_select_tab_content.html.erb +6 -0
  36. data/app/views/foreman_ansible/{hosts/_tab_title.html.erb → ansible_roles/_select_tab_title.html.erb} +0 -0
  37. data/config/routes.rb +25 -2
  38. data/db/migrate/20160705082036_create_ansible_role.rb +2 -1
  39. data/db/migrate/20160706074540_create_join_table_hosts_ansible_roles.rb +1 -0
  40. data/db/migrate/20160707195442_create_host_ansible_roles.rb +1 -0
  41. data/db/migrate/20160729094457_add_columns_to_ansible_role.rb +12 -0
  42. data/db/migrate/20160802153302_create_join_table_hostgroup_ansible_roles.rb +10 -0
  43. data/db/migrate/20160805094233_add_primary_key_hostgroup_ansible_roles.rb +6 -0
  44. data/db/seeds.d/{62-ansible_proxy_feature.rb → 62_ansible_proxy_feature.rb} +0 -0
  45. data/lib/foreman_ansible.rb +1 -0
  46. data/lib/foreman_ansible/engine.rb +54 -4
  47. data/lib/foreman_ansible/version.rb +1 -1
  48. data/locale/en/foreman_ansible.po +139 -2
  49. data/locale/foreman_ansible.pot +200 -8
  50. data/test/factories/ansible_proxy.rb +7 -0
  51. data/test/factories/ansible_roles.rb +2 -2
  52. data/test/fixtures/ansible_permissions.yml +11 -0
  53. data/test/functional/ansible_roles_controller_test.rb +16 -0
  54. data/test/functional/api/v2/ansible_roles_controller_test.rb +24 -0
  55. data/test/functional/hosts_controller_test.rb +65 -24
  56. data/test/support/fixture_support.rb +25 -0
  57. data/test/support/foreman_tasks/task.rb +47 -0
  58. data/test/support/foreman_test_helper_additions.rb +22 -0
  59. data/test/test_plugin_helper.rb +5 -2
  60. data/test/unit/ansible_role_test.rb +6 -2
  61. data/test/unit/concerns/host_managed_extensions_test.rb +37 -0
  62. data/test/unit/concerns/hostgroup_extensions_test.rb +36 -0
  63. data/test/unit/fact_importer_test.rb +11 -8
  64. data/test/unit/fact_parser_test.rb +2 -0
  65. data/test/unit/fact_sparser_test.rb +1 -0
  66. data/test/unit/helpers/foreman_ansible/ansible_reports_helper_test.rb +1 -0
  67. data/test/unit/host_ansible_role_test.rb +10 -0
  68. data/test/unit/hostgroup_ansible_role_test.rb +19 -0
  69. data/test/unit/lib/foreman_ansible_core/playbook_runner_test.rb +28 -0
  70. data/test/unit/lib/proxy_api/ansible_test.rb +23 -0
  71. data/test/unit/services/api_roles_importer_test.rb +28 -0
  72. data/test/unit/services/proxy_selector_test.rb +28 -0
  73. data/test/unit/services/roles_importer_test.rb +17 -0
  74. data/test/unit/services/ui_roles_importer_test.rb +30 -0
  75. metadata +108 -14
  76. data/app/jobs/foreman_ansible/run_playbook_job.rb +0 -28
  77. data/app/services/foreman_ansible/role_player.rb +0 -26
  78. data/app/views/foreman_ansible/hosts/_tab_content.html.erb +0 -4
  79. data/lib/tasks/foreman_ansible_tasks.rake +0 -40
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fc4ac60019a9a765f9d6952ef2353dd4c67ddac3
4
- data.tar.gz: 1cdc990b070ea857835c74822b021340640bac56
3
+ metadata.gz: 7394d81e94f7dd6bfda57b0a539adc09a1c068e0
4
+ data.tar.gz: 87475c40db6d3a594517de1443878ffe107b85f6
5
5
  SHA512:
6
- metadata.gz: 6658351477f153ae2b6b42356ba9cb75515fbfeae8a8a40040e2c80d458b127adb2a2a37171b42dc54ca73b135cfd207aa804d190ad6a63df5b8ae8f211805b7
7
- data.tar.gz: 20f1305a896c46529bc488044f3a3284feb09290845ecbdd333be2ad616473051aa2160cacca849656164bd8c91b654e063a35b4ad9a9edeaa2e257db37cee4c
6
+ metadata.gz: 48d0c08d791d3543d6cbf2341c6a3ed66ba9c0d4b9a8c9e5bbcc0344444ef9c8c94db91ae4b7bf193ef915c75d9162470877140c46cd9d3c44e110d3593d809b
7
+ data.tar.gz: 79df637cc5ce51f5f6d9ee40ae0587b18c4dc9f339ae0edd4d813dd97fcfe1965bc589d7c176fcabcbd202cd47d75a839695ed652a9636ff32e37175164e168b
data/Rakefile CHANGED
@@ -4,37 +4,16 @@ begin
4
4
  rescue LoadError
5
5
  puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
6
  end
7
- begin
8
- require 'rdoc/task'
9
- rescue LoadError
10
- require 'rdoc/rdoc'
11
- require 'rake/rdoctask'
12
- RDoc::Task = Rake::RDocTask
13
- end
14
-
15
- RDoc::Task.new(:rdoc) do |rdoc|
16
- rdoc.rdoc_dir = 'rdoc'
17
- rdoc.title = 'ForemanAnsible'
18
- rdoc.options << '--line-numbers'
19
- rdoc.rdoc_files.include('README.rdoc')
20
- rdoc.rdoc_files.include('lib/**/*.rb')
21
- end
22
-
23
- APP_RAKEFILE = File.expand_path('../test/dummy/Rakefile', __FILE__)
24
-
25
- Bundler::GemHelper.install_tasks
26
7
 
27
8
  require 'rake/testtask'
28
-
29
- Rake::TestTask.new(:test) do |t|
30
- t.libs << 'lib'
31
- t.libs << 'test'
32
- t.pattern = 'test/**/*_test.rb'
33
- t.verbose = false
9
+ Rake::TestTask.new("test:core") do |test|
10
+ test_dir = File.join(File.dirname(__FILE__), 'test/lib')
11
+ test.pattern = "#{test_dir}/**/*_test.rb"
12
+ test.libs << test_dir
13
+ test.verbose = false
14
+ test.warning = false
34
15
  end
35
16
 
36
- task :default => :test
37
-
38
17
  begin
39
18
  require 'rubocop/rake_task'
40
19
  RuboCop::RakeTask.new
@@ -0,0 +1,55 @@
1
+ # UI controller for ansible roles
2
+ class AnsibleRolesController < ::ApplicationController
3
+ include Foreman::Controller::AutoCompleteSearch
4
+
5
+ before_action :find_resource, :only => [:destroy]
6
+ before_action :find_proxy, :only => [:import]
7
+ before_action :create_importer, :only => [:import, :confirm_import]
8
+
9
+ def index
10
+ @ansible_roles = resource_base.search_for(params[:search],
11
+ :order => params[:order]).
12
+ paginate(:page => params[:page],
13
+ :per_page => params[:per_page])
14
+ end
15
+
16
+ def destroy
17
+ if @ansible_role.destroy
18
+ process_success
19
+ else
20
+ process_error
21
+ end
22
+ end
23
+
24
+ def import
25
+ changed = @importer.import!
26
+ if changed.values.all?(&:empty?)
27
+ notice no_changed_roles_message
28
+ redirect_to ansible_roles_path
29
+ else
30
+ render :locals => { :changed => changed }
31
+ end
32
+ end
33
+
34
+ def confirm_import
35
+ @importer.finish_import(params[:changed])
36
+ notice _('Import of roles successfully finished.')
37
+ redirect_to ansible_roles_path
38
+ end
39
+
40
+ private
41
+
42
+ def find_proxy
43
+ return nil unless params[:proxy]
44
+ @proxy = SmartProxy.authorized(:view_smart_proxies).find(params[:proxy])
45
+ end
46
+
47
+ def create_importer
48
+ @importer = ForemanAnsible::UiRolesImporter.new(@proxy)
49
+ end
50
+
51
+ def no_changed_roles_message
52
+ return _('No changes in roles detected.') unless @proxy.present?
53
+ _('No changes in roles detected on %s.') % @proxy.name
54
+ end
55
+ end
@@ -0,0 +1,52 @@
1
+ module Api
2
+ module V2
3
+ # API controller for Ansible Roles
4
+ class AnsibleRolesController < ::Api::V2::BaseController
5
+ include ::Api::Version2
6
+
7
+ before_action :find_resource, :only => [:show, :destroy]
8
+ before_action :find_proxy, :only => [:import, :obsolete]
9
+ before_action :create_importer, :only => [:import, :obsolete]
10
+
11
+ api :GET, '/ansible/ansible_roles/:id', N_('Show role')
12
+ param :id, :identifier, :required => true
13
+ def show
14
+ end
15
+
16
+ api :GET, '/ansible/ansible_roles', N_('List Ansible roles')
17
+ param_group :search_and_pagination, ::Api::V2::BaseController
18
+ def index
19
+ @ansible_roles = resource_scope_for_index
20
+ end
21
+
22
+ api :DELETE, '/ansible/ansible_roles/:id', N_('Deletes Ansible role')
23
+ param :id, :identifier, :required => true
24
+ def destroy
25
+ process_response @ansible_role.destroy
26
+ end
27
+
28
+ api :POST, '/ansible/ansible_roles/import', N_('Import Ansible roles')
29
+ param :proxy, Hash, N_('Smart Proxy to import from')
30
+ def import
31
+ @imported = @importer.import!
32
+ end
33
+
34
+ api :POST, '/ansible/ansible_roles/obsolete', N_('Obsolete Ansible roles')
35
+ param :proxy, Hash, N_('Smart Proxy to import from')
36
+ def obsolete
37
+ @obsoleted = @importer.obsolete!
38
+ end
39
+
40
+ private
41
+
42
+ def find_proxy
43
+ return nil unless params[:proxy]
44
+ @proxy = SmartProxy.authorized(:view_smart_proxies).find(params[:proxy])
45
+ end
46
+
47
+ def create_importer
48
+ @importer = ForemanAnsible::ApiRolesImporter.new(@proxy)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -3,23 +3,24 @@ module ForemanAnsible
3
3
  # Extra methods to enforce Ansible roles on a host or multiple hosts
4
4
  module HostsControllerExtensions
5
5
  extend ActiveSupport::Concern
6
+ include ForemanTasks::Triggers
6
7
 
7
8
  def play_roles
8
9
  find_resource
9
- RolePlayer.new(@host).play
10
- notice(_('Ansible roles running on background: ') +
11
- @host.ansible_roles.map(&:name).join(', '))
12
- redirect_to :back
10
+ task = async_task(::Actions::ForemanAnsible::PlayHostRoles, @host)
11
+ redirect_to task
12
+ rescue Foreman::Exception => e
13
+ error e.message
14
+ redirect_to host_path(@host)
13
15
  end
14
16
 
15
17
  def multiple_play_roles
16
18
  find_multiple
17
- @hosts.each do |host|
18
- RolePlayer.new(host).play
19
- end
20
- notice(_('Ansible roles running on background for hosts: ') +
21
- @hosts.map(&:name).join(', '))
22
- redirect_to :hosts
19
+ task = async_task(::Actions::ForemanAnsible::PlayHostsRoles, @hosts)
20
+ redirect_to task
21
+ rescue Foreman::Exception => e
22
+ error e.message
23
+ redirect_to hosts_path
23
24
  end
24
25
 
25
26
  private
@@ -0,0 +1,8 @@
1
+ module ForemanAnsible
2
+ # General helper for foreman_ansible
3
+ module AnsiblePluginHelper
4
+ def ansible_doc_url
5
+ 'http://theforeman.org/plugins/foreman_ansible/1.x/index.html'
6
+ end
7
+ end
8
+ end
@@ -12,10 +12,12 @@ module ForemanAnsible
12
12
 
13
13
  def ansible_module_message(log)
14
14
  paragraph_style = 'margin:0px;font-family:Menlo,Monaco,Consolas,monospace'
15
- JSON.parse(log.message.value).except('invocation').map do |name, value|
16
- next if value.blank?
17
- "<p style=#{paragraph_style}>#{name}: #{value}</p>"
18
- end.join.html_safe
15
+ safe_join(
16
+ JSON.parse(log.message.value).except('invocation').map do |name, value|
17
+ next if value.blank?
18
+ content_tag(:p, "#{name}: #{value}", :style => paragraph_style)
19
+ end
20
+ )
19
21
  end
20
22
 
21
23
  def ansible_report?(log)
@@ -1,9 +1,23 @@
1
1
  module ForemanAnsible
2
2
  # Small convenience to list all roles in the UI
3
3
  module AnsibleRolesHelper
4
- def ansible_roles
5
- ForemanAnsible::RolesImporter.import!
6
- AnsibleRole.all
4
+ def ansible_proxy_links(hash, classes = nil)
5
+ links = SmartProxy.with_features('Ansible').map do |proxy|
6
+ display_link_if_authorized(_('Import from %s') % proxy.name,
7
+ hash.merge(:proxy => proxy),
8
+ :class => classes)
9
+ end.flatten
10
+ links.unshift display_link_if_authorized(_('Import from Foreman host'),
11
+ hash,
12
+ :class => classes)
13
+ end
14
+
15
+ def ansible_proxy_import(hash)
16
+ select_action_button(_('Import'), {}, ansible_proxy_links(hash))
17
+ end
18
+
19
+ def import_time(role)
20
+ _('%s ago') % time_ago_in_words(role.updated_at)
7
21
  end
8
22
  end
9
23
  end
@@ -11,9 +11,10 @@ module ForemanAnsible
11
11
  def host_title_actions_with_run_ansible_roles(*args)
12
12
  button = link_to(
13
13
  icon_text('play', ' ' + _('Ansible roles'), :kind => 'fa'),
14
- play_roles_ansible_host_path(:id => args.first.id),
14
+ play_roles_host_path(:id => args.first.id),
15
15
  :id => :ansible_roles_button,
16
- :class => 'btn btn-default')
16
+ :class => 'btn btn-default'
17
+ )
17
18
  title_actions(button_group(button)) if args.first.ansible_roles.present?
18
19
  host_title_actions_without_run_ansible_roles(*args)
19
20
  end
@@ -21,7 +22,7 @@ module ForemanAnsible
21
22
  def multiple_actions_with_run_ansible_roles
22
23
  multiple_actions_without_run_ansible_roles +
23
24
  [[_('Play Ansible roles'),
24
- multiple_play_roles_ansible_hosts_path,
25
+ multiple_play_roles_hosts_path,
25
26
  false]]
26
27
  end
27
28
  end
@@ -0,0 +1,56 @@
1
+ module Actions
2
+ module ForemanAnsible
3
+ # Actions that initiaztes the playbook run for roles assigned to
4
+ # the host. It doest that either locally or via a proxy when available.
5
+ class PlayHostRoles < Actions::EntryAction
6
+ include ::Actions::Helpers::WithContinuousOutput
7
+ include ::Actions::Helpers::WithDelegatedAction
8
+
9
+ def plan(host, proxy_selector = ::ForemanAnsible::ProxySelector.new)
10
+ input[:host] = { :id => host.id, :name => host.fqdn }
11
+ proxy = proxy_selector.determine_proxy(host)
12
+ inventory_creator = ::ForemanAnsible::InventoryCreator.new([host])
13
+ role_names = host.all_ansible_roles.map(&:name)
14
+ playbook_creator = ::ForemanAnsible::PlaybookCreator.new(role_names)
15
+ plan_delegated_action(proxy, ::ForemanAnsibleCore::Actions::RunPlaybook,
16
+ :inventory => inventory_creator.to_hash.to_json,
17
+ :playbook => playbook_creator.roles_playbook)
18
+ plan_self
19
+ end
20
+
21
+ def finalize
22
+ if delegated_output[:exit_status].to_s != '0'
23
+ error! _('Playbook execution failed')
24
+ end
25
+ end
26
+
27
+ def rescue_strategy
28
+ ::Dynflow::Action::Rescue::Fail
29
+ end
30
+
31
+ def humanized_input
32
+ _('on host %{name}') % { :name => input.fetch(:host, {})[:name] }
33
+ end
34
+
35
+ def humanized_name
36
+ _('Play Ansible roles')
37
+ end
38
+
39
+ def humanized_output
40
+ continuous_output.humanize
41
+ end
42
+
43
+ def continuous_output_providers
44
+ super << self
45
+ end
46
+
47
+ def fill_continuous_output(continuous_output)
48
+ delegated_output.fetch('result', []).each do |raw_output|
49
+ continuous_output.add_raw_output(raw_output)
50
+ end
51
+ rescue => e
52
+ continuous_output.add_exception(_('Error loading data from proxy'), e)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,26 @@
1
+ module Actions
2
+ module ForemanAnsible
3
+ # Bulk action for running ansible roles
4
+ class PlayHostsRoles < Actions::ActionWithSubPlans
5
+ def plan(hosts)
6
+ plan_self(:host_ids => hosts.map(&:id))
7
+ end
8
+
9
+ def create_sub_plans
10
+ proxy_selector = ::ForemanAnsible::ProxySelector.new
11
+ input[:host_ids].map do |host_id|
12
+ host = Host.find(host_id)
13
+ trigger(PlayHostRoles, host, proxy_selector)
14
+ end
15
+ end
16
+
17
+ def rescue_strategy
18
+ ::Dynflow::Action::Rescue::Fail
19
+ end
20
+
21
+ def humanized_name
22
+ _('Bulk play Ansible roles')
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,28 @@
1
+ module ProxyAPI
2
+ # ProxyAPI for Ansible
3
+ class Ansible < ::ProxyAPI::Resource
4
+ def initialize(args)
5
+ @url = args[:url] + '/ansible/'
6
+ super args
7
+ end
8
+
9
+ PROXY_ERRORS = [
10
+ Errno::ECONNREFUSED,
11
+ SocketError,
12
+ Timeout::Error,
13
+ Errno::EINVAL,
14
+ Errno::ECONNRESET,
15
+ EOFError,
16
+ Net::HTTPBadResponse,
17
+ Net::HTTPHeaderSyntaxError,
18
+ Net::ProtocolError,
19
+ RestClient::ResourceNotFound
20
+ ].freeze
21
+
22
+ def roles
23
+ parse(get('roles'))
24
+ rescue *PROXY_ERRORS => e
25
+ raise ProxyException.new(url, e, N_('Unable to get roles from Ansible'))
26
+ end
27
+ end
28
+ end
@@ -1,6 +1,14 @@
1
1
  # Simple model to store basic info about the Ansible role
2
2
  class AnsibleRole < ActiveRecord::Base
3
+ include Authorizable
4
+
5
+ self.include_root_in_json = false
3
6
  validates :name, :presence => true, :uniqueness => true
4
7
  has_many :host_ansible_roles
5
8
  has_many_hosts :through => :host_ansible_roles, :dependent => :destroy
9
+ has_many :hostgroup_ansible_roles
10
+ has_many :hostgroups, :through => :hostgroup_ansible_roles,
11
+ :dependent => :destroy
12
+
13
+ scoped_search :on => :name, :complete_value => true
6
14
  end
@@ -0,0 +1,13 @@
1
+ module ForemanAnsible
2
+ # Define common behaviors between models that have many Ansible roles,
3
+ # currently Host and Hostgroup. Takes care of inheritance, etc...
4
+ module HasManyAnsibleRoles
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ def all_ansible_roles
9
+ (ansible_roles + inherited_ansible_roles).uniq
10
+ end
11
+ end
12
+ end
13
+ end
@@ -7,8 +7,12 @@ module ForemanAnsible
7
7
  has_many :host_ansible_roles, :foreign_key => :host_id
8
8
  has_many :ansible_roles, :through => :host_ansible_roles,
9
9
  :dependent => :destroy
10
+ include ForemanAnsible::HasManyAnsibleRoles
10
11
 
11
- attr_accessible :ansible_role_ids, :ansible_roles
12
+ def inherited_ansible_roles
13
+ return [] unless hostgroup
14
+ hostgroup.all_ansible_roles
15
+ end
12
16
  end
13
17
  end
14
18
  end
@@ -0,0 +1,19 @@
1
+ module ForemanAnsible
2
+ # Relations to make Hostgroup 'have' ansible roles
3
+ module HostgroupExtensions
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ has_many :hostgroup_ansible_roles, :foreign_key => :hostgroup_id
8
+ has_many :ansible_roles, :through => :hostgroup_ansible_roles,
9
+ :dependent => :destroy
10
+ include ForemanAnsible::HasManyAnsibleRoles
11
+
12
+ def inherited_ansible_roles
13
+ ancestors.reduce([]) do |roles, hostgroup|
14
+ roles + hostgroup.ansible_roles
15
+ end.uniq
16
+ end
17
+ end
18
+ end
19
+ end