Revision 67110766
Added by Lukas Zapletal almost 6 years ago
app/controllers/hosts_controller.rb | ||
---|---|---|
before_action :find_resource, :only => [:show, :clone, :edit, :update, :destroy, :puppetrun, :review_before_build,
|
||
:setBuild, :cancelBuild, :power, :get_power_state, :overview, :bmc, :vm,
|
||
:runtime, :resources, :nics, :ipmi_boot, :console,
|
||
:toggle_manage, :pxe_config, :disassociate]
|
||
:toggle_manage, :pxe_config, :disassociate, :build_errors]
|
||
|
||
before_action :taxonomy_scope, :only => [:new, :edit] + AJAX_REQUESTS
|
||
before_action :set_host_type, :only => [:update]
|
||
... | ... | |
end
|
||
end
|
||
|
||
def build_errors
|
||
render :plain => @host.build_errors
|
||
end
|
||
|
||
def power
|
||
return invalid_request unless PowerManager::REAL_ACTIONS.include?(params[:power_action])
|
||
@host.power.send(params[:power_action].to_sym)
|
||
... | ... | |
|
||
define_action_permission [
|
||
'clone', 'externalNodes', 'overview', 'bmc', 'vm', 'runtime', 'resources', 'templates', 'nics',
|
||
'pxe_config', 'active', 'errors', 'out_of_sync', 'pending', 'disabled', 'get_power_state', 'preview_host_collection'
|
||
'pxe_config', 'active', 'errors', 'out_of_sync', 'pending', 'disabled', 'get_power_state', 'preview_host_collection', 'build_errors'
|
||
], :view
|
||
define_action_permission [
|
||
'setBuild', 'cancelBuild', 'multiple_build', 'submit_multiple_build', 'review_before_build',
|
app/controllers/unattended_controller.rb | ||
---|---|---|
# We dont require any of these methods for provisioning
|
||
skip_before_action :require_login, :session_expiry, :update_activity_time, :set_taxonomy, :authorize, :unless => Proc.new { preview? }
|
||
|
||
# Allow HTTP POST methods without CSRF
|
||
skip_before_action :verify_authenticity_token
|
||
|
||
before_action :set_admin_user, :unless => Proc.new { preview? }
|
||
# We want to find out our requesting host
|
||
before_action :get_host_details, :except => [:hostgroup_template, :built]
|
||
before_action :get_built_host_details, :only => :built
|
||
before_action :get_host_details, :except => [:hostgroup_template, :built, :failed]
|
||
before_action :get_built_host_details, :only => [:built, :failed]
|
||
before_action :allowed_to_install?, :except => :hostgroup_template
|
||
before_action :handle_ca, :if => Proc.new { params[:kind] == 'provision' }
|
||
before_action :handle_realm, :if => Proc.new { params[:kind] == 'provision' }
|
||
... | ... | |
# all of our requests should be returned in text/plain
|
||
after_action :set_content_type
|
||
|
||
# this actions is called by each operatingsystem post/finish script - it notify us that the OS installation is done.
|
||
# maximum size of built/failed request body accepted to prevent DoS (in bytes)
|
||
MAX_BUILT_BODY = 65535
|
||
|
||
def built
|
||
logger.info "#{controller_name}: #{@host.name} is Built!"
|
||
logger.info "#{controller_name}: #{@host.name} is built!"
|
||
# clear possible previous errors
|
||
@host.build_errors = nil
|
||
update_ip if Setting[:update_ip_from_built_request]
|
||
head(@host.built ? :created : :conflict)
|
||
end
|
||
|
||
def failed
|
||
return if preview? || !@host.build
|
||
logger.warn "#{controller_name}: #{@host.name} build failed!"
|
||
@host.build_errors = request.body.read(MAX_BUILT_BODY)
|
||
body_length = @host.build_errors.try(:size) || 0
|
||
@host.build_errors += "\n\nOutput trimmed\n" if body_length >= MAX_BUILT_BODY
|
||
logger.warn { "Log lines from the OS installer:\n#{@host.build_errors}" }
|
||
head(@host.built ? :created : :conflict)
|
||
end
|
||
|
||
def hostgroup_template
|
||
return head(:not_found) unless (params.has_key?("id") && params.has_key?(:hostgroup))
|
||
|
app/helpers/hosts_helper.rb | ||
---|---|---|
(SETTINGS[:unattended] && host.managed?) ? host.shortname : host.name
|
||
end
|
||
|
||
def build_duration(host)
|
||
return _("N/A") if host.initiated_at.nil? && host.installed_at.nil?
|
||
if host.installed_at.nil?
|
||
time_ago_in_words(host.initiated_at, include_seconds: true) + " (in progress)"
|
||
else
|
||
distance_of_time_in_words(host.initiated_at, host.installed_at, include_seconds: true)
|
||
end
|
||
end
|
||
|
||
def overview_fields(host)
|
||
global_status = host.build_global_status
|
||
fields = [
|
||
... | ... | |
]
|
||
]
|
||
fields += host_detailed_status_list(host)
|
||
fields += [[_("Build duration"), build_duration(host)]]
|
||
fields += [[_("Build errors"), link_to("Logs from OS installer", build_errors_host_path(:id => host.id))]] if host.build_errors.present?
|
||
fields += [[_("Token"), host.token || _("N/A")]] if User.current.admin?
|
||
fields += [[_("Domain"), link_to(host.domain, hosts_path(:search => %{domain = "#{host.domain}"}))]] if host.domain.present?
|
||
fields += [[_("Realm"), link_to(host.realm, hosts_path(:search => %{realm = "#{host.realm}"}))]] if host.realm.present?
|
||
fields += [[_("IP Address"), host.ip]] if host.ip.present?
|
app/models/host/managed.rb | ||
---|---|---|
return unless respond_to?(:old) && old && build? && !old.build?
|
||
clear_facts
|
||
clear_reports
|
||
self.build_errors = nil
|
||
end
|
||
|
||
# Called from the host build post install process to indicate that the base build has completed
|
||
... | ... | |
# Any facts are discarded
|
||
def setBuild
|
||
self.build = true
|
||
self.save
|
||
self.initiated_at = Time.now.utc
|
||
logger.warn("Set build failed: #{errors.inspect}") unless self.save
|
||
errors.empty?
|
||
end
|
||
|
app/models/host_status/build_status.rb | ||
---|---|---|
class BuildStatus < Status
|
||
PENDING = 1
|
||
TOKEN_EXPIRED = 2
|
||
BUILD_FAILED = 3
|
||
BUILT = 0
|
||
|
||
def self.status_name
|
||
... | ... | |
N_("Token expired")
|
||
when BUILT
|
||
N_("Installed")
|
||
when BUILD_FAILED
|
||
N_("Installation error")
|
||
else
|
||
N_("Unknown build status")
|
||
end
|
||
... | ... | |
|
||
def to_global(options = {})
|
||
case to_status
|
||
when TOKEN_EXPIRED
|
||
when TOKEN_EXPIRED, BUILD_FAILED
|
||
HostStatus::Global::ERROR
|
||
else
|
||
HostStatus::Global::OK
|
||
... | ... | |
PENDING
|
||
end
|
||
else
|
||
BUILT
|
||
if build_errors?
|
||
BUILD_FAILED
|
||
else
|
||
BUILT
|
||
end
|
||
end
|
||
end
|
||
|
||
... | ... | |
def token_expired?
|
||
host&.token_expired?
|
||
end
|
||
|
||
def build_errors?
|
||
host && host.build_errors.present?
|
||
end
|
||
end
|
||
end
|
||
|
app/registries/foreman/access_permissions.rb | ||
---|---|---|
tasks_ajax_actions = [:show]
|
||
|
||
map.permission :view_hosts, {:hosts => [:index, :show, :errors, :active, :out_of_sync, :disabled, :pending, :vm,
|
||
:externalNodes, :pxe_config, :auto_complete_search, :bmc,
|
||
:externalNodes, :pxe_config, :auto_complete_search, :bmc, :build_errors,
|
||
:runtime, :resources, :templates, :overview, :nics, :get_power_state, :preview_host_collection],
|
||
:dashboard => [:OutOfSync, :errors, :active],
|
||
:unattended => [:host_template, :hostgroup_template],
|
config/routes.rb | ||
---|---|---|
get 'review_before_build'
|
||
put 'setBuild'
|
||
get 'cancelBuild'
|
||
get 'build_errors'
|
||
get 'puppetrun'
|
||
get 'pxe_config'
|
||
put 'toggle_manage'
|
||
... | ... | |
# get only for alterator unattended scripts
|
||
get 'unattended/provision/:metadata', :controller => 'unattended', :action => 'host_template', :format => 'text',
|
||
:constraints => { :metadata => /(autoinstall\.scm|vm-profile\.scm|pkg-groups\.tar)/ }
|
||
# get for end of build action
|
||
# built call can be done both via GET (for backward compatibility) and POST
|
||
get 'unattended/built/(:id(:format))', :controller => 'unattended', :action => 'built', :format => 'text'
|
||
post 'unattended/built/(:id(:format))', :controller => 'unattended', :action => 'built', :format => 'text'
|
||
# failed call only via POST
|
||
post 'unattended/failed/(:id(:format))', :controller => 'unattended', :action => 'failed', :format => 'text'
|
||
# get for all unattended scripts
|
||
get 'unattended/(:kind/(:id(:format)))', :controller => 'unattended', :action => 'host_template', :format => 'text'
|
||
|
db/migrate/20180305111232_add_build_errors_to_hosts.rb | ||
---|---|---|
class AddBuildErrorsToHosts < ActiveRecord::Migration[5.1]
|
||
def change
|
||
add_column :hosts, :initiated_at, :datetime
|
||
add_column :hosts, :build_errors, :text
|
||
end
|
||
end
|
test/controllers/unattended_controller_test.rb | ||
---|---|---|
assert_response :success
|
||
end
|
||
|
||
test "should accept built notifications" do
|
||
test "should accept built notifications via legacy GET method" do
|
||
@request.env["REMOTE_ADDR"] = @ub_host.ip
|
||
get :built
|
||
assert_response :created
|
||
host = Nic::Base.primary.find_by_ip(@ub_host.ip)
|
||
assert_equal host.build, false
|
||
nic = Nic::Base.primary.find_by_ip(@ub_host.ip)
|
||
refute nic.build
|
||
end
|
||
|
||
test "should accept built notifications" do
|
||
@request.env["REMOTE_ADDR"] = @ub_host.ip
|
||
post :built
|
||
assert_response :created
|
||
nic = Nic::Base.primary.find_by_ip(@ub_host.ip)
|
||
refute nic.build
|
||
end
|
||
|
||
test "should accept failed notifications" do
|
||
@request.env["REMOTE_ADDR"] = @ub_host.ip
|
||
post :failed
|
||
assert_response :created
|
||
nic = Nic::Base.primary.find_by_ip(@ub_host.ip)
|
||
refute nic.build
|
||
end
|
||
|
||
test "should accept failed notifications with large body" do
|
||
@request.env["REMOTE_ADDR"] = @ub_host.ip
|
||
post :failed, body: (' ' * 65537)
|
||
assert_response :created
|
||
host = Nic::Base.primary.find_by_ip(@ub_host.ip).host
|
||
refute host.build
|
||
assert_match(/Output trimmed/, host.build_errors)
|
||
end
|
||
|
||
test "should accept built notifications_with_expired_token" do
|
test/unit/foreman/access_permissions_test.rb | ||
---|---|---|
MAY_SKIP_REQUIRE_LOGIN = [
|
||
"users/login", "users/logout", "users/extlogin", "users/extlogout", "home/status", "notices/destroy",
|
||
|
||
# unattended built action is not for interactive use
|
||
"unattended/built",
|
||
# unattended built and failed action is not for interactive use
|
||
"unattended/built", "unattended/failed",
|
||
|
||
# puppetmaster interfaces
|
||
"fact_values/create", "reports/create",
|
test/unit/shared/access_permissions_test_base.rb | ||
---|---|---|
# Pass if the controller deliberately skips login requirement
|
||
next if controller < ApplicationController && filters.select { |f| f.filter == :require_login }.empty?
|
||
|
||
assert_not_empty Foreman::AccessControl.permissions.select { |p| p.actions.include? path }
|
||
assert_not_empty Foreman::AccessControl.permissions.select { |p| p.actions.include? path }, "permission for #{path} not found, check access_permissions.rb"
|
||
end
|
||
end
|
||
end
|
Also available in: Unified diff
Fixes #21007 - new unattended action 'failed'