Project

General

Profile

« Previous | Next » 

Revision c83e29ac

Added by Lukas Zapletal about 10 years ago

fixes #1966 - improved UI errors for proxy

View differences:

app/assets/javascripts/i18n.js
// Add normal gettext aliases with gettext_i18n_rails_js to enable extraction
// when SETTINGS[:mark_translated] is enabled, wrap all strings
if (typeof(I18N_MARK) != 'undefined' && I18N_MARK) {
window.__ = function() { return 'X' + i18n.gettext.apply(i18n, arguments) + 'X' };
window.n__ = function() { return 'X' + i18n.ngettext.apply(i18n, arguments) + 'X' };
window.__ = function() { return '\u00BB' + i18n.gettext.apply(i18n, arguments) + '\u00AB' };
window.n__ = function() { return '\u00BB' + i18n.ngettext.apply(i18n, arguments) + '\u00AB' };
}
});
app/models/concerns/orchestration.rb
private
def proxy_error e
e.respond_to?(:message) ? e.message : e
end
# Handles the actual queue
# takes care for running the tasks in order
# if any of them fail, it rollbacks all completed tasks
......
rescue Net::LeaseConflict => e
task.status = "failed"
failure _("DHCP has a lease at %s") % e, e.backtrace
rescue RestClient::Exception => e
task.status = "failed"
failure _("%{task} task failed with the following error: %{e}") % { :task => task.name, :e => proxy_error(e) }, e.backtrace
rescue => e
task.status = "failed"
failure _("%{task} task failed with the following error: %{e}") % { :task => task.name, :e => e }, e.backtrace
app/models/concerns/orchestration/dhcp.rb
# if that failed, trying to guess out tftp next server based on the smart proxy hostname
bs ||= URI.parse(subnet.tftp.url).host
# now convert it into an ip address (see http://theforeman.org/issues/show/1381)
return to_ip_address(bs) if bs.present?
ip = to_ip_address(bs) if bs.present?
return ip unless ip.nil?
failure _("Unable to determine the host's boot server. The DHCP smart proxy failed to provide this information and this subnet is not provided with TFTP services.")
rescue => e
app/models/concerns/orchestration/puppetca.rb
def delCertificate
logger.info "Remove puppet certificate for #{name}"
puppetca.del_certificate certname
rescue => e
failure _("Failed to remove %{name}'s puppet certificate: %{e}") % { :name => name, :e => proxy_error(e) }
end
# Empty method for rollbacks - maybe in the future we would support creating the certificates directly
......
def setAutosign
logger.info "Adding autosign entry for #{name}"
puppetca.set_autosign certname
rescue => e
failure _("Failed to add %{name} to autosign file: %{e}") % { :name => name, :e => proxy_error(e) }
end
# Removes the host's name from the autosign.conf file
def delAutosign
logger.info "Delete the autosign entry for #{name}"
puppetca.del_autosign certname
rescue => e
failure _("Failed to remove %{self} from the autosign file: %{e}") % { :self => self, :e => proxy_error(e) }
end
private
app/models/concerns/orchestration/tftp.rb
def setTFTP
logger.info "Add the TFTP configuration for #{name}"
tftp.set mac, :pxeconfig => generate_pxe_template
rescue => e
failure _("Failed to set TFTP: %s") % proxy_error(e)
end
# Removes the host from the forward and reverse TFTP zones
......
def delTFTP
logger.info "Delete the TFTP configuration for #{name}"
tftp.delete mac
rescue => e
failure _("Failed to delete TFTP: %s") % proxy_error(e)
end
def setTFTPBootFiles
......
end
failure _("Failed to fetch boot files") unless valid
valid
rescue => e
failure _("Failed to fetch boot files: %s") % proxy_error(e)
end
#empty method for rollbacks
app/models/host/managed.rb
# otherwise, use normal systems dns settings to resolv
def to_ip_address name_or_ip
return name_or_ip if name_or_ip =~ Net::Validations::IP_REGEXP
return dns_ptr_record.dns_lookup(name_or_ip).ip if dns_ptr_record
if dns_ptr_record
lookup = dns_ptr_record.dns_lookup(name_or_ip)
return lookup.ip unless lookup.nil?
end
# fall back to normal dns resolution
domain.resolver.getaddress(name_or_ip).to_s
rescue => e
logger.warn "Unable to find IP address for '#{name_or_ip}': #{e}"
raise ::Foreman::WrappedException.new(e, N_("Unable to find IP address for '%s'"), name_or_ip)
end
def set_default_user
app/services/foreman/wrapped_exception.rb
module Foreman
class WrappedException < ::Foreman::Exception
def initialize exception, message, *params
def initialize wrapped_exception, message, *params
super(message, *params)
@exception = exception
@wrapped_exception = wrapped_exception
end
def wrapped_exception
@exception
@wrapped_exception
end
def message
if @exception.nil?
if @wrapped_exception.nil?
wrapped = ""
super
else
wrapped = " (#{@exception.class.name} - #{@exception.message})"
cls = @wrapped_exception.class.name
msg = @wrapped_exception.message.try(:truncate, 90)
super + " ([#{cls}]: #{msg})"
end
"#{code}: #{@message}#{wrapped}"
end
end
lib/foreman/exception.rb
@params = params
end
# Error code is made up first 8 characters of base64 (RFC 4648) encoded MD5
# sum of concatenated classname and message
def self.calculate_error_code classname, message
class_hash = Zlib::crc32(classname) % 100
return 'ERF00-0000' if classname.nil? or message.nil?
basename = classname.split(':').last
class_hash = Zlib::crc32(basename) % 100
msg_hash = Zlib::crc32(message) % 10000
sprintf "ERF%02d-%04d", class_hash, msg_hash
end
......
if Kernel.respond_to? :_
translated_msg = _(@message) % @params
else
translated_msg = @message
# use plain ruby interpolation
translated_msg = @message % @params
end
"#{code}: #{translated_msg}"
"#{code} [#{self.class.name}]: #{translated_msg}"
end
alias :to_s :message
def to_s
message
end
end
class FingerprintException < Exception
lib/foreman/gettext/debug.rb
# include this module to see translations in the UI
module Foreman::Gettext::Debug
DL = "\u00BB".encode("UTF-8") rescue '>'
DR = "\u00AB".encode("UTF-8") rescue '<'
# slightly modified copy of fast_gettext D_* method
def _(key)
FastGettext.translation_repositories.each_key do |domain|
result = FastGettext::TranslationMultidomain.d_(domain, key) {nil}
return "X#{result}X" unless result.nil?
return DL + result + DR unless result.nil?
end
'X' + key + 'X'
DL + key + DR
end
# slightly modified copy of fast_gettext D_* method
def n_(*keys)
FastGettext.translation_repositories.each_key do |domain|
result = FastGettext::TranslationMultidomain.dn_(domain, *keys) {nil}
return "X#{result}X" unless result.nil?
return DL + result + DR unless result.nil?
end
'X' + keys[-3].split(keys[-2]||FastGettext::NAMESPACE_SEPARATOR).last + 'X'
DL + keys[-3].split(keys[-2]||FastGettext::NAMESPACE_SEPARATOR).last + DR
end
# slightly modified copy of fast_gettext D_* method
def s_(key, separator=nil)
FastGettext.translation_repositories.each_key do |domain|
result = FastGettext::TranslationMultidomain.ds_(domain, key, separator) {nil}
return "X#{result}X" unless result.nil?
return DL + result + DR unless result.nil?
end
'X' + key.split(separator||FastGettext::NAMESPACE_SEPARATOR).last + 'X'
DL + key.split(separator||FastGettext::NAMESPACE_SEPARATOR).last + DR
end
# slightly modified copy of fast_gettext D_* method
def ns_(*keys)
FastGettext.translation_repositories.each_key do |domain|
result = FastGettext::TranslationMultidomain.dns_(domain, *keys) {nil}
return "X#{result}X" unless result.nil?
return DL + result + DR unless result.nil?
end
'X' + keys[-2].split(FastGettext::NAMESPACE_SEPARATOR).last + 'X'
DL + keys[-2].split(FastGettext::NAMESPACE_SEPARATOR).last + DR
end
end
lib/foreman/gettext/support.rb
else
FastGettext.default_available_locales = Dir.glob(locale_search_path).collect {|f| locale_search_re.match(f)[1] }
end
rescue Exception => e
rescue => e
Rails.logger.warn "Unable to set available locales for domain #{locale_domain}: #{e}"
FastGettext.default_available_locales = ['en']
end
lib/proxy_api/bmc.rb
# gets a list of supported providers
def providers
parse get("providers")
rescue => e
raise ProxyException.new(url, e, N_("Unable to get BMC providers"))
end
# gets a list of supported providers installed on the proxy
def providers_installed
parse get("providers/installed")
rescue => e
raise ProxyException.new(url, e, N_("Unable to get installed BMC providers"))
end
# Perform a boot operation on the bmc device
......
else
raise NoMethodError
end
rescue NoMethodError => e
raise e
rescue => e
raise ProxyException.new(url, e, N_("Unable to perform boot BMC operation"))
end
# Perform a power operation on the bmc device
......
else
raise NoMethodError
end
rescue NoMethodError => e
raise e
rescue => e
raise ProxyException.new(url, e, N_("Unable to perform power BMC operation"))
end
# perform an identify operation on the bmc device
......
else
raise NoMethodError
end
rescue NoMethodError => e
raise e
rescue => e
raise ProxyException.new(url, e, N_("Unable to perform identify BMC operation"))
end
# perform a lan get operation on the bmc device
......
else
raise NoMethodError
end
rescue NoMethodError => e
raise e
rescue => e
raise ProxyException.new(url, e, N_("Unable to perform lan BMC operation"))
end
private
lib/proxy_api/dhcp.rb
# Example [{"network":"192.168.11.0","netmask":"255.255.255.0"},{"network":"192.168.122.0","netmask":"255.255.255.0"}]
def subnets
parse get
rescue => e
raise ProxyException.new(url, e, N_("Unable to retrieve DHCP subnets"))
end
def subnet subnet
parse get(subnet)
rescue => e
raise ProxyException.new(url, e, N_("Unable to retrieve DHCP subnet"))
end
def unused_ip subnet, mac = nil
......
params = ""
end
parse get("#{subnet.network}/unused_ip#{params}")
rescue => e
raise ProxyException.new(url, e, N_("Unable to retrieve unused IP"))
end
# Retrieves a DHCP entry
......
end
rescue RestClient::ResourceNotFound
nil
rescue => e
raise ProxyException.new(url, e, N_("Unable to retrieve DHCP entry for %s"), mac)
end
# Sets a DHCP entry
......
raise "Must define a subnet" if subnet.empty?
raise "Must provide arguments" unless args.is_a?(Hash)
parse(post(args, subnet.to_s))
rescue => e
raise ProxyException.new(url, e, N_("Unable to set DHCP entry"))
end
# Deletes a DHCP entry
......
rescue RestClient::ResourceNotFound
# entry doesn't exists anyway
return true
rescue => e
raise ProxyException.new(url, e, N_("Unable to delete DHCP entry for %s"), mac)
end
end
end
lib/proxy_api/dns.rb
# Returns : Boolean status
def set args
parse post(args, "")
rescue => e
raise ProxyException.new(url, e, N_("Unable to set DNS entry"))
end
# Deletes a DNS entry
......
rescue RestClient::ResourceNotFound
# entry doesn't exists anyway
return true
rescue => e
raise ProxyException.new(url, e, N_("Unable to delete DNS entry"))
end
end
end
lib/proxy_api/features.rb
def features
parse get
rescue => e
raise ProxyException.new(url, e, N_("Unable to detect features"))
end
end
end
lib/proxy_api/proxy_exception.rb
module ProxyAPI
class ProxyException < ::Foreman::WrappedException
attr_reader :url
def initialize url, exception, message, *params
super(exception, message, *params)
@url = url
end
def message
super + ' ' + _('for proxy') + ' ' + url
end
end
end
lib/proxy_api/puppet.rb
def environments
parse(get "environments")
rescue => e
raise ProxyException.new(url, e, N_("Unable to get environments from Puppet"))
end
def environment env
parse(get "environments/#{env}")
rescue => e
raise ProxyException.new(url, e, N_("Unable to get environment from Puppet"))
end
def classes env
......
Hash[pcs.map { |k| [k.keys.first, Foreman::ImporterPuppetclass.new(k.values.first)] }]
rescue RestClient::ResourceNotFound
[]
rescue => e
raise ProxyException.new(url, e, N_("Unable to get classes from Puppet for %s"), env)
end
def run hosts
parse(post({:nodes => hosts}, "run"))
rescue => e
raise ProxyException.new(url, e, N_("Unable to execute Puppet run"))
end
end
end
lib/proxy_api/puppetca.rb
def autosign
parse(get "autosign")
rescue => e
raise ProxyException.new(url, e, N_("Unable to get PuppetCA autosign"))
end
def set_autosign certname
parse(post("", "autosign/#{certname}"))
rescue => e
raise ProxyException.new(url, e, N_("Unable to set PuppetCA autosign for %s"), certname)
end
def del_autosign certname
......
rescue RestClient::ResourceNotFound
# entry doesn't exists anyway
true
rescue => e
raise ProxyException.new(url, e, N_("Unable to delete PuppetCA autosign for %s"), certname)
end
def sign_certificate certname
parse(post("", certname))
rescue => e
raise ProxyException.new(url, e, N_("Unable to sign PuppetCA certificate for %s"), certname)
end
def del_certificate certname
......
rescue RestClient::ResourceNotFound
# entry doesn't exists anyway
true
rescue => e
raise ProxyException.new(url, e, N_("Unable to delete PuppetCA certificate for %s"), certname)
end
def all
parse(get)
rescue => e
raise ProxyException.new(url, e, N_("Unable to get PuppetCA certificates"))
end
end
end
lib/proxy_api/tftp.rb
# Returns : Boolean status
def set mac, args
parse(post(args, "#{@variant}/#{mac}"))
rescue => e
raise ProxyException.new(url, e, N_("Unable to set TFTP boot entry for %s"), mac)
end
# Deletes a TFTP boot entry
......
# Returns : Boolean status
def delete mac
parse(super("#{@variant}/#{mac}"))
rescue => e
raise ProxyException.new(url, e, N_("Unable to delete TFTP boot entry for %s"), mac)
end
# Requests that the proxy download the bootfile from the media's source
......
# Returns : Boolean status
def fetch_boot_file args
parse(post(args, "fetch_boot_file"))
rescue => e
raise ProxyException.new(url, e, N_("Unable to fetch TFTP boot file"))
end
# returns the TFTP boot server for this proxy
......
false
rescue RestClient::ResourceNotFound
nil
rescue => e
raise ProxyException.new(url, e, N_("Unable to detect TFTP boot server"))
end
# Create a default pxe menu
......
# Returns : Boolean status
def create_default args
parse(post(args, "create_default"))
rescue => e
raise ProxyException.new(url, e, N_("Unable to create default TFTP boot menu"))
end
end
lib/tasks/exception.rake
task :codes => :environment do
wiki = defined? ENV['WIKI']
exceptions = [
::Foreman::Exception,
::Foreman::WrappedException,
'Foreman::Exception',
'WrappedException',
'ProxyException',
]
result = {}
regexp = /raise.*:?:?(#{exceptions.join('|')})(\.new)?\(?N_\(?(["'])([^\3]+?)\3\)?\)?/
regexp = /raise.*(#{exceptions.join('|')})(\.new)?.*N_\(?(["'])([^\3]+?)\3\)?\)?/
Dir['app/**/*rb', 'lib/**/*rb'].each do |path|
File.open(path) do |f|
f.grep( /#{regexp}/ ) do |line|
......
result.keys.sort.each do |k|
v = result[k]
puts "#{k} #{v}"
puts " * [[#{k}]] - #{v}"
end
end
test/functional/api/v2/hosts_controller_test.rb
facts = fact_json['facts']
post :facts, {:name => hostname, :facts => facts, :type => "Host::Invalid"}, set_session_user
assert_response :unprocessable_entity
assert_equal JSON.parse(response.body)['message'], 'ERF51-2640: A problem occurred when detecting host type: uninitialized constant Host::Invalid'
assert JSON.parse(response.body)['message'] =~ /ERF42-2640/
end
test "when the imported host failed to save, :unprocessable_entity is returned" do

Also available in: Unified diff