foreman_discovery 3.0.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/app/controllers/api/v2/discovered_hosts_controller.rb +31 -11
  4. data/app/controllers/api/v2/discovery_rules_controller.rb +8 -16
  5. data/app/controllers/concerns/foreman/controller/discovered_extensions.rb +47 -2
  6. data/app/controllers/discovered_hosts_controller.rb +30 -55
  7. data/app/controllers/discovery_rules_controller.rb +2 -4
  8. data/app/helpers/discovered_hosts_helper.rb +27 -5
  9. data/app/lib/facter_utils.rb +18 -0
  10. data/app/lib/puppet_fact_parser_extensions.rb +18 -32
  11. data/app/models/discovery_rule.rb +33 -2
  12. data/app/models/host/discovered.rb +47 -61
  13. data/app/models/host/managed_extensions.rb +12 -3
  14. data/app/models/setting/discovered.rb +18 -6
  15. data/app/services/foreman_discovery/host_converter.rb +22 -0
  16. data/app/views/api/v2/discovered_hosts/main.json.rabl +1 -1
  17. data/app/views/api/v2/discovery_rules/main.json.rabl +6 -1
  18. data/app/views/api/v2/discovery_rules/show.json.rabl +4 -0
  19. data/app/views/dashboard/_discovery_widget.html.erb +6 -4
  20. data/app/views/dashboard/_discovery_widget_host.html.erb +4 -0
  21. data/app/views/dashboard/_discovery_widget_host_list.html.erb +2 -3
  22. data/app/views/discovered_hosts/_discovered_host.html.erb +3 -5
  23. data/app/views/discovered_hosts/_discovered_hosts_list.html.erb +7 -7
  24. data/app/views/discovered_hosts/index.html.erb +1 -0
  25. data/app/views/discovery_rules/_form.html.erb +51 -32
  26. data/config/routes.rb +4 -4
  27. data/db/migrate/20150310153859_remove_discovery_attribute_sets_from_managed_hosts.rb +8 -0
  28. data/db/migrate/20150331132115_remove_old_permissions.rb +16 -0
  29. data/db/migrate/20150505111345_remove_leftover_tokens.rb +13 -0
  30. data/db/migrate/20150512150432_remove_old_discovery_reader_permissions.rb +10 -0
  31. data/db/migrate/20150714144500_review_discovery_permissions.rb +17 -0
  32. data/lib/foreman_discovery/engine.rb +46 -16
  33. data/lib/foreman_discovery/version.rb +1 -1
  34. data/locale/de/LC_MESSAGES/foreman_discovery.mo +0 -0
  35. data/locale/de/foreman_discovery.po +74 -72
  36. data/locale/de/foreman_discovery.pox +40 -0
  37. data/locale/en_GB/LC_MESSAGES/foreman_discovery.mo +0 -0
  38. data/locale/en_GB/foreman_discovery.po +90 -88
  39. data/locale/en_GB/foreman_discovery.pox +0 -0
  40. data/locale/es/LC_MESSAGES/foreman_discovery.mo +0 -0
  41. data/locale/es/foreman_discovery.po +3 -3
  42. data/locale/es/foreman_discovery.pox +41 -0
  43. data/locale/fr/LC_MESSAGES/foreman_discovery.mo +0 -0
  44. data/locale/fr/foreman_discovery.po +3 -3
  45. data/locale/fr/foreman_discovery.pox +69 -0
  46. data/locale/gl/LC_MESSAGES/foreman_discovery.mo +0 -0
  47. data/locale/gl/foreman_discovery.po +3 -3
  48. data/locale/gl/foreman_discovery.pox +21 -0
  49. data/locale/it/LC_MESSAGES/foreman_discovery.mo +0 -0
  50. data/locale/it/foreman_discovery.po +3 -3
  51. data/locale/it/foreman_discovery.pox +0 -0
  52. data/locale/ja/LC_MESSAGES/foreman_discovery.mo +0 -0
  53. data/locale/ja/foreman_discovery.po +67 -66
  54. data/locale/ja/foreman_discovery.pox +29 -0
  55. data/locale/ko/LC_MESSAGES/foreman_discovery.mo +0 -0
  56. data/locale/ko/foreman_discovery.po +3 -3
  57. data/locale/ko/foreman_discovery.pox +189 -0
  58. data/locale/messages.mo +0 -0
  59. data/locale/pt_BR/LC_MESSAGES/foreman_discovery.mo +0 -0
  60. data/locale/pt_BR/foreman_discovery.po +7 -7
  61. data/locale/pt_BR/foreman_discovery.pox +0 -0
  62. data/locale/ru/LC_MESSAGES/foreman_discovery.mo +0 -0
  63. data/locale/ru/foreman_discovery.po +98 -96
  64. data/locale/ru/foreman_discovery.pox +0 -0
  65. data/locale/sv_SE/LC_MESSAGES/foreman_discovery.mo +0 -0
  66. data/locale/sv_SE/foreman_discovery.po +3 -3
  67. data/locale/zh_CN/LC_MESSAGES/foreman_discovery.mo +0 -0
  68. data/locale/zh_CN/foreman_discovery.po +3 -3
  69. data/locale/zh_CN/foreman_discovery.pox +33 -0
  70. data/locale/zh_TW/LC_MESSAGES/foreman_discovery.mo +0 -0
  71. data/locale/zh_TW/foreman_discovery.po +3 -3
  72. data/locale/zh_TW/foreman_discovery.pox +23 -0
  73. data/test/functional/api/v2/discovered_hosts_controller_test.rb +45 -7
  74. data/test/functional/discovered_hosts_controller_test.rb +52 -11
  75. data/test/unit/discovered_extensions_test.rb +40 -17
  76. data/test/unit/discovery_rule_test.rb +59 -0
  77. data/test/unit/host_discovered_test.rb +69 -6
  78. data/test/unit/puppet_fact_parser_extensions_test.rb +55 -40
  79. metadata +103 -81
  80. data/app/services/host_converter.rb +0 -30
@@ -3,9 +3,6 @@ require 'foreman_discovery/proxy_operations'
3
3
  class Host::Discovered < ::Host::Base
4
4
  include ScopedSearchExtensions
5
5
 
6
- belongs_to :location
7
- belongs_to :organization
8
- belongs_to :subnet
9
6
  belongs_to :hostgroup
10
7
  has_one :discovery_attribute_set, :foreign_key => :host_id, :dependent => :destroy
11
8
 
@@ -13,71 +10,46 @@ class Host::Discovered < ::Host::Base
13
10
 
14
11
  delegate :memory, :cpu_count, :disk_count, :disks_size, :to => :discovery_attribute_set
15
12
 
16
- scoped_search :on => :name, :complete_value => true, :default_order => true
13
+ scoped_search :on => :name, :complete_value => true
14
+ scoped_search :on => :created_at, :default_order => :desc
17
15
  scoped_search :on => :last_report, :complete_value => true
18
- scoped_search :on => :ip, :complete_value => true
19
- scoped_search :on => :mac, :complete_value => true
16
+ scoped_search :in => :primary_interface, :on => :ip, :complete_value => true
17
+ scoped_search :in => :primary_interface, :on => :mac, :complete_value => true
20
18
  scoped_search :in => :model, :on => :name, :complete_value => true, :rename => :model
21
19
  scoped_search :in => :fact_values, :on => :value, :in_key => :fact_names, :on_key => :name, :rename => :facts, :complete_value => true, :only_explicit => true
22
20
  scoped_search :in => :location, :on => :name, :rename => :location, :complete_value => true if SETTINGS[:locations_enabled]
23
21
  scoped_search :in => :organization, :on => :name, :rename => :organization, :complete_value => true if SETTINGS[:organizations_enabled]
24
22
  scoped_search :in => :subnet, :on => :network, :complete_value => true, :rename => :subnet
23
+ scoped_search :in => :subnet, :on => :name, :complete_value => true, :rename => 'subnet.name'
25
24
  scoped_search :in => :discovery_attribute_set, :on => :cpu_count, :rename => :cpu_count, :complete_value => true, :only_explicit => true
26
25
  scoped_search :in => :discovery_attribute_set, :on => :memory, :rename => :memory, :complete_value => true, :only_explicit => true
27
26
  scoped_search :in => :discovery_attribute_set, :on => :disk_count, :rename => :disk_count, :complete_value => true, :only_explicit => true
28
27
  scoped_search :in => :discovery_attribute_set, :on => :disks_size, :rename => :disks_size, :complete_value => true, :only_explicit => true
29
28
 
30
29
  default_scope lambda {
31
- org = Organization.current
32
- loc = Location.current
33
- conditions = {}
34
- conditions[:organization_id] = org.subtree_ids if org
35
- conditions[:location_id] = loc.subtree_ids if loc
36
- where(conditions)
30
+ where(taxonomy_conditions).order("hosts.created_at DESC")
37
31
  }
38
32
 
39
33
  def self.import_host_and_facts facts
40
34
  raise(::Foreman::Exception.new(N_("Invalid facts, must be a Hash"))) unless facts.is_a?(Hash)
41
- fact_name = Setting[:discovery_fact] || 'discovery_bootif'
42
- prefix_from_settings = Setting[:discovery_prefix]
43
- hostname_prefix = prefix_from_settings if prefix_from_settings.present? && prefix_from_settings.match(/^[a-zA-Z].*/)
44
- hostname_prefix ||= 'mac'
45
- hostname = facts[fact_name].try(:downcase).try(:gsub,/:/,'').try(:sub,/^/, hostname_prefix)
46
- raise(::Foreman::Exception.new(N_("Invalid facts: hash does not contain the required fact '%s'"), fact_name)) unless hostname
47
- raise(::Foreman::Exception.new(N_("Invalid facts: hash does not contain IP address"))) unless facts['ipaddress']
48
35
 
49
36
  # filter facts
50
37
  facts.reject!{|k,v| k =~ /kernel|operatingsystem|osfamily|ruby|path|time|swap|free|filesystem/i }
51
38
 
39
+ raise ::Foreman::Exception.new(N_("Expected discovery_fact '%s' is missing, unable to detect primary interface and set hostname") % FacterUtils::bootif_name) unless FacterUtils::bootif_present(facts)
40
+
41
+ # construct hostname
42
+ prefix_from_settings = Setting[:discovery_prefix]
43
+ hostname_prefix = prefix_from_settings if prefix_from_settings.present? && prefix_from_settings.match(/^[a-zA-Z].*/)
44
+ hostname_prefix ||= 'mac'
45
+ hostname = FacterUtils::bootif_mac(facts).try(:downcase).try(:gsub,/:/,'').try(:sub,/^/, hostname_prefix)
46
+
47
+ # create new host record
52
48
  h = ::Host::Discovered.find_by_name hostname
53
49
  h ||= Host.new :name => hostname, :type => "Host::Discovered"
54
50
  h.type = "Host::Discovered"
55
51
 
56
- macfact = facts[fact_name].try(:downcase)
57
- begin
58
- macfact = Net::Validations.normalize_mac(macfact)
59
- rescue ArgumentError => e
60
- macfact = facts['discovery_bootif'].try(:downcase)
61
- h.name = facts['discovery_bootif'].try(:downcase).try(:gsub,/:/,'').try(:sub,/^/, hostname_prefix)
62
- end
63
- h.mac = macfact
64
-
65
-
66
- if SETTINGS[:locations_enabled]
67
- begin
68
- h.location = (Location.find_by_name Setting[:discovery_location]) || Location.first
69
- rescue
70
- h.location = Location.first
71
- end
72
- end
73
- if SETTINGS[:organizations_enabled]
74
- begin
75
- h.organization = (Organization.find_by_name Setting[:discovery_organization]) || Organization.first
76
- rescue
77
- h.organization = Organization.first
78
- end
79
- end
80
-
52
+ # and save (interfaces are created via puppet parser extension)
81
53
  h.save(:validate => false) if h.new_record?
82
54
  state = h.import_facts(facts)
83
55
  return h, state
@@ -100,17 +72,35 @@ class Host::Discovered < ::Host::Base
100
72
  super
101
73
  end
102
74
 
103
- def populate_fields_from_facts facts = self.facts_hash, type = 'puppet'
104
- # type arg only added in 1.7
105
- if Gem::Dependency.new('', '>= 1.7').match?('', SETTINGS[:version].notag)
106
- importer = super
75
+ def populate_fields_from_facts(facts = self.facts_hash, type = 'puppet')
76
+ # detect interfaces and primary interface using extensions
77
+ parser = super(facts, type)
78
+
79
+ # set additional discovery attributes
80
+ primary_ip = self.primary_interface.ip
81
+ unless primary_ip.nil?
82
+ subnet = Subnet.subnet_for(primary_ip)
83
+ Rails.logger.warn "Subnet not detected for #{primary_ip}" if subnet.nil?
84
+ # set subnet
85
+ self.primary_interface.subnet = subnet
86
+ # set location and organization
87
+ if SETTINGS[:locations_enabled]
88
+ self.location = Location.find_by_name(Setting[:discovery_location]) ||
89
+ subnet.try(:locations).try(:first) ||
90
+ Location.first
91
+ end
92
+ if SETTINGS[:organizations_enabled]
93
+ self.organization = Organization.find_by_name(Setting[:discovery_organization]) ||
94
+ subnet.try(:organizations).try(:first) ||
95
+ Organization.first
96
+ end
107
97
  else
108
- importer = super(facts)
98
+ raise(::Foreman::Exception.new(N_("Unable to assign subnet, primary interface is missing IP address")))
109
99
  end
110
- self.primary_interface.subnet = Subnet.subnet_for(self.primary_interface.ip)
111
100
  self.discovery_attribute_set = DiscoveryAttributeSet.where(:host_id => id).first_or_create
112
101
  self.discovery_attribute_set.update_attributes(import_from_facts)
113
- self.save
102
+ self.save!
103
+ parser
114
104
  end
115
105
 
116
106
  def import_from_facts(facts = self.facts_hash)
@@ -160,18 +150,14 @@ class Host::Discovered < ::Host::Base
160
150
  proxy_url = self.proxy_url
161
151
 
162
152
  if proxy_url[:type] == 'proxy'
163
- status = ForemanDiscovery::ProxyOperations.new(:url => proxy_url[:url], :operation => 'reboot').parse_put_operation
153
+ ForemanDiscovery::ProxyOperations.new(:url => proxy_url[:url], :operation => 'reboot').
154
+ parse_put_operation.try(:fetch, 'result')
164
155
  else
165
- status = ::ProxyAPI::BMC.new(:url => "http://#{self.ip}:8443").power :action => "cycle"
156
+ ::ProxyAPI::BMC.new(:url => "http://#{self.ip}:8443").power :action => "cycle"
166
157
  end
167
-
168
- msg = status ? 'successful' : 'failed'
169
- logger.info "ForemanDiscovery: reboot result: #{msg}"
170
- status
171
- rescue => e
172
- logger.info "ForemanDiscovery: reboot result: failed"
173
- logger.warn e.backtrace.join('\n')
174
- raise e
158
+ rescue => e
159
+ ::Foreman::Logging.exception("Unable to reboot #{name}", e)
160
+ raise ::Foreman::WrappedException.new(e, N_("Unable to reboot %{name}: %{msg}"), :name => name, :msg => e.to_s)
175
161
  end
176
162
 
177
163
  def self.model_name
@@ -4,24 +4,33 @@ module Host::ManagedExtensions
4
4
  included do
5
5
  # execute standard callbacks
6
6
  after_validation :queue_reboot
7
+ after_save :delete_discovery_attribute_set
7
8
 
8
9
  belongs_to :discovery_rule
9
10
  end
10
11
 
11
12
  def queue_reboot
13
+ return unless errors.empty? && Setting[:discovery_reboot]
12
14
  return if new_record? # Discovered Hosts already exist, and new_records will break `find`
13
15
  return unless type_changed? and ::Host::Base.find(self.id).type == "Host::Discovered"
16
+ # reboot task must be high priority and there is no compensation action apparently
14
17
  post_queue.create(:name => _("Rebooting %s") % self, :priority => 10000,
15
18
  :action => [self, :setReboot])
16
19
  end
17
20
 
18
21
  def setReboot
19
22
  old.becomes(Host::Discovered).reboot
23
+ # It is too late to report error in the post_queue, we catch them and
24
+ # continue. If flash is implemented for new hosts (http://projects.theforeman.org/issues/10559)
25
+ # we can report the error to the user perhaps.
26
+ true
27
+ rescue ::Foreman::Exception
28
+ true
20
29
  end
21
30
 
22
- def delReboot
23
- # nothing to do here, in reality we should never hit this method since this should be the
24
- # last action in the queue.
31
+ def delete_discovery_attribute_set
32
+ return if new_record?
33
+ DiscoveryAttributeSet.destroy_all(:host_id => self.id) if type_changed?
25
34
  end
26
35
 
27
36
  end
@@ -9,34 +9,35 @@ class Setting::Discovered < ::Setting
9
9
 
10
10
  Setting.transaction do
11
11
  [
12
- self.set('discovery_fact', _("The default fact name to use for the MAC of the system"), "discovery_bootif"),
13
- self.set('discovery_auto', _("Automatically provision newly discovered hosts, according to the provisioning rules"), false),
12
+ self.set('discovery_fact', N_("Fact name to use for primary interface detection and hostname"), "discovery_bootif"),
13
+ self.set('discovery_auto', N_("Automatically provision newly discovered hosts, according to the provisioning rules"), false),
14
+ self.set('discovery_reboot', N_("Automatically reboot discovered host during provisioning"), true),
14
15
  ].compact.each { |s| self.create s.update(:category => "Setting::Discovered")}
15
16
  end
16
17
 
17
18
  Setting.transaction do
18
19
  [
19
- self.set('discovery_prefix', _("The default prefix to use for the host name, must start with a letter"), "mac"),
20
+ self.set('discovery_prefix', N_("The default prefix to use for the host name, must start with a letter"), "mac"),
20
21
  ].compact.each { |s| self.create s.update(:category => "Setting::Discovered")}
21
22
  end
22
23
 
23
24
  Setting.transaction do
24
25
  [
25
- self.set('discovery_fact_column', _("Show fact as an extra column in the discovered hosts list"), ""),
26
+ self.set('discovery_fact_column', N_("Extra facter columns to show in host lists (separate by comma)"), ""),
26
27
  ].compact.each { |s| self.create s.update(:category => "Setting::Discovered")}
27
28
  end
28
29
 
29
30
  if SETTINGS[:locations_enabled]
30
31
  Setting.transaction do
31
32
  [
32
- self.set('discovery_location', _("The default location to place discovered hosts in"), ""),
33
+ self.set('discovery_location', N_("The default location to place discovered hosts in"), ""),
33
34
  ].compact.each { |s| self.create s.update(:category => "Setting::Discovered")}
34
35
  end
35
36
  end
36
37
  if SETTINGS[:organizations_enabled]
37
38
  Setting.transaction do
38
39
  [
39
- self.set('discovery_organization', _("The default organization to place discovered hosts in"), "" ),
40
+ self.set('discovery_organization', N_("The default organization to place discovered hosts in"), "" ),
40
41
  ].compact.each { |s| self.create s.update(:category => "Setting::Discovered")}
41
42
  end
42
43
  end
@@ -45,4 +46,15 @@ class Setting::Discovered < ::Setting
45
46
 
46
47
  end
47
48
 
49
+ def self.discovery_fact_column_array
50
+ return [] if !Setting['discovery_fact_column'].present?
51
+ list = []
52
+ Setting['discovery_fact_column'].to_s.split(",").each do |value|
53
+ list << value.strip
54
+ end
55
+ rescue => error
56
+ logger.warn "Failed to parse comma delimited list [%s] into array. Error: %s" % [list,error]
57
+ ensure
58
+ list
59
+ end
48
60
  end
@@ -0,0 +1,22 @@
1
+ class ForemanDiscovery::HostConverter
2
+
3
+ # Converts discovered host to managed host without uptading the database.
4
+ # Record must be saved explicitly (using save! or update_attributes! or similar).
5
+ # This method MUST be called from a SQL transaction.
6
+ def self.to_managed(original_host, set_managed = true, set_build = true)
7
+ if ActiveRecord::Base.connection.open_transactions <= 0
8
+ raise "This method must be executed with explicit transaction"
9
+ end
10
+ host = original_host.becomes(::Host::Managed)
11
+ host.type = 'Host::Managed'
12
+ # the following flags can be skipped when parameters are set to false
13
+ if set_managed
14
+ host.managed = set_managed
15
+ host.primary_interface.managed = set_managed
16
+ end
17
+ # set build only and only on final save (otherwise interfaces are not being identified)
18
+ host.build = set_build if set_build
19
+ host
20
+ end
21
+
22
+ end
@@ -2,7 +2,7 @@ object @discovered_host
2
2
 
3
3
  extends "api/v2/discovered_hosts/base"
4
4
 
5
- attributes :ip, :mac, :last_report, :subnet_id, :subnet_name, :memory, :disk_count, :disks_size
5
+ attributes :ip, :mac, :last_report, :subnet_id, :subnet_name, :model_name, :memory, :disk_count, :disks_size
6
6
  attribute :cpu_count => :cpus
7
7
 
8
8
  if SETTINGS[:organizations_enabled]
@@ -2,4 +2,9 @@ object @discovery_rule
2
2
 
3
3
  extends "api/v2/discovery_rules/base"
4
4
 
5
- attributes :name, :enabled, :hostgroup_id, :hostname, :max_count, :priority, :search
5
+ attributes :name, :enabled, :hostgroup_id, :hostgroup_name, :hostname, :priority, :search
6
+ attribute :max_count => :hosts_limit
7
+
8
+ child :hosts do
9
+ extends "api/v2/discovered_hosts/base"
10
+ end
@@ -1,3 +1,7 @@
1
1
  object @discovery_rule
2
2
 
3
3
  extends "api/v2/discovery_rules/main"
4
+
5
+ node do |discovery_rule|
6
+ partial("api/v2/taxonomies/children_nodes", :object => discovery_rule)
7
+ end
@@ -1,10 +1,12 @@
1
- <h4 class="ca"><%= _('Discovered Host Pool') %> - <%= @current_permission %></h4>
2
1
  <% all_hosts = Host::Discovered.includes(:model).includes(:discovery_attribute_set).scoped %>
3
- <% hosts = all_hosts.limit(5) %>
2
+ <% hosts = all_hosts.limit(6) %>
3
+
4
+ <h4 class="header">
5
+ <%= link_to "#{all_hosts.size} #{n_('Discovered Host', 'Discovered Hosts', all_hosts.size)}", discovered_hosts_path %>
6
+ </h4>
7
+
4
8
  <% if hosts.empty? %>
5
9
  <p class="ca"><%= _("No discovered hosts available") %></p>
6
10
  <% else %>
7
11
  <%= render :partial => 'discovery_widget_host_list', :locals => { :hosts => hosts } %>
8
- <p class="ca"><%= link_to n_("Total pool size", "Total pool size", all_hosts.size), discovered_hosts_path %>
9
- - <%= all_hosts.size %> </p>
10
12
  <% end %>
@@ -0,0 +1,4 @@
1
+ <td><%= discovery_status_icon(host) %>&#8239;<%= link_to trunc_with_tooltip(h(host.name)), discovered_host_path(host) %></td>
2
+ <td class="hidden-tablet hidden-xs"><%= model_name host %></td>
3
+ <td class="hidden-tablet hidden-xs"><%= discovery_attribute(host, :cpu_count) %></td>
4
+ <td class="hidden-tablet hidden-xs"><%= number_to_human_size(discovery_attribute(host, :memory, 0) * 1024 * 1024) %></td>
@@ -2,15 +2,14 @@
2
2
  <thead>
3
3
  <th><%= _('Host') %></th>
4
4
  <th><%= _('Model') %></th>
5
- <th><%= _('IP Address') %></th>
6
5
  <th><%= _('CPUs') %></th>
7
6
  <th><%= _('Memory') %></th>
8
7
  </thead>
9
8
  <tbody>
10
9
  <% hosts.each do |host| %>
11
10
  <tr>
12
- <%= render :partial => "discovered_hosts/discovered_host", :locals => {
13
- :host => host, :hide_disk => true } %>
11
+ <%= render :partial => "discovery_widget_host", :locals => {
12
+ :host => host } %>
14
13
  </tr>
15
14
  <% end %>
16
15
  </tbody>
@@ -1,12 +1,10 @@
1
- <td><%= link_to trunc_with_tooltip(h(host.name)), discovered_host_path(host) %></td>
1
+ <td><%= discovery_status_icon(host) %>&#8239;<%= link_to trunc_with_tooltip(h(host.name)), discovered_host_path(host) %></td>
2
2
  <td class="hidden-tablet hidden-xs"><%= model_name host %></td>
3
3
  <td class="hidden-tablet hidden-xs"><%= host.ip %></td>
4
4
  <td class="hidden-tablet hidden-xs"><%= discovery_attribute(host, :cpu_count) %></td>
5
5
  <td class="hidden-tablet hidden-xs"><%= number_to_human_size(discovery_attribute(host, :memory, 0) * 1024 * 1024) %></td>
6
- <% unless defined? hide_disk %>
7
6
  <td class="hidden-tablet hidden-xs"><%= discovery_attribute(host, :disk_count) %></td>
8
7
  <td class="hidden-tablet hidden-xs"><%= number_to_human_size(discovery_attribute(host, :disks_size, 0) * 1024 * 1024) %></td>
9
- <% if Setting['discovery_fact_column'].present? %>
10
- <td class="hidden-tablet hidden-xs"><%= host.facts_hash[Setting['discovery_fact_column']] || 'N/A' %></td>
11
- <% end %>
8
+ <% Setting::Discovered.discovery_fact_column_array.each do |fact_column| %>
9
+ <td class="hidden-tablet hidden-xs"><%= host.facts_hash[fact_column.strip] || 'N/A' %></td>
12
10
  <% end %>
@@ -10,8 +10,8 @@
10
10
  <th class="hidden-tablet hidden-xs"><%= sort :memory, :as => _('Memory') %></th>
11
11
  <th class="hidden-tablet hidden-xs"><%= sort :disk_count, :as => _('Disk count') %></th>
12
12
  <th class="hidden-tablet hidden-xs"><%= sort :disks_size, :as => _('Disks size') %></th>
13
- <% if Setting['discovery_fact_column'].present? %>
14
- <th class="hidden-tablet hidden-xs"><%= Setting['discovery_fact_column'].capitalize %></th>
13
+ <% Setting::Discovered.discovery_fact_column_array.each do |fact_column| %>
14
+ <th class="hidden-tablet hidden-xs"><%= fact_column.strip.capitalize %></th>
15
15
  <% end %>
16
16
  <% if SETTINGS[:locations_enabled] -%>
17
17
  <th class="hidden-tablet hidden-xs"><%= sort :location, :as => _('Location') %></th>
@@ -26,7 +26,7 @@
26
26
  <% @hosts.each do |host| -%>
27
27
  <tr>
28
28
  <td class="ca">
29
- <%= check_box_tag "host_ids[]", nil, false, :id => "host_ids_#{host.id}", :disabled => !authorized?, :class => 'host_select_boxes', :onclick => 'hostChecked(this)' -%>
29
+ <%= check_box_tag "host_ids[]", nil, false, :id => "host_ids_#{host.id}", :disabled => !authorized_for_edit_destroy?, :class => 'host_select_boxes', :onclick => 'hostChecked(this)' -%>
30
30
  </td>
31
31
  <%= render :partial => "discovered_host", :locals => {:host => host} %>
32
32
  <% if SETTINGS[:locations_enabled] -%>
@@ -39,10 +39,10 @@
39
39
  <td class="hidden-tablet hidden-xs"><%= disc_report_column(host) %></td>
40
40
  <td>
41
41
  <%= action_buttons(
42
- link_to(_("Provision"), hash_for_edit_discovered_host_path(:id => host)),
43
- link_to(_("Auto Provision"), hash_for_auto_provision_discovered_host_path(:id => host), :method => :post),
44
- link_to(_("Refresh facts"), hash_for_refresh_facts_discovered_host_path(:id => host)),
45
- link_to(_("Reboot"), hash_for_reboot_discovered_host_path(:id => host), :method => :put),
42
+ display_link_if_authorized(_("Provision"), hash_for_edit_discovered_host_path(:id => host)),
43
+ display_link_if_authorized(_("Auto Provision"), hash_for_auto_provision_discovered_host_path(:id => host), :method => :post),
44
+ display_link_if_authorized(_("Refresh facts"), hash_for_refresh_facts_discovered_host_path(:id => host)),
45
+ display_link_if_authorized(_("Reboot"), hash_for_reboot_discovered_host_path(:id => host), :method => :put),
46
46
  display_delete_if_authorized(hash_for_discovered_host_path(:id => host), :confirm => _("Delete %s?") % host.name, :action => :destroy))%>
47
47
  </td>
48
48
  </tr>
@@ -1,3 +1,4 @@
1
1
  <% title_actions multiple_discovered_hosts_actions_select -%>
2
+ <% title_actions display_link_if_authorized(_("Reboot All"), hash_for_reboot_all_discovered_hosts_path, {:method => :put, :disabled => @hosts.empty?}) %>
2
3
  <% title_actions display_link_if_authorized(_("Auto Provision All"), hash_for_auto_provision_all_discovered_hosts_path, {:method => :post, :disabled => @hosts.empty?}) %>
3
4
  <%= render 'discovered_hosts_list' %>
@@ -1,34 +1,53 @@
1
1
  <%= form_for @discovery_rule, :url => (@discovery_rule.new_record? ? discovery_rules_path : discovery_rule_path(:id => @discovery_rule.id)) do |f| %>
2
- <%= base_errors_for @discovery_rule %>
3
- <%= text_f f, :name %>
4
- <%= autocomplete_f f, :search,
5
- :path => "discovered_hosts",
6
- :control_group_id => 'search_group', :size => 'col-md-9' %>
7
- <%= select_f f, :hostgroup_id, accessible_hostgroups, :id, :to_label, { :include_blank => false },
8
- { :help_inline => _('Target host group for this rule with all properties set')} %>
9
- <%= text_f f, :hostname, :help_inline => popover(
10
- _("Template"),
11
- "<div>" +
12
- _("Specify target hostname template pattern in the same syntax as in Provisioning Templates (ERB).") +
13
- "</div>"+
14
- "<div>" +
15
- _("Domain will be appended automatically. A hostname based on MAC address will be used when left blank. In addition to @host attribute function rand for random integers is available. Examples:") +
16
- "</div>"+
17
- "<pre>"+
18
- "myhost-&lt;%= rand(99999) %&gt;" + "\n\n" +
19
- "abc-&lt;%= @host.facts['bios_vendor'] + " + "\n" +
20
- " '-' + rand(99999) %&gt;" + "\n\n" +
21
- "xyz-&lt;%= @host.hostgroup.name %&gt;" + "\n\n" +
22
- "srv-&lt;%= @host.discovery_rule.name %&gt;" + "\n\n" +
23
- "server-&lt;%= @host.ip.gsub('.','-') + " + "\n" +
24
- " '-' + @host.hostgroup.subnet.name %&gt;" + "\n" +
25
- "</pre>"+
26
- "</div>"+
27
- "<div>"+
28
- _("When creating hostname patterns, make sure the resulting host names are unique. Hostnames must not start with numbers. A good approach is to use unique information provided by facter (MAC address, BIOS or serial ID).") +
29
- "</div>", :title => _("Hostname for provisioned hosts")).html_safe %>
30
- <%= text_f f, :max_count, :label => _('Hosts limit'), :help_inline => _('Maximum hosts provisioned with this rule (0 = unlimited)') %>
31
- <%= text_f f, :priority, :help_inline => _('Rule priority (lower integer means higher priority)') %>
32
- <%= checkbox_f f, :enabled %>
33
- <%= submit_or_cancel f %>
2
+ <%= base_errors_for @discovery_rule %>
3
+
4
+ <ul class="nav nav-tabs" data-tabs="tabs">
5
+ <li class="active"><a href="#primary" data-toggle="tab"><%= _('Primary') %></a></li>
6
+ <% if show_location_tab? %>
7
+ <li><a href="#locations" data-toggle="tab"><%= _('Locations') %></a></li>
8
+ <% end %>
9
+ <% if show_organization_tab? %>
10
+ <li><a href="#organizations" data-toggle="tab"><%= _('Organizations') %></a></li>
11
+ <% end %>
12
+ </ul>
13
+
14
+ <div class="tab-content">
15
+
16
+ <div class="tab-pane active" id="primary">
17
+ <%= text_f f, :name %>
18
+ <%= autocomplete_f f, :search,
19
+ :path => "discovered_hosts",
20
+ :control_group_id => 'search_group', :size => 'col-md-9' %>
21
+ <%= select_f f, :hostgroup_id, accessible_hostgroups, :id, :to_label, { :include_blank => false },
22
+ { :help_inline => _('Target host group for this rule with all properties set') } %>
23
+ <%= text_f f, :hostname, :help_inline => popover(
24
+ _("Template"),
25
+ "<div>" +
26
+ _("Specify target hostname template pattern in the same syntax as in Provisioning Templates (ERB).") +
27
+ "</div>"+
28
+ "<div>" +
29
+ _("Domain will be appended automatically. A hostname based on MAC address will be used when left blank. In addition to @host attribute function rand for random integers is available. Examples:") +
30
+ "</div>"+
31
+ "<pre>"+
32
+ "myhost-&lt;%= rand(99999) %&gt;" + "\n\n" +
33
+ "abc-&lt;%= @host.facts['bios_vendor'] + " + "\n" +
34
+ " '-' + rand(99999) %&gt;" + "\n\n" +
35
+ "xyz-&lt;%= @host.hostgroup.name %&gt;" + "\n\n" +
36
+ "srv-&lt;%= @host.discovery_rule.name %&gt;" + "\n\n" +
37
+ "server-&lt;%= @host.ip.gsub('.','-') + " + "\n" +
38
+ " '-' + @host.hostgroup.subnet.name %&gt;" + "\n" +
39
+ "</pre>"+
40
+ "</div>"+
41
+ "<div>"+
42
+ _("When creating hostname patterns, make sure the resulting host names are unique. Hostnames must not start with numbers. A good approach is to use unique information provided by facter (MAC address, BIOS or serial ID).") +
43
+ "</div>", :title => _("Hostname for provisioned hosts")).html_safe %>
44
+ <%= text_f f, :max_count, :label => _('Hosts limit'), :help_inline => _('Maximum hosts provisioned with this rule (0 = unlimited)') %>
45
+ <%= text_f f, :priority, :help_inline => _('Rule priority (lower integer means higher priority)') %>
46
+ <%= checkbox_f f, :enabled %>
47
+ </div>
48
+ <% if show_taxonomy_tabs? %>
49
+ <%= render 'taxonomies/loc_org_tabs', :f => f, :obj => @discovery_rule %>
50
+ <% end %>
51
+ </div>
52
+ <%= submit_or_cancel f %>
34
53
  <% end %>
data/config/routes.rb CHANGED
@@ -7,7 +7,7 @@ Rails.application.routes.draw do
7
7
  match 'medium_selected_discovered_hosts' => 'hosts#medium_selected'
8
8
 
9
9
  constraints(:id => /[^\/]+/) do
10
- resources :discovered_hosts do
10
+ resources :discovered_hosts, :except => [:new, :create] do
11
11
  member do
12
12
  get 'refresh_facts'
13
13
  put 'reboot'
@@ -22,6 +22,7 @@ Rails.application.routes.draw do
22
22
  post 'update_multiple_location'
23
23
  get 'auto_complete_search'
24
24
  post 'auto_provision_all'
25
+ put 'reboot_all'
25
26
  end
26
27
  end
27
28
  end
@@ -40,13 +41,12 @@ Rails.application.routes.draw do
40
41
  member do
41
42
  post 'auto_provision'
42
43
  put 'reboot'
44
+ put 'refresh_facts'
43
45
  end
44
46
  collection do
45
47
  post 'facts'
46
48
  post 'auto_provision_all'
47
- end
48
- member do
49
- put 'refresh_facts'
49
+ put 'reboot_all'
50
50
  end
51
51
  end
52
52
  resources :discovery_rules, :except => [:new, :edit]
@@ -0,0 +1,8 @@
1
+ class RemoveDiscoveryAttributeSetsFromManagedHosts < ActiveRecord::Migration
2
+ def up
3
+ DiscoveryAttributeSet.where(:id => DiscoveryAttributeSet.joins(:host).where(:'hosts.managed' => true).pluck("#{DiscoveryAttributeSet.table_name}.id")).delete_all
4
+ end
5
+
6
+ def down
7
+ end
8
+ end
@@ -0,0 +1,16 @@
1
+ class RemoveOldPermissions < ActiveRecord::Migration
2
+ def up
3
+ # remove invalid permissions causing http://projects.theforeman.org/issues/9963
4
+ perms = Permission.where("name like '%_discovered_hosts' and resource_type is null").destroy_all
5
+ say "Removed invalid permissions: #{perms.inspect}" if perms.size > 0
6
+
7
+ # unassociate and remove unused role "Discovery" (renamed to "Discovery Manager")
8
+ if old_role = Role.where(:name => "Discovery").first
9
+ UserRole.where(:role_id => old_role.id).destroy_all
10
+ say "Role 'Discovery' was removed, use 'Discovery Manager' instead" if old_role.destroy
11
+ end
12
+ end
13
+
14
+ def down
15
+ end
16
+ end
@@ -0,0 +1,13 @@
1
+ class RemoveLeftoverTokens < ActiveRecord::Migration
2
+ def up
3
+ existing_tokens = Host::Managed.all.map(&:token).compact
4
+ if existing_tokens.empty?
5
+ Token.delete_all
6
+ else
7
+ Token.where('id not in (?)', existing_tokens).delete_all
8
+ end
9
+ end
10
+
11
+ def down
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ class RemoveOldDiscoveryReaderPermissions < ActiveRecord::Migration
2
+ def up
3
+ Permission.where("name like '%_discovery_rules' and resource_type is null").each do |permission|
4
+ permission.update_attributes(:resource_type => 'DiscoveryRule')
5
+ end
6
+ end
7
+
8
+ def down
9
+ end
10
+ end
@@ -0,0 +1,17 @@
1
+ class ReviewDiscoveryPermissions < ActiveRecord::Migration
2
+ def up
3
+ if (mgr = Role.find_by_name("Discovery Manager"))
4
+ perms = []
5
+ perms << "submit_discovered_hosts" if Permission.find_by_name("edit_discovered_hosts")
6
+ perms << "auto_provision_discovered_hosts" if Permission.find_by_name("provision_discovered_hosts")
7
+ perms << "create_discovery_rules" if Permission.find_by_name("new_discovery_rules")
8
+ perms << "destroy_discovery_rules" if Permission.find_by_name("delete_discovery_rules")
9
+ mgr.add_permissions!(perms)
10
+ end
11
+ Permission.find_by_name("new_discovery_rules").try(:destroy)
12
+ end
13
+
14
+ def down
15
+ # rollback is not supported
16
+ end
17
+ end