Revision e54016da
Added by Marek Hulán over 8 years ago
app/assets/stylesheets/application.scss | ||
---|---|---|
.error-message{
|
||
padding-right: 10px;
|
||
}
|
||
|
||
i.glyphicon.host-status {
|
||
margin-right: 5px;
|
||
}
|
||
|
||
span.glyphicon.host-status {
|
||
top: 3px;
|
||
}
|
app/assets/stylesheets/status-colors.scss | ||
---|---|---|
.status-ok {
|
||
color: #5CB85C;
|
||
}
|
||
|
||
.status-error {
|
||
color: #D9534F
|
||
}
|
||
|
||
.status-warn, .status-question {
|
||
color: #DB843D;
|
||
}
|
app/controllers/api/v1/hosts_controller.rb | ||
---|---|---|
eos
|
||
|
||
def status
|
||
render :json => { :status => @host.host_status }.to_json if @host
|
||
Foreman::Deprecation.api_deprecation_warning('The /status route is deprecated, please use the new /status/configuration instead')
|
||
render :json => { :status => @host.get_status(HostStatus::ConfigurationStatus).to_label }.to_json if @host
|
||
end
|
||
|
||
private
|
app/controllers/api/v2/hosts_controller.rb | ||
---|---|---|
param_group :search_and_pagination, ::Api::V2::BaseController
|
||
|
||
def index
|
||
@hosts = resource_scope_for_index
|
||
@hosts = resource_scope_for_index.includes([ :host_statuses, :compute_resource, :hostgroup, :operatingsystem, :interfaces])
|
||
# SQL optimizations queries
|
||
@last_report_ids = Report.where(:host_id => @hosts.map(&:id)).group(:host_id).maximum(:id)
|
||
@last_reports = Report.where(:id => @last_report_ids.values)
|
||
end
|
||
|
||
api :GET, "/hosts/:id/", N_("Show a host")
|
||
... | ... | |
process_response @host.destroy
|
||
end
|
||
|
||
api :GET, "/hosts/:id/status", N_("Get status of host")
|
||
api :GET, "/hosts/:id/status", N_("Get configuration status of host")
|
||
param :id, :identifier_dottable, :required => true
|
||
description <<-eos
|
||
Return value may either be one of the following:
|
||
... | ... | |
eos
|
||
|
||
def status
|
||
render :json => { :status => @host.host_status }.to_json if @host
|
||
Foreman::Deprecation.api_deprecation_warning('The /status route is deprecated, please use the new /status/configuration instead')
|
||
render :json => { :status => @host.get_status(HostStatus::ConfigurationStatus).to_label }.to_json if @host
|
||
end
|
||
|
||
api :GET, "/hosts/:id/status/:type", N_("Get status of host")
|
||
param :id, :identifier_dottable, :required => true
|
||
param :type, [ HostStatus::Global ] + HostStatus.status_registry.to_a.map { |s| s.humanized_name }, :required => true, :desc => N_(<<-eos
|
||
status type, can be one of
|
||
* global
|
||
* configuration
|
||
* build
|
||
eos
|
||
)
|
||
description N_('Returns string representing a host status of a given type')
|
||
def get_status
|
||
case params[:type]
|
||
when 'global'
|
||
@status = @host.build_global_status
|
||
else
|
||
@status = @host.get_status(HostStatus.find_status_by_humanized_name(params[:type]))
|
||
end
|
||
end
|
||
|
||
api :GET, "/hosts/:id/vm_compute_attributes", N_("Get vm attributes of host")
|
||
... | ... | |
:console
|
||
when 'disassociate'
|
||
:edit
|
||
when 'vm_compute_attributes'
|
||
when 'vm_compute_attributes', 'get_status'
|
||
:view
|
||
else
|
||
super
|
app/controllers/application_controller.rb | ||
---|---|---|
# If the user has a fact_filter then we need to include :fact_values
|
||
# We do not include most associations unless we are processing a html page
|
||
def included_associations(include = [])
|
||
include += [:hostgroup, :compute_resource, :operatingsystem, :environment, :model ]
|
||
include
|
||
include + [ :hostgroup, :compute_resource, :operatingsystem, :environment, :model, :host_statuses ]
|
||
end
|
||
|
||
def errors_hash(errors)
|
app/controllers/hosts_controller.rb | ||
---|---|---|
format.html do
|
||
@hosts = search.includes(included_associations).paginate(:page => params[:page])
|
||
# SQL optimizations queries
|
||
@last_reports = Report.where(:host_id => @hosts.map(&:id)).group(:host_id).maximum(:id)
|
||
@last_report_ids = Report.where(:host_id => @hosts.map(&:id)).group(:host_id).maximum(:id)
|
||
@last_reports = Report.where(:id => @last_report_ids.values)
|
||
# rendering index page for non index page requests (out of sync hosts etc)
|
||
@hostgroup_authorizer = Authorizer.new(User.current, :collection => @hosts.map(&:hostgroup_id).compact.uniq)
|
||
render :index if title and (@title = title)
|
||
... | ... | |
end
|
||
|
||
def review_before_build
|
||
@build = @host.build_status
|
||
@build = @host.build_status_checker
|
||
render :layout => false
|
||
end
|
||
|
app/helpers/hosts_helper.rb | ||
---|---|---|
|
||
def last_report_tooltip(record)
|
||
opts = { :rel => "twipsy" }
|
||
if @last_reports[record.id]
|
||
if @last_report_ids[record.id]
|
||
opts.merge!( "data-original-title" => _("View last report details"))
|
||
else
|
||
opts.merge!(:disabled => true, :class => "disabled", :onclick => 'return false')
|
||
... | ... | |
end
|
||
|
||
# method that reformat the hostname column by adding the status icons
|
||
def name_column(record)
|
||
label = record.host_status
|
||
case label
|
||
when "Pending Installation"
|
||
style ="label-info"
|
||
# TRANSLATORS: host's status: first character of "build"
|
||
short = s_("Build|B")
|
||
when "Alerts disabled"
|
||
style = "label-default"
|
||
# TRANSLATORS: host's status: first character of "disabled"
|
||
short = s_("Disabled|D")
|
||
when "No reports"
|
||
style = "label-default"
|
||
# TRANSLATORS: host's status: first character of "no reports"
|
||
short = s_("No reports|N")
|
||
when "Out of sync"
|
||
style = "label-warning"
|
||
# TRANSLATORS: host's status: first character of "sync" (out of sync)
|
||
short = s_("Sync|S")
|
||
when "Error"
|
||
style = "label-danger"
|
||
# TRANSLATORS: host's status: first character of "error"
|
||
short = s_("Error|E")
|
||
when "Active"
|
||
style = "label-info"
|
||
# TRANSLATORS: host's status: first character of "active"
|
||
short = s_("Active|A")
|
||
when "Pending"
|
||
style = "label-warning"
|
||
# TRANSLATORS: host's status: first character of "pending"
|
||
short = s_("Pending|P")
|
||
else
|
||
style = "label-success"
|
||
# TRANSLATORS: host's status: first character of "OK"
|
||
short = s_("OK|O")
|
||
def name_column(host)
|
||
style = host_global_status_icon_class_for_host(host)
|
||
tooltip = host.host_statuses.select(&:relevant?).sort_by(&:type).map { |status| "#{_(status.name)}: #{_(status.to_label)}" }.join(', ')
|
||
|
||
content = content_tag(:span, "", {:rel => "twipsy", :class => style, :"data-original-title" => tooltip} )
|
||
content += link_to(trunc_with_tooltip(" #{host}"), host_path(host))
|
||
content
|
||
end
|
||
|
||
def host_global_status_icon_class_for_host(host)
|
||
options = {}
|
||
options[:last_reports] = @last_reports unless @last_reports.nil?
|
||
host_global_status_icon_class(host.build_global_status(options).status)
|
||
end
|
||
|
||
def host_global_status_icon_class(status)
|
||
icon_class = case status
|
||
when HostStatus::Global::OK
|
||
'glyphicon-ok-sign'
|
||
when HostStatus::Global::WARN
|
||
'glyphicon-info-sign'
|
||
when HostStatus::Global::ERROR
|
||
'glyphicon-exclamation-sign'
|
||
else
|
||
'glyphicon-question-sign'
|
||
end
|
||
|
||
"host-status glyphicon #{icon_class} #{host_global_status_class(status)}"
|
||
end
|
||
|
||
def host_global_status_class(status)
|
||
case status
|
||
when HostStatus::Global::OK
|
||
'status-ok'
|
||
when HostStatus::Global::WARN
|
||
'status-warn'
|
||
when HostStatus::Global::ERROR
|
||
'status-error'
|
||
else
|
||
'status-question'
|
||
end
|
||
content_tag(:span, short, {:rel => "twipsy", :class => "label label-light " + style, :"data-original-title" => _(label)} ) +
|
||
link_to(trunc_with_tooltip(" #{record}"), host_path(record))
|
||
end
|
||
|
||
def days_ago(time)
|
||
... | ... | |
end
|
||
|
||
def overview_fields(host)
|
||
global_status = host.build_global_status
|
||
fields = [
|
||
[_("Status"), content_tag(:i, ''.html_safe, :class => host_global_status_icon_class(global_status.status)) +
|
||
content_tag(:span, _(global_status.to_label), :class => host_global_status_class(global_status.status))
|
||
]
|
||
]
|
||
fields += host_detailed_status_list(host)
|
||
fields += [
|
||
[_("Domain"), (link_to(host.domain, hosts_path(:search => "domain = #{host.domain}")) if host.domain)],
|
||
[_("Realm"), (link_to(host.realm, hosts_path(:search => "realm = #{host.realm}")) if host.realm)],
|
||
[_("IP Address"), host.ip],
|
||
... | ... | |
fields
|
||
end
|
||
|
||
def host_detailed_status_list(host)
|
||
host.host_statuses.sort_by(&:type).map do |status|
|
||
next unless status.relevant?
|
||
[
|
||
_(status.name),
|
||
content_tag(:i, ' '.html_safe, :class => host_global_status_icon_class(status.to_global)) +
|
||
content_tag(:span, _(status.to_label), :class => host_global_status_class(status.to_global))
|
||
]
|
||
end
|
||
end
|
||
|
||
def possible_images(cr, arch = nil, os = nil)
|
||
return cr.images unless controller_name == "hosts"
|
||
return [] unless arch && os
|
app/models/concerns/configuration_status_scoped_search.rb | ||
---|---|---|
module ConfigurationStatusScopedSearch
|
||
extend ActiveSupport::Concern
|
||
|
||
module ClassMethods
|
||
def scoped_search_status(status, options)
|
||
options.merge!({ :offset => Report::METRIC.index(status.to_s), :word_size => Report::BIT_NUM })
|
||
scoped_search options
|
||
end
|
||
end
|
||
end
|
app/models/concerns/hostext/search.rb | ||
---|---|---|
|
||
included do
|
||
include ScopedSearchExtensions
|
||
include ConfigurationStatusScopedSearch
|
||
|
||
has_many :search_parameters, :class_name => 'Parameter', :foreign_key => :reference_id
|
||
belongs_to :search_users, :class_name => 'User', :foreign_key => :owner_id
|
||
... | ... | |
scoped_search :on => :managed, :complete_value => {:true => true, :false => false}
|
||
scoped_search :on => :owner_type, :complete_value => true, :only_explicit => true
|
||
scoped_search :on => :owner_id, :complete_enabled => false, :only_explicit => true
|
||
scoped_search :on => :puppet_status, :offset => 0, :word_size => Report::BIT_NUM*4, :complete_value => {:true => true, :false => false}, :rename => :'status.interesting'
|
||
scoped_search :on => :puppet_status, :offset => Report::METRIC.index("applied"), :word_size => Report::BIT_NUM, :rename => :'status.applied'
|
||
scoped_search :on => :puppet_status, :offset => Report::METRIC.index("restarted"), :word_size => Report::BIT_NUM, :rename => :'status.restarted'
|
||
scoped_search :on => :puppet_status, :offset => Report::METRIC.index("failed"), :word_size => Report::BIT_NUM, :rename => :'status.failed'
|
||
scoped_search :on => :puppet_status, :offset => Report::METRIC.index("failed_restarts"), :word_size => Report::BIT_NUM, :rename => :'status.failed_restarts'
|
||
scoped_search :on => :puppet_status, :offset => Report::METRIC.index("skipped"), :word_size => Report::BIT_NUM, :rename => :'status.skipped'
|
||
scoped_search :on => :puppet_status, :offset => Report::METRIC.index("pending"), :word_size => Report::BIT_NUM, :rename => :'status.pending'
|
||
|
||
scoped_search :in => :configuration_status_object, :on => :status, :offset => 0, :word_size => Report::BIT_NUM*4, :rename => :'status.interesting', :complete_value => {:true => true, :false => false}
|
||
scoped_search_status "applied", :in => :configuration_status_object, :on => :status, :rename => :'status.applied'
|
||
scoped_search_status "restarted", :in => :configuration_status_object, :on => :status, :rename => :'status.restarted'
|
||
scoped_search_status "failed", :in => :configuration_status_object, :on => :status, :rename => :'status.failed'
|
||
scoped_search_status "failed_restarts", :in => :configuration_status_object, :on => :status, :rename => :'status.failed_restarts'
|
||
scoped_search_status "skipped", :in => :configuration_status_object, :on => :status, :rename => :'status.skipped'
|
||
scoped_search_status "pending", :in => :configuration_status_object, :on => :status, :rename => :'status.pending'
|
||
|
||
scoped_search :on => :global_status, :complete_value => { :ok => HostStatus::Global::OK, :warning => HostStatus::Global::WARN, :error => HostStatus::Global::ERROR }
|
||
|
||
scoped_search :in => :model, :on => :name, :complete_value => true, :rename => :model
|
||
scoped_search :in => :hostgroup, :on => :name, :complete_value => true, :rename => :hostgroup
|
app/models/concerns/report_common.rb | ||
---|---|---|
module ReportCommon
|
||
METRIC = %w[applied restarted failed failed_restarts skipped pending]
|
||
BIT_NUM = 6
|
||
MAX = (1 << BIT_NUM) -1 # maximum value per metric
|
||
LOG_LEVELS = %w[debug info notice warning err alert emerg crit]
|
||
|
||
extend ActiveSupport::Concern
|
||
|
||
included do
|
||
# search for a metric - e.g.:
|
||
# Report.with("failed") --> all reports which have a failed counter > 0
|
||
# Report.with("failed",20) --> all reports which have a failed counter > 20
|
||
scope :with, ->(*arg) { { :conditions => "(#{report_status} >> #{BIT_NUM*METRIC.index(arg[0])} & #{MAX}) > #{arg[1] || 0}"} }
|
||
end
|
||
|
||
# generate dynamically methods for all metrics
|
||
# e.g. Report.last.applied
|
||
METRIC.each do |method|
|
||
define_method method do
|
||
status method
|
||
end
|
||
end
|
||
|
||
# returns true if total error metrics are > 0
|
||
def error?
|
||
%w[failed failed_restarts].sum {|f| status f} > 0
|
||
end
|
||
|
||
# returns true if total action metrics are > 0
|
||
def changes?
|
||
%w[applied restarted].sum {|f| status f} > 0
|
||
end
|
||
|
||
# returns true if there are any changes pending
|
||
def pending?
|
||
pending > 0
|
||
end
|
||
|
||
#returns metrics
|
||
#when no metric type is specific returns hash with all values
|
||
#passing a METRIC member will return its value
|
||
def status(type = nil)
|
||
@calc ||= ReportStatusCalculator.new(:bit_field => read_attribute(self.class.report_status))
|
||
@calc.status(type)
|
||
end
|
||
end
|
app/models/host/hostmix.rb | ||
---|---|---|
belongs_to :host, {:class_name => "Host::Managed", :foreign_key => :host_id}.merge(options)
|
||
end
|
||
end
|
||
end
|
||
end
|
app/models/host/managed.rb | ||
---|---|---|
class Host::Managed < Host::Base
|
||
include ReportCommon
|
||
include Hostext::Search
|
||
PROVISION_METHODS = %w[build image]
|
||
|
||
... | ... | |
has_many :puppetclasses, :through => :host_classes, :dependent => :destroy
|
||
belongs_to :hostgroup
|
||
has_many :reports, :foreign_key => :host_id
|
||
has_one :last_report_object, :foreign_key => :host_id, :order => "#{Report.table_name}.id DESC", :class_name => 'Report'
|
||
has_many :host_parameters, :dependent => :destroy, :foreign_key => :reference_id, :inverse_of => :host
|
||
has_many :parameters, :dependent => :destroy, :foreign_key => :reference_id, :class_name => "HostParameter"
|
||
accepts_nested_attributes_for :host_parameters, :allow_destroy => true
|
||
... | ... | |
belongs_to :owner, :polymorphic => true
|
||
belongs_to :compute_resource
|
||
belongs_to :image
|
||
has_many :host_statuses, :class_name => 'HostStatus::Status', :foreign_key => 'host_id', :inverse_of => :host,
|
||
:dependent => :destroy
|
||
has_one :configuration_status_object, :class_name => 'HostStatus::ConfigurationStatus', :foreign_key => 'host_id'
|
||
|
||
has_one :token, :foreign_key => :host_id, :dependent => :destroy
|
||
before_destroy :remove_reports
|
||
... | ... | |
define_model_callbacks :build, :only => :after
|
||
define_model_callbacks :provision, :only => :before
|
||
|
||
before_validation :refresh_build_status, :if => :build_changed?
|
||
|
||
# Custom hooks will be executed after_commit
|
||
after_commit :build_hooks
|
||
before_save :clear_data_on_build
|
||
... | ... | |
|
||
scope :with_os, -> { where('hosts.operatingsystem_id IS NOT NULL') }
|
||
|
||
scope :with_status, lambda { |status_type|
|
||
includes(:host_statuses).where("host_status.type = '#{status_type}'")
|
||
}
|
||
|
||
scope :with_config_status, lambda {
|
||
with_status('HostStatus::ConfigurationStatus')
|
||
}
|
||
|
||
# search for a metric - e.g.:
|
||
# Host::Managed.with("failed") --> all reports which have a failed counter > 0
|
||
# Host::Managed.with("failed",20) --> all reports which have a failed counter > 20
|
||
scope :with, lambda { |*arg|
|
||
with_config_status.where("(host_status.status >> #{HostStatus::ConfigurationStatus.bit_mask(arg[0].to_s)}) > #{arg[1] || 0}")
|
||
}
|
||
|
||
scope :with_error, lambda {
|
||
where("(puppet_status > 0) and
|
||
( ((puppet_status >> #{BIT_NUM*METRIC.index("failed")} & #{MAX}) != 0) or
|
||
((puppet_status >> #{BIT_NUM*METRIC.index("failed_restarts")} & #{MAX}) != 0) )")
|
||
with_config_status.where("(host_status.status > 0) and (
|
||
#{HostStatus::ConfigurationStatus.is('failed')} or
|
||
#{HostStatus::ConfigurationStatus.is('failed_restarts')}
|
||
)")
|
||
}
|
||
|
||
scope :without_error, lambda {
|
||
where("((puppet_status >> #{BIT_NUM*METRIC.index("failed")} & #{MAX}) = 0) and
|
||
((puppet_status >> #{BIT_NUM*METRIC.index("failed_restarts")} & #{MAX}) = 0)")
|
||
with_config_status.where("
|
||
#{HostStatus::ConfigurationStatus.is_not('failed')} and
|
||
#{HostStatus::ConfigurationStatus.is_not('failed_restarts')}
|
||
")
|
||
}
|
||
|
||
scope :with_changes, lambda {
|
||
where("(puppet_status > 0) and
|
||
( ((puppet_status >> #{BIT_NUM*METRIC.index("applied")} & #{MAX}) != 0) or
|
||
((puppet_status >> #{BIT_NUM*METRIC.index("restarted")} & #{MAX}) != 0) )")
|
||
with_config_status.where("(host_status.status > 0) and (
|
||
#{HostStatus::ConfigurationStatus.is('applied')} or
|
||
#{HostStatus::ConfigurationStatus.is('restarted')}
|
||
)")
|
||
}
|
||
|
||
scope :without_changes, lambda {
|
||
where("((puppet_status >> #{BIT_NUM*METRIC.index("applied")} & #{MAX}) = 0) and
|
||
((puppet_status >> #{BIT_NUM*METRIC.index("restarted")} & #{MAX}) = 0)")
|
||
with_config_status.where("
|
||
#{HostStatus::ConfigurationStatus.is_not('applied')} and
|
||
#{HostStatus::ConfigurationStatus.is_not('restarted')}
|
||
")
|
||
}
|
||
|
||
scope :with_pending_changes, -> { where("(puppet_status > 0) and ((puppet_status >> #{BIT_NUM*METRIC.index("pending")} & #{MAX}) != 0)") }
|
||
scope :without_pending_changes, -> { where("((puppet_status >> #{BIT_NUM*METRIC.index("pending")} & #{MAX}) = 0)") }
|
||
scope :with_pending_changes, lambda {
|
||
with_config_status.where("(host_status.status > 0) AND (#{HostStatus::ConfigurationStatus.is('pending')})")
|
||
}
|
||
|
||
scope :without_pending_changes, lambda {
|
||
with_config_status.where("#{HostStatus::ConfigurationStatus.is_not('pending')}")
|
||
}
|
||
|
||
scope :successful, -> { without_changes.without_error.without_pending_changes}
|
||
|
||
... | ... | |
if self.compute_resource
|
||
host.compute_attributes = host.compute_resource.vm_compute_attributes_for(self.uuid)
|
||
end
|
||
host.puppet_status = 0
|
||
host.refresh_global_status
|
||
host
|
||
end
|
||
|
||
... | ... | |
compute_resource ? compute_resource.vm_compute_attributes_for(uuid) : nil
|
||
end
|
||
|
||
def host_status
|
||
if build
|
||
N_("Pending Installation")
|
||
elsif respond_to?(:enabled) && !enabled
|
||
N_("Alerts disabled")
|
||
elsif respond_to?(:last_report) && last_report.nil?
|
||
N_("No reports")
|
||
elsif no_report
|
||
N_("Out of sync")
|
||
elsif error?
|
||
N_("Error")
|
||
elsif changes?
|
||
N_("Active")
|
||
elsif pending?
|
||
N_("Pending")
|
||
else
|
||
N_("No changes")
|
||
end
|
||
end
|
||
|
||
def smart_proxies
|
||
SmartProxy.where(:id => smart_proxy_ids)
|
||
end
|
||
... | ... | |
unattended_render(template)
|
||
end
|
||
|
||
def build_status
|
||
def build_status_checker
|
||
build_status = HostBuildStatus.new(self)
|
||
build_status.check_all_statuses
|
||
build_status
|
||
... | ... | |
@old = super { |clone| clone.interfaces = self.interfaces.map {|i| setup_object_clone(i) } }
|
||
end
|
||
|
||
def refresh_global_status
|
||
self.global_status = build_global_status.status
|
||
end
|
||
|
||
def refresh_statuses
|
||
HostStatus.status_registry.each do |status_class|
|
||
status = get_status(status_class)
|
||
status.refresh! if status.relevant?
|
||
end
|
||
host_statuses.reload
|
||
refresh_global_status
|
||
end
|
||
|
||
def get_status(type)
|
||
status = self.new_record? ? host_statuses.detect { |s| s.type == type.to_s } : host_statuses.find_by_type(type.to_s)
|
||
if status.nil?
|
||
host_statuses.new(:host => self, :type => type.to_s)
|
||
else
|
||
status
|
||
end
|
||
end
|
||
|
||
def build_global_status(options = {})
|
||
HostStatus::Global.build(host_statuses, options)
|
||
end
|
||
|
||
def global_status_label(options = {})
|
||
HostStatus::Global.build(host_statuses, options).to_label
|
||
end
|
||
|
||
def configuration_status(options = {})
|
||
@configuration_status ||= get_status(HostStatus::ConfigurationStatus).to_status(options)
|
||
end
|
||
|
||
def configuration_status_label(options = {})
|
||
@configuration_status_label ||= get_status(HostStatus::ConfigurationStatus).to_label(options)
|
||
end
|
||
|
||
def puppet_status
|
||
Foreman::Deprecation.deprecation_warning('1.12', 'Host#puppet_status has been deprecated, you should use configuration_status')
|
||
configuration_status
|
||
end
|
||
|
||
def build_status(options = {})
|
||
@build_status ||= get_status(HostStatus::BuildStatus).to_status(options)
|
||
end
|
||
|
||
def build_status_label(options = {})
|
||
@build_status_label ||= get_status(HostStatus::BuildStatus).to_label(options)
|
||
end
|
||
|
||
private
|
||
|
||
# validate uniqueness can't prevent saving two interfaces that has same DNS name
|
||
... | ... | |
status
|
||
end
|
||
|
||
# alias to ensure same method that resolves the last report between the hosts and reports tables.
|
||
def reported_at
|
||
last_report
|
||
end
|
||
|
||
# puppet report status table column name
|
||
def self.report_status
|
||
"puppet_status"
|
||
end
|
||
|
||
# converts a name into ip address using DNS.
|
||
# if we are managing DNS, we can query the correct DNS server
|
||
# otherwise, use normal systems dns settings to resolv
|
||
... | ... | |
self.config_groups = []
|
||
end
|
||
end
|
||
|
||
def refresh_build_status
|
||
self.get_status(HostStatus::BuildStatus).refresh
|
||
end
|
||
end
|
app/models/host_status.rb | ||
---|---|---|
module HostStatus
|
||
def self.status_registry
|
||
@status_registry ||= Set.new
|
||
end
|
||
|
||
def self.find_status_by_humanized_name(name)
|
||
status_registry.find { |s| s.humanized_name == name }
|
||
end
|
||
end
|
||
|
||
require_dependency 'host_status/status'
|
app/models/host_status/build_status.rb | ||
---|---|---|
module HostStatus
|
||
class BuildStatus < Status
|
||
PENDING = 1
|
||
BUILT = 0
|
||
|
||
def self.status_name
|
||
N_("Build")
|
||
end
|
||
|
||
def to_label(options = {})
|
||
case to_status
|
||
when PENDING
|
||
N_("Pending installation")
|
||
when BUILT
|
||
N_("Installed")
|
||
else
|
||
N_("Unknown build status")
|
||
end
|
||
end
|
||
|
||
def to_status(options = {})
|
||
if waiting_for_build?
|
||
PENDING
|
||
else
|
||
BUILT
|
||
end
|
||
end
|
||
|
||
def relevant?
|
||
SETTINGS[:unattended] && host.managed?
|
||
end
|
||
|
||
def waiting_for_build?
|
||
host && host.build
|
||
end
|
||
end
|
||
end
|
||
|
||
HostStatus.status_registry.add(HostStatus::BuildStatus)
|
app/models/host_status/configuration_status.rb | ||
---|---|---|
module HostStatus
|
||
class ConfigurationStatus < Status
|
||
delegate :error?, :changes?, :pending?, :to => :calculator
|
||
delegate(*Report::METRIC, :to => :calculator)
|
||
|
||
def last_report
|
||
self.last_report = host.last_report_object unless @last_report_set
|
||
@last_report
|
||
end
|
||
|
||
def last_report=(report)
|
||
@last_report_set = true
|
||
@last_report = report
|
||
end
|
||
|
||
def out_of_sync?
|
||
if (host && !host.enabled?) || no_reports?
|
||
false
|
||
else
|
||
!reported_at.nil? && reported_at < (Time.now - (Setting[:puppet_interval] + Setting[:outofsync_interval]).minutes)
|
||
end
|
||
end
|
||
|
||
def no_reports?
|
||
host && last_report.nil?
|
||
end
|
||
|
||
def to_global(options = {})
|
||
handle_options(options)
|
||
|
||
if error?
|
||
# error
|
||
return HostStatus::Global::ERROR
|
||
elsif out_of_sync?
|
||
# out of sync
|
||
return HostStatus::Global::WARN
|
||
else
|
||
# active, pending, no changes, no reports
|
||
return HostStatus::Global::OK
|
||
end
|
||
end
|
||
|
||
def self.status_name
|
||
N_("Configuration")
|
||
end
|
||
|
||
def to_label(options = {})
|
||
handle_options(options)
|
||
|
||
if host && !host.enabled
|
||
N_("Alerts disabled")
|
||
elsif no_reports?
|
||
N_("No reports")
|
||
elsif error?
|
||
N_("Error")
|
||
elsif out_of_sync?
|
||
N_("Out of sync")
|
||
elsif changes?
|
||
N_("Active")
|
||
elsif pending?
|
||
N_("Pending")
|
||
else
|
||
N_("No changes")
|
||
end
|
||
end
|
||
|
||
def to_status(options = {})
|
||
handle_options(options)
|
||
|
||
if host && last_report.present?
|
||
last_report.read_attribute(:status)
|
||
else
|
||
0
|
||
end
|
||
end
|
||
|
||
def self.is(config_status)
|
||
"((host_status.status >> #{bit_mask(config_status)}) != 0)"
|
||
end
|
||
|
||
def self.is_not(config_status)
|
||
"((host_status.status >> #{bit_mask(config_status)}) = 0)"
|
||
end
|
||
|
||
def self.bit_mask(config_status)
|
||
"#{Report::BIT_NUM * Report::METRIC.index(config_status)} & #{Report::MAX}"
|
||
end
|
||
|
||
private
|
||
|
||
def handle_options(options)
|
||
if options.has_key?(:last_reports) && !options[:last_reports].nil?
|
||
cached_report = options[:last_reports].find { |r| r.host_id == self.host_id }
|
||
self.last_report = cached_report
|
||
end
|
||
end
|
||
|
||
def update_timestamp
|
||
self.reported_at = last_report.try(:reported_at) || Time.now
|
||
end
|
||
|
||
def calculator
|
||
ReportStatusCalculator.new(:bit_field => status)
|
||
end
|
||
end
|
||
end
|
||
|
||
HostStatus.status_registry.add(HostStatus::ConfigurationStatus)
|
app/models/host_status/global.rb | ||
---|---|---|
module HostStatus
|
||
class Global
|
||
OK = 0
|
||
WARN = 1
|
||
ERROR = 2
|
||
|
||
attr_accessor :status
|
||
|
||
def self.build(statuses, options = {})
|
||
max_status = statuses.select { |s| s.relevant? }.map { |s| s.to_global(options) }.max
|
||
|
||
new(max_status || OK)
|
||
end
|
||
|
||
def self.status_name
|
||
N_('Global')
|
||
end
|
||
|
||
def name
|
||
self.class.status_name
|
||
end
|
||
|
||
def initialize(status)
|
||
self.status = status
|
||
end
|
||
|
||
def to_label
|
||
case status
|
||
when OK
|
||
N_('OK')
|
||
when WARN
|
||
N_('Warning')
|
||
when ERROR
|
||
N_('Error')
|
||
else
|
||
raise 'Unknown global status'
|
||
end
|
||
end
|
||
end
|
||
end
|
app/models/host_status/status.rb | ||
---|---|---|
module HostStatus
|
||
class Status < ActiveRecord::Base
|
||
include Foreman::STI
|
||
|
||
self.table_name = 'host_status'
|
||
|
||
belongs_to_host :inverse_of => :host_statuses
|
||
|
||
attr_accessible :host, :type
|
||
|
||
def to_global(options = {})
|
||
HostStatus::Global::OK
|
||
end
|
||
|
||
def to_label(options = {})
|
||
raise NotImplementedError, "Method 'to_label' method needs to be implemented"
|
||
end
|
||
|
||
def to_status(options = {})
|
||
raise NotImplementedError, "Method 'to_status' method needs to be implemented"
|
||
end
|
||
|
||
def self.status_name
|
||
raise NotImplementedError, "Method 'status_name' method needs to be implemented"
|
||
end
|
||
|
||
def name
|
||
self.class.status_name
|
||
end
|
||
|
||
def self.humanized_name
|
||
status_name.underscore
|
||
end
|
||
|
||
def refresh!
|
||
refresh
|
||
save!
|
||
end
|
||
|
||
def refresh
|
||
update_timestamp
|
||
update_status
|
||
end
|
||
|
||
def relevant?
|
||
true
|
||
end
|
||
|
||
private
|
||
|
||
def update_timestamp
|
||
self.reported_at = Time.now
|
||
end
|
||
|
||
def update_status
|
||
self.status = to_status
|
||
end
|
||
end
|
||
end
|
||
|
||
require_dependency 'host_status/configuration_status'
|
||
require_dependency 'host_status/build_status'
|
app/models/report.rb | ||
---|---|---|
class Report < ActiveRecord::Base
|
||
METRIC = %w[applied restarted failed failed_restarts skipped pending]
|
||
BIT_NUM = 6
|
||
MAX = (1 << BIT_NUM) -1 # maximum value per metric
|
||
LOG_LEVELS = %w[debug info notice warning err alert emerg crit]
|
||
|
||
include Authorizable
|
||
include ReportCommon
|
||
include ConfigurationStatusScopedSearch
|
||
|
||
validates_lengths_from_database
|
||
belongs_to_host
|
||
... | ... | |
scoped_search :on => :reported_at, :complete_value => true, :default_order => :desc, :rename => :reported, :only_explicit => true
|
||
scoped_search :on => :status, :offset => 0, :word_size => 4*BIT_NUM, :complete_value => {:true => true, :false => false}, :rename => :eventful
|
||
|
||
scoped_search :on => :status, :offset => METRIC.index("applied"), :word_size => BIT_NUM, :rename => :applied
|
||
scoped_search :on => :status, :offset => METRIC.index("restarted"), :word_size => BIT_NUM, :rename => :restarted
|
||
scoped_search :on => :status, :offset => METRIC.index("failed"), :word_size => BIT_NUM, :rename => :failed
|
||
scoped_search :on => :status, :offset => METRIC.index("failed_restarts"), :word_size => BIT_NUM, :rename => :failed_restarts
|
||
scoped_search :on => :status, :offset => METRIC.index("skipped"), :word_size => BIT_NUM, :rename => :skipped
|
||
scoped_search :on => :status, :offset => METRIC.index("pending"), :word_size => BIT_NUM, :rename => :pending
|
||
scoped_search_status 'applied', :on => :status, :rename => :applied
|
||
scoped_search_status 'restarted', :on => :status, :rename => :restarted
|
||
scoped_search_status 'failed', :on => :status, :rename => :failed
|
||
scoped_search_status 'failed_restarts', :on => :status, :rename => :failed_restarts
|
||
scoped_search_status 'skipped', :on => :status, :rename => :skipped
|
||
scoped_search_status 'pending', :on => :status, :rename => :pending
|
||
|
||
# search for a metric - e.g.:
|
||
# Report.with("failed") --> all reports which have a failed counter > 0
|
||
# Report.with("failed",20) --> all reports which have a failed counter > 20
|
||
scope :with, lambda { |*arg|
|
||
where("(#{report_status} >> #{HostStatus::ConfigurationStatus.bit_mask(arg[0].to_s)}) > #{arg[1] || 0}")
|
||
}
|
||
|
||
# returns reports for hosts in the User's filter set
|
||
scope :my_reports, lambda {
|
||
... | ... | |
else
|
||
raise Foreman::Exception(N_('Unsupported report status format'))
|
||
end
|
||
@calc = nil
|
||
write_attribute(:status,s)
|
||
write_attribute(:status, s)
|
||
end
|
||
|
||
# extracts serialized metrics and keep them as a hash_with_indifferent_access
|
||
... | ... | |
METRIC.each {|m| metrics[m] = 0 }
|
||
host.reports.recent(time).select(:status).each do |r|
|
||
metrics.each_key do |m|
|
||
metrics[m] += r.status(m)
|
||
metrics[m] += r.status_of(m)
|
||
end
|
||
end
|
||
list[host.name] = {:metrics => metrics, :id => host.id} if metrics.values.sum > 0
|
||
... | ... | |
def self.report_status
|
||
"status"
|
||
end
|
||
|
||
delegate :error?, :changes?, :pending?, :status, :status_of, :to => :calculator
|
||
delegate(*METRIC, :to => :calculator)
|
||
|
||
def calculator
|
||
ReportStatusCalculator.new(:bit_field => read_attribute(self.class.report_status))
|
||
end
|
||
end
|
app/services/foreman/access_permissions.rb | ||
---|---|---|
:dashboard => [:OutOfSync, :errors, :active],
|
||
:unattended => [:template, :provision],
|
||
:"api/v1/hosts" => [:index, :show, :status],
|
||
:"api/v2/hosts" => [:index, :show, :status, :vm_compute_attributes],
|
||
:"api/v2/hosts" => [:index, :show, :status, :get_status, :vm_compute_attributes],
|
||
:"api/v2/interfaces" => [:index, :show],
|
||
:locations => [:mismatches],
|
||
:organizations => [:mismatches]
|
app/services/foreman/plugin.rb | ||
---|---|---|
end
|
||
@apipie_ignored_controllers
|
||
end
|
||
|
||
# register custom host status class, it should inherit from HostStatus::Status
|
||
def register_custom_status(klass)
|
||
HostStatus.status_registry.add(klass)
|
||
end
|
||
end
|
||
end
|
app/services/report_importer.rb | ||
---|---|---|
end
|
||
|
||
# convert report status to bit field
|
||
st = ReportStatusCalculator.new(:counters => raw['status']).calculate
|
||
st = ReportStatusCalculator.new(:counters => raw['status']).calculate
|
||
|
||
# we update our host record, so we won't need to lookup the report information just to display the host list / info
|
||
host.last_report = time if host.last_report.nil? or host.last_report.utc < time
|
||
# we save the report bit status value in our host too.
|
||
host.puppet_status = st
|
||
|
||
# if proxy authentication is enabled and we have no puppet proxy set, use it.
|
||
host.puppet_proxy_id ||= proxy_id
|
||
... | ... | |
# Check for errors
|
||
inspect_report
|
||
logger.info("Imported report for #{name} in #{(Time.now - start_time).round(2)} seconds")
|
||
|
||
host.refresh_statuses
|
||
end
|
||
|
||
private
|
app/services/report_status_calculator.rb | ||
---|---|---|
@raw_status = options[:bit_field] || 0
|
||
end
|
||
|
||
# calculates the raw_status based on counters
|
||
def calculate
|
||
@raw_status = 0
|
||
counters.each do |type, value|
|
||
... | ... | |
raw_status
|
||
end
|
||
|
||
#returns metrics
|
||
#when no metric type is specific returns hash with all values
|
||
#passing a METRIC member will return its value
|
||
def status(type = nil)
|
||
calculate if raw_status == 0
|
||
raise(Foreman::Exception(N_("invalid type %s") % type)) if type && !Report::METRIC.include?(type)
|
||
counters = Hash.new(0)
|
||
(type.is_a?(String) ? [type] : Report::METRIC).each do |m|
|
||
counters[m] = (raw_status || 0) >> (Report::BIT_NUM * Report::METRIC.index(m)) & Report::MAX
|
||
# returns metrics (counters) based on raw_status (aka bit field)
|
||
# to get status of specific metric, @see #status_of
|
||
def status
|
||
@status ||= begin
|
||
calculate if raw_status == 0
|
||
counters = Hash.new(0)
|
||
Report::METRIC.each do |m|
|
||
counters[m] = (raw_status || 0) >> (Report::BIT_NUM * Report::METRIC.index(m)) & Report::MAX
|
||
end
|
||
counters
|
||
end
|
||
end
|
||
|
||
def status_of(counter)
|
||
raise(Foreman::Exception.new(N_("invalid type %s"), counter)) unless Report::METRIC.include?(counter)
|
||
status[counter]
|
||
end
|
||
|
||
# returns true if total error metrics are > 0
|
||
def error?
|
||
status_of('failed') + status_of('failed_restarts') > 0
|
||
end
|
||
|
||
# returns true if total action metrics are > 0
|
||
def changes?
|
||
status_of('applied') + status_of('restarted') > 0
|
||
end
|
||
|
||
# returns true if there are any changes pending
|
||
def pending?
|
||
status_of('pending') > 0
|
||
end
|
||
|
||
# generate dynamically methods for all metrics
|
||
# e.g. applied failed ...
|
||
Report::METRIC.each do |method|
|
||
define_method method do
|
||
status_of(method)
|
||
end
|
||
type.nil? ? counters : counters[type]
|
||
end
|
||
|
||
private
|
||
|
||
attr_reader :raw_status, :counters
|
||
end
|
||
end
|
app/views/api/v2/hosts/get_status.json.rabl | ||
---|---|---|
object @status
|
||
|
||
attributes :to_label => :status_label
|
||
attributes :status
|
app/views/api/v2/hosts/main.json.rabl | ||
---|---|---|
|
||
extends "api/v2/hosts/base"
|
||
|
||
# we need to cache results with @last_reports, rabl can't pass custom parameters to attriute methods
|
||
@object.global_status_label(:last_reports => @last_reports)
|
||
@object.configuration_status(:last_reports => @last_reports)
|
||
@object.configuration_status_label(:last_reports => @last_reports)
|
||
|
||
attributes :ip, :environment_id, :environment_name, :last_report, :mac, :realm_id, :realm_name,
|
||
:sp_mac, :sp_ip, :sp_name, :domain_id, :domain_name, :architecture_id, :architecture_name, :operatingsystem_id, :operatingsystem_name,
|
||
:subnet_id, :subnet_name, :sp_subnet_id, :ptable_id, :ptable_name, :medium_id, :medium_name, :build,
|
||
... | ... | |
:enabled, :puppet_ca_proxy_id, :managed, :use_image, :image_file, :uuid, :compute_resource_id, :compute_resource_name,
|
||
:compute_profile_id, :compute_profile_name, :capabilities, :provision_method,
|
||
:puppet_proxy_id, :certname, :image_id, :image_name, :created_at, :updated_at,
|
||
:last_compile, :puppet_status
|
||
:last_compile, :global_status, :global_status_label
|
||
attributes :organization_id, :organization_name if SETTINGS[:organizations_enabled]
|
||
attributes :location_id, :location_name if SETTINGS[:locations_enabled]
|
||
|
||
# to avoid deprecation warning on puppet_status method
|
||
attributes :configuration_status => :puppet_status
|
||
|
||
HostStatus.status_registry.each do |status_class|
|
||
attributes "#{status_class.humanized_name}_status", "#{status_class.humanized_name}_status_label", :if => @object.get_status(status_class).relevant?
|
||
end
|
config/application.rb | ||
---|---|---|
config.autoload_paths += %W(#{config.root}/app/models/auth_sources)
|
||
config.autoload_paths += %W(#{config.root}/app/models/compute_resources)
|
||
config.autoload_paths += %W(#{config.root}/app/models/lookup_keys)
|
||
config.autoload_paths += %W(#{config.root}/app/models/host_status)
|
||
config.autoload_paths += %W(#{config.root}/app/models/operatingsystems)
|
||
config.autoload_paths += %W(#{config.root}/app/models/parameters)
|
||
config.autoload_paths += %W(#{config.root}/app/models/trends)
|
config/routes/api/v2.rb | ||
---|---|---|
end
|
||
resources :hosts, :except => [:new, :edit] do
|
||
get :status, :on => :member
|
||
get 'status/:type', :on => :member, :action => :get_status
|
||
get :vm_compute_attributes, :on => :member
|
||
put :puppetrun, :on => :member
|
||
put :disassociate, :on => :member
|
db/migrate/20150612135546_create_host_status.rb | ||
---|---|---|
class CreateHostStatus < ActiveRecord::Migration
|
||
def up
|
||
create_table :host_status do |t|
|
||
t.string :type
|
||
t.integer :status, :default => 0, :null => false
|
||
t.references :host, :null => false
|
||
t.datetime :reported_at, :null => false
|
||
end
|
||
add_index :host_status, :host_id
|
||
add_foreign_key "host_status", "hosts", :name => "host_status_hosts_host_id_fk", :column => 'host_id'
|
||
add_column :hosts, :global_status, :integer, :default => 0, :null => false
|
||
|
||
Host.all.each do |host|
|
||
host.refresh_statuses
|
||
end
|
||
|
||
remove_column :hosts, :puppet_status
|
||
end
|
||
|
||
def down
|
||
add_column :hosts, :puppet_status, :integer, :null => false, :default => 0
|
||
remove_column :hosts, :global_status
|
||
remove_foreign_key "host_status", :name => "host_status_hosts_host_id_fk"
|
||
remove_index :host_status, :host_id
|
||
|
||
Host.all.each do |host|
|
||
config_status = host.host_statuses.find_by_type("HostStatus::ConfigurationStatus")
|
||
unless config_status.nil?
|
||
host.puppet_status = config_status.status
|
||
host.save
|
||
end
|
||
end
|
||
|
||
drop_table :host_status
|
||
end
|
||
end
|
test/functional/api/v2/hosts_controller_test.rb | ||
---|---|---|
assert_response :success
|
||
end
|
||
|
||
test "should show specific status hosts" do
|
||
get :get_status, { :id => @host.to_param, :type => 'global' }
|
||
assert_response :success
|
||
end
|
||
|
||
test "should be able to create hosts even when restricted" do
|
||
disable_orchestration
|
||
assert_difference('Host.count') do
|
test/unit/host_build_status_test.rb | ||
---|---|---|
:domain => domains(:mydomain), :operatingsystem => operatingsystems(:redhat), :subnet => subnets(:one), :puppet_proxy => smart_proxies(:puppetmaster),
|
||
:architecture => architectures(:x86_64), :environment => environments(:production), :managed => true,
|
||
:owner_type => "User", :root_pass => "xybxa6JUkz63w")
|
||
@build = @host.build_status
|
||
@build = @host.build_status_checker
|
||
# bypass host.valid?
|
||
HostBuildStatus.any_instance.stubs(:host_status).returns(true)
|
||
end
|
||
... | ... | |
host = @host
|
||
kind = FactoryGirl.create(:template_kind)
|
||
FactoryGirl.create(:provisioning_template, :template => "provision script <%= @foreman.server.status %>",:name => "My Failed Template", :template_kind => kind, :operatingsystem_ids => [host.operatingsystem_id], :environment_ids => [host.environment_id], :hostgroup_ids => [host.hostgroup_id] )
|
||
@build = host.build_status
|
||
@build = host.build_status_checker
|
||
refute_empty @build.errors[:templates]
|
||
end
|
||
|
test/unit/host_status/build_status_test.rb | ||
---|---|---|
require 'test_helper'
|
||
|
||
class BuildStatusTest < ActiveSupport::TestCase
|
||
def setup
|
||
@host = FactoryGirl.build(:host)
|
||
@status = HostStatus::BuildStatus.new
|
||
@status.host = @host
|
||
end
|
||
|
||
test '#to_label changes based on waiting_for_build?' do
|
||
@status.stub(:waiting_for_build?, true) do
|
||
assert_equal 'Pending installation', @status.to_label
|
||
end
|
||
|
||
@status.stub(:waiting_for_build?, false) do
|
||
assert_equal 'Installed', @status.to_label
|
||
end
|
||
end
|
||
|
||
test '#relevant? is only for managed hosts in unattended mode' do
|
||
@host.managed = true
|
||
assert @status.relevant?
|
||
|
||
original, SETTINGS[:unattended] = SETTINGS[:unattended], false
|
||
refute @status.relevant?
|
||
SETTINGS[:unattended] = original
|
||
|
||
@host.managed = false
|
||
refute @status.relevant?
|
||
end
|
||
|
||
test '#waiting_for_build? verifies build flag and host relation' do
|
||
refute @status.waiting_for_build?
|
||
|
||
@status.host.build = true
|
||
assert @status.waiting_for_build?
|
||
|
||
@status.host = nil
|
||
refute @status.waiting_for_build?
|
||
end
|
||
end
|
test/unit/host_status/configuration_status_test.rb | ||
---|---|---|
require 'test_helper'
|
||
|
||
class ConfigurationStatusTest < ActiveSupport::TestCase
|
||
def setup
|
||
@host = FactoryGirl.create(:host)
|
||
@report = @host.reports.build
|
||
@report.status = {"applied" => 92, "restarted" => 300, "failed" => 4, "failed_restarts" => 12, "skipped" => 3, "pending" => 0}
|
||
@report.reported_at = '2015-01-01 00:00:00'
|
||
@report.save
|
||
@status = HostStatus::ConfigurationStatus.new(:host => @host)
|
||
@status.refresh!
|
||
end
|
||
|
||
test '#last_report defaults to host\'s last if nothing was set yet' do
|
||
assert_equal @report, @status.last_report
|
||
end
|
||
|
||
test '#last_report returns custom value that was set using writer method' do
|
||
@status.last_report = :something
|
||
assert_equal :something, @status.last_report
|
||
end
|
||
|
||
test '#last_report returns custom value that was set using writer method even for nil' do
|
||
@status.last_report = nil
|
||
assert_nil @status.last_report
|
||
end
|
||
|
||
test '#out_of_sync? is false if host reporting is disabled' do
|
||
assert @status.out_of_sync?
|
||
|
||
@host.enabled = false
|
||
refute @status.out_of_sync?
|
||
end
|
||
|
||
test '#out_of_sync? is true if reported_at is set and is too long ago' do
|
||
assert @status.reported_at.present?
|
||
window = (Setting[:puppet_interval] + Setting[:outofsync_interval]).minutes
|
||
assert @status.reported_at < Time.now - window
|
||
|
||
assert @status.out_of_sync?
|
||
end
|
||
|
||
test '#out_of_sync? is false when reported_at is unknown' do
|
||
@status.reported_at = nil
|
||
refute @status.out_of_sync?
|
||
end
|
||
|
||
test '#out_of_sync? is false when window is big enough' do
|
||
original, Setting[:outofsync_interval] = Setting[:outofsync_interval], (Time.now - @report.reported_at).to_i / 60 + 1
|
||
refute @status.out_of_sync?
|
||
Setting[:outofsync_interval] = original
|
||
end
|
||
|
||
test '#refresh! refreshes the date and persists the record' do
|
||
@status.expects(:refresh)
|
||
@status.refresh!
|
||
|
||
assert @status.persisted?
|
||
end
|
||
|
||
test '#refresh updates date to reported_at of last report' do
|
||
@status.reported_at = nil
|
||
@status.refresh
|
||
|
||
assert_equal @report.reported_at, @status.reported_at
|
||
end
|
||
|
||
test '.is_not' do
|
||
assert_equal '((host_status.status >> 6 & 63) = 0)', HostStatus::ConfigurationStatus.is_not('restarted')
|
||
end
|
||
|
||
test '.is' do
|
||
assert_equal '((host_status.status >> 6 & 63) != 0)', HostStatus::ConfigurationStatus.is('restarted')
|
||
end
|
||
|
||
test '.bit_mask' do
|
||
assert_equal '0 & 63', HostStatus::ConfigurationStatus.bit_mask('applied')
|
||
assert_equal '6 & 63', HostStatus::ConfigurationStatus.bit_mask('restarted')
|
||
assert_equal '12 & 63', HostStatus::ConfigurationStatus.bit_mask('failed')
|
||
end
|
||
end
|
test/unit/host_status/global_test.rb | ||
---|---|---|
require 'test_helper'
|
||
|
||
class GlobalTest < ActiveSupport::TestCase
|
||
class StatusMock < Struct.new(:global, :relevant)
|
||
alias_method :relevant?, :relevant
|
||
|
||
def to_global(options = {})
|
||
global
|
||
end
|
||
end
|
||
|
||
def setup
|
||
@status1 = StatusMock.new(HostStatus::Global::WARN, true)
|
||
@status2 = StatusMock.new(HostStatus::Global::ERROR, true)
|
||
@status3 = StatusMock.new(HostStatus::Global::OK, true)
|
||
end
|
||
|
||
test '.build(statuses) builds new global status with highest status code' do
|
||
global = HostStatus::Global.build([@status1, @status2, @status3])
|
||
assert_equal HostStatus::Global::ERROR, global.status
|
||
end
|
||
|
||
test '.build(statuses, :last_reports => [reports]) uses reports cache for configuration statuses' do
|
||
status = HostStatus::ConfigurationStatus.new
|
||
report = Report.new(:host_id => 1)
|
||
status.expects(:relevant?).returns(true)
|
||
status.expects(:to_global).returns(:result)
|
||
global = HostStatus::Global.build([ status ], :last_reports => [ report ])
|
||
assert_equal :result, global.status
|
||
end
|
||
|
||
test '.to_label returns string representation of status code' do
|
||
global = HostStatus::Global.new(HostStatus::Global::OK)
|
||
assert_kind_of String, global.to_label
|
||
end
|
||
end
|
test/unit/host_status_test.rb | ||
---|---|---|
require 'test_helper'
|
||
|
||
class HostStatusTest < ActiveSupport::TestCase
|
||
class DummyStatus < HostStatus::Status
|
||
def self.status_name
|
||
N_("DummyStatus")
|
||
end
|
||
end
|
||
|
||
test '.status_registry allows adding new status and recalling it later' do
|
||
status = OpenStruct.new
|
||
HostStatus.status_registry.add(status)
|
||
assert_includes HostStatus.status_registry, status
|
||
HostStatus.status_registry.delete(status)
|
||
refute_includes HostStatus.status_registry, status
|
||
end
|
||
|
||
test '.find_status_by_humanized_name' do
|
||
assert_equal HostStatus::ConfigurationStatus, HostStatus.find_status_by_humanized_name('configuration')
|
||
|
||
HostStatus.status_registry.add(DummyStatus)
|
||
assert_equal DummyStatus, HostStatus.find_status_by_humanized_name('dummy_status')
|
||
HostStatus.status_registry.delete(DummyStatus)
|
||
refute_includes HostStatus.status_registry, DummyStatus
|
||
end
|
||
end
|
test/unit/host_test.rb | ||
---|---|---|
assert_nil host
|
||
end
|
||
|
||
test 'host #refresh_global_status defaults to OK' do
|
||
host = FactoryGirl.build(:host)
|
Also available in: Unified diff
Fixes #10782 - global host status
Introduce new global host status that is composed of host substatuses.Each substatus defines a mapping to the global one which can result in
three values
Plugins can add their own substatuses. These are automatically
propagated also to API.
Thanks to Tomas Strachota who wrote the original code.