foreman/app/models/host.rb @ 771129f2
b4b14336 | Ohad Levy | class Host < Puppet::Rails::Host
|
|
9fd7478e | Paul Kelly | include Authorization
|
|
9390e9cf | Ohad Levy | include ReportCommon
|
|
6e50fa1d | Ohad Levy | belongs_to :model
|
|
8a65dff7 | Ohad Levy | has_many :host_classes, :dependent => :destroy
|
|
has_many :puppetclasses, :through => :host_classes
|
|||
086ec942 | Ohad Levy | belongs_to :hostgroup
|
|
87c40d2e | Ohad Levy | has_many :reports, :dependent => :destroy
|
|
aa1796f3 | Paul Kelly | has_many :host_parameters, :dependent => :destroy, :foreign_key => :reference_id
|
|
b09b4515 | Ohad Levy | accepts_nested_attributes_for :host_parameters, :reject_if => lambda { |a| a[:value].blank? }, :allow_destroy => true
|
|
9c0e127b | Paul Kelly | belongs_to :owner, :polymorphic => true
|
|
a6f4f5f7 | Ohad Levy | belongs_to :sp_subnet
|
|
816b9c22 | Ohad Levy | ||
7747485a | Ohad Levy | include Hostext::Search
|
|
e5d3f34e | Ohad Levy | include HostCommon
|
|
32847027 | Justin Sherrill | class Jail < Safemode::Jail
|
|
68f7a705 | Ohad Levy | allow :name, :diskLayout, :puppetmaster, :operatingsystem, :os, :environment, :ptable, :hostgroup, :url_for_boot,
|
|
9390e9cf | Ohad Levy | :params, :hostgroup, :domain, :ip, :mac
|
|
32847027 | Justin Sherrill | end
|
|
8ea536e3 | Ohad Levy | attr_reader :cached_host_params, :cached_lookup_keys_params
|
|
b063a9bc | Paul Kelly | ||
76607ed5 | Ohad Levy | named_scope :recent, lambda { |*args| {:conditions => ["last_report > ?", (args.first || (Setting[:puppet_interval] + 5).minutes.ago)]} }
|
|
named_scope :out_of_sync, lambda { |*args| {:conditions => ["last_report < ? and enabled != ?", (args.first || (Setting[:puppet_interval] + 5).minutes.ago), false]} }
|
|||
06160506 | Ohad Levy | ||
named_scope :with_fact, lambda { |fact,value|
|
|||
unless fact.nil? or value.nil?
|
|||
51b0abad | Ohad Levy | { :joins => "INNER JOIN fact_values fv_#{fact} ON fv_#{fact}.host_id = hosts.id
|
|
INNER JOIN fact_names fn_#{fact} ON fn_#{fact}.id = fv_#{fact}.fact_name_id",
|
|||
9390e9cf | Ohad Levy | :select => "DISTINCT hosts.name, hosts.id", :conditions =>
|
|
["fv_#{fact}.value = ? and fn_#{fact}.name = ? and fv_#{fact}.fact_name_id = fn_#{fact}.id",value, fact ] }
|
|||
06160506 | Ohad Levy | else
|
|
raise "invalid fact"
|
|||
end
|
|||
}
|
|||
named_scope :with_class, lambda { |klass|
|
|||
unless klass.nil?
|
|||
{ :joins => :puppetclasses, :select => "hosts.name", :conditions => {:puppetclasses => {:name => klass }} }
|
|||
else
|
|||
9a9b3e75 | Paul Kelly | raise "invalid class"
|
|
06160506 | Ohad Levy | end
|
|
}
|
|||
16cb7742 | Ohad Levy | named_scope :with_error, { :conditions => "(puppet_status > 0) and
|
|
9390e9cf | Ohad Levy | ((puppet_status >> #{BIT_NUM*METRIC.index("failed")} & #{MAX}) != 0) or
|
|
((puppet_status >> #{BIT_NUM*METRIC.index("failed_restarts")} & #{MAX}) != 0)"
|
|||
16cb7742 | Ohad Levy | }
|
|
4173bd27 | Ohad Levy | named_scope :without_error, { :conditions =>
|
|
"((puppet_status >> #{BIT_NUM*METRIC.index("failed")} & #{MAX}) = 0) and
|
|||
((puppet_status >> #{BIT_NUM*METRIC.index("failed_restarts")} & #{MAX}) = 0)"
|
|||
}
|
|||
16cb7742 | Ohad Levy | named_scope :with_changes, { :conditions => "(puppet_status > 0) and
|
|
9390e9cf | Ohad Levy | ((puppet_status >> #{BIT_NUM*METRIC.index("applied")} & #{MAX}) != 0) or
|
|
4173bd27 | Ohad Levy | ((puppet_status >> #{BIT_NUM*METRIC.index("restarted")} & #{MAX}) != 0)"
|
|
16cb7742 | Ohad Levy | }
|
|
4173bd27 | Ohad Levy | named_scope :without_changes, { :conditions =>
|
|
"((puppet_status >> #{BIT_NUM*METRIC.index("applied")} & #{MAX}) = 0) or
|
|||
((puppet_status >> #{BIT_NUM*METRIC.index("restarted")} & #{MAX}) = 0)"
|
|||
}
|
|||
named_scope :successful, lambda { without_changes.without_error.scope(:find)}
|
|||
5aee7268 | Eric Shamow | named_scope :alerts_disabled, {:conditions => ["enabled = ?", false] }
|
|
16cb7742 | Ohad Levy | ||
9fd7478e | Paul Kelly | named_scope :my_hosts, lambda {
|
|
user = User.current
|
|||
owner_conditions = sanitize_sql_for_conditions(["((hosts.owner_id in (?) AND hosts.owner_type = 'Usergroup') OR (hosts.owner_id = ? AND hosts.owner_type = 'User'))", user.my_usergroups.map(&:id), user.id])
|
|||
domain_conditions = sanitize_sql_for_conditions([" (hosts.domain_id in (?))",dms = (user.domains).map(&:id)])
|
|||
hostgroup_conditions = sanitize_sql_for_conditions([" (hosts.hostgroup_id in (?))",(hgs = user.hostgroups).map(&:id)])
|
|||
fact_conditions = ""
|
|||
for user_fact in (ufs = user.user_facts)
|
|||
fact_conditions += sanitize_sql_for_conditions ["(hosts.id = fact_values.host_id and fact_values.fact_name_id = ? and fact_values.value #{user_fact.operator} ?)", user_fact.fact_name_id, user_fact.criteria]
|
|||
fact_conditions = user_fact.andor == "and" ? "(#{fact_conditions}) and " : "#{fact_conditions} or "
|
|||
end
|
|||
if match = fact_conditions.match(/^(.*).....$/)
|
|||
fact_conditions = "(#{match[1]})"
|
|||
end
|
|||
conditions = ""
|
|||
if user.filtering?
|
|||
conditions = "#{owner_conditions}" if user.filter_on_owner
|
|||
(conditions = (user.domains_andor == "and") ? "(#{conditions}) and #{domain_conditions} " : "#{conditions} or #{domain_conditions} ") unless dms.empty?
|
|||
(conditions = (user.hostgroups_andor == "and") ? "(#{conditions}) and #{hostgroup_conditions} " : "#{conditions} or #{hostgroup_conditions} ") unless hgs.empty?
|
|||
(conditions = (user.facts_andor == "and") ? "(#{conditions}) and #{fact_conditions} " : "#{conditions} or #{fact_conditions} ") unless ufs.empty?
|
|||
conditions.sub!(/\s*\(\)\s*/, "")
|
|||
conditions.sub!(/^(?:\(\))?\s?(?:and|or)\s*/, "")
|
|||
conditions.sub!(/\(\s*(?:or|and)\s*\(/, "((")
|
|||
end
|
|||
{:conditions => conditions}
|
|||
}
|
|||
3e0f05a7 | Ohad Levy | named_scope :completer_scope, lambda { my_hosts.scope(:find) }
|
|
aa5a2230 | Ohad Levy | # audit the changes to this model
|
|
acts_as_audited :except => [:last_report, :puppet_status, :last_compile]
|
|||
c22d6db2 | Ohad Levy | # some shortcuts
|
|
alias_attribute :os, :operatingsystem
|
|||
alias_attribute :arch, :architecture
|
|||
dd2b33da | Ohad Levy | alias_attribute :hostname, :name
|
|
9afa092e | Ohad Levy | alias_attribute :fqdn, :name
|
|
d3d91384 | Ohad Levy | ||
50b094c0 | Ohad Levy | validates_uniqueness_of :name
|
|
69923df9 | Ohad Levy | validates_presence_of :name, :environment_id
|
|
d5707b63 | Ohad Levy | if SETTINGS[:unattended]
|
|
90b83222 | Ohad Levy | # handles all orchestration of smart proxies.
|
|
9cd25bde | Ohad Levy | include Foreman::Renderer
|
|
90b83222 | Ohad Levy | include Orchestration
|
|
68f7a705 | Ohad Levy | include HostTemplateHelpers
|
|
90b83222 | Ohad Levy | ||
e26c3e9b | Ohad Levy | validates_uniqueness_of :ip, :if => Proc.new {|host| host.managed}
|
|
validates_uniqueness_of :mac, :unless => Proc.new { |host| host.hypervisor? or !host.managed }
|
|||
69923df9 | Ohad Levy | validates_uniqueness_of :sp_mac, :allow_nil => true, :allow_blank => true
|
|
validates_uniqueness_of :sp_name, :sp_ip, :allow_blank => true, :allow_nil => true
|
|||
validates_format_of :sp_name, :with => /.*-sp/, :allow_nil => true, :allow_blank => true
|
|||
c9fff7c1 | Paul Kelly | validates_presence_of :architecture_id, :operatingsystem_id, :if => Proc.new {|host| host.managed}
|
|
96be8845 | Ohad Levy | validates_presence_of :domain_id
|
|
e26c3e9b | Ohad Levy | validates_presence_of :mac, :unless => Proc.new { |host| host.hypervisor? or !host.managed }
|
|
69923df9 | Ohad Levy | validates_length_of :root_pass, :minimum => 8,:too_short => 'should be 8 characters or more'
|
|
ec777026 | Ohad Levy | validates_format_of :mac, :with => Net::Validations::MAC_REGEXP, :unless => Proc.new { |host| host.hypervisor_id or !host.managed }
|
|
validates_format_of :ip, :with => Net::Validations::IP_REGEXP, :if => Proc.new {|host| host.managed}
|
|||
19bd108a | Paul Kelly | validates_presence_of :ptable, :message => "cant be blank unless a custom partition has been defined",
|
|
c9fff7c1 | Paul Kelly | :if => Proc.new { |host| host.managed and host.disk.empty? and not defined?(Rake) }
|
|
ec777026 | Ohad Levy | validates_format_of :sp_mac, :with => Net::Validations::MAC_REGEXP, :allow_nil => true, :allow_blank => true
|
|
validates_format_of :sp_ip, :with => Net::Validations::IP_REGEXP, :allow_nil => true, :allow_blank => true
|
|||
69923df9 | Ohad Levy | validates_format_of :serial, :with => /[01],\d{3,}n\d/, :message => "should follow this format: 0,9600n8", :allow_blank => true, :allow_nil => true
|
|
end
|
|||
d3d91384 | Ohad Levy | ||
f6cbdcd1 | Ohad Levy | before_validation :set_hostgroup_defaults, :set_default_user, :normalize_addresses, :normalize_hostname
|
|
f1ff5404 | Ohad Levy | after_validation :ensure_assoications
|
|
d3d91384 | Ohad Levy | ||
68589d47 | Ohad Levy | def set_default_user
|
|
91e8f35b | Paul Kelly | self.owner ||= User.current
|
|
end
|
|||
86744a6b | Ohad Levy | def to_param
|
|
name
|
|||
end
|
|||
9c0e127b | Paul Kelly | def <=>(other)
|
|
self.name <=> other.name
|
|||
end
|
|||
ea4fd101 | Ohad Levy | def shortname
|
|
59ff4da6 | Ohad Levy | domain.nil? ? name : name.chomp("." + domain.name)
|
|
d3d91384 | Ohad Levy | end
|
|
9c0e127b | Paul Kelly | # method to return the correct owner list for host edit owner select dropbox
|
|
def is_owned_by
|
|||
owner.id_and_type if owner
|
|||
end
|
|||
# virtual attributes which sets the owner based on the user selection
|
|||
# supports a simple user, or a usergroup
|
|||
# selection parameter is expected to be an ActiveRecord id_and_type method (see Foreman's AR extentions).
|
|||
def is_owned_by=(selection)
|
|||
9fd7478e | Paul Kelly | oid = User.find(selection.to_i) if selection =~ (/-Users$/)
|
|
oid = Usergroup.find(selection.to_i) if selection =~ (/-Usergroups$/)
|
|||
9c0e127b | Paul Kelly | self.owner = oid
|
|
end
|
|||
b4b14336 | Ohad Levy | def clearReports
|
|
d3d91384 | Ohad Levy | # Remove any reports that may be held against this host
|
|
2dcf4736 | Ohad Levy | Report.delete_all("host_id = #{id}")
|
|
d3d91384 | Ohad Levy | end
|
|
def clearFacts
|
|||
2dcf4736 | Ohad Levy | FactValue.delete_all("host_id = #{id}")
|
|
d3d91384 | Ohad Levy | end
|
|
# Called from the host build post install process to indicate that the base build has completed
|
|||
# Build is cleared and the boot link and autosign entries are removed
|
|||
# A site specific build script is called at this stage that can do site specific tasks
|
|||
7700f32f | Frank Sweetser | def built(installed = true)
|
|
d3d91384 | Ohad Levy | self.build = false
|
|
7700f32f | Frank Sweetser | self.installed_at = Time.now.utc if installed
|
|
36f93e4d | Ohad Levy | # If this save fails then an exception is raised and further actions are not processed
|
|
unless Rails.env == "test"
|
|||
# Disallow any auto signing for our host.
|
|||
395eb615 | Ohad Levy | GW::Puppetca.disable name unless puppetca? or not Setting[:manage_puppetca]
|
|
36f93e4d | Ohad Levy | GW::Tftp.remove mac unless respond_to?(:tftp?) and tftp?
|
|
end
|
|||
b34e9aea | Ohad Levy | self.save
|
|
36f93e4d | Ohad Levy | rescue => e
|
|
logger.warn "Failed to set Build on #{self}: #{e}"
|
|||
false
|
|||
d3d91384 | Ohad Levy | end
|
|
b0d3f4ee | Ohad Levy | #retuns fqdn of host puppetmaster
|
|
def pm_fqdn
|
|||
3fe6982a | Ohad Levy | puppetmaster == "puppet" ? "puppet.#{domain.name}" : "#{puppetmaster}"
|
|
36f93e4d | Ohad Levy | end
|
|
# Cleans Certificate and enable Autosign
|
|||
# Called after a host is given their provisioning template
|
|||
# Returns : Boolean status of the operation
|
|||
def handle_ca
|
|||
return true if Rails.env == "test"
|
|||
42c9a0a1 | Ohad Levy | return true unless Setting[:manage_puppetca]
|
|
36f93e4d | Ohad Levy | if puppetca?
|
|
9b1fd486 | Paul Kelly | respond_to?(:initialize_puppetca) && initialize_puppetca && delCertificate && setAutosign
|
|
36f93e4d | Ohad Levy | else
|
|
# Legacy CA handling
|
|||
GW::Puppetca.clean(name) && GW::Puppetca.sign(name)
|
|||
end
|
|||
b0d3f4ee | Ohad Levy | end
|
|
286a2207 | Ohad Levy | # returns the host correct disk layout, custom or common
|
|
def diskLayout
|
|||
9cd25bde | Ohad Levy | pxe_render((disk.empty? ? ptable.layout : disk).gsub("\r",""))
|
|
286a2207 | Ohad Levy | end
|
|
cab0d8c6 | Ohad Levy | # returns a configuration template (such as kickstart) to a given host
|
|
69f9cb82 | Ohad Levy | def configTemplate opts = {}
|
|
opts[:kind] ||= "provision"
|
|||
opts[:operatingsystem_id] ||= operatingsystem_id
|
|||
opts[:hostgroup_id] ||= hostgroup_id
|
|||
opts[:environment_id] ||= environment_id
|
|||
ConfigTemplate.find_template opts
|
|||
cab0d8c6 | Ohad Levy | end
|
|
87c40d2e | Ohad Levy | # reports methods
|
|
def error_count
|
|||
2dcf4736 | Ohad Levy | %w[failed failed_restarts].sum {|f| status f}
|
|
87c40d2e | Ohad Levy | end
|
|
def no_report
|
|||
76607ed5 | Ohad Levy | last_report.nil? or last_report < Time.now - (Setting[:puppet_interval] + 3).minutes and enabled?
|
|
87c40d2e | Ohad Levy | end
|
|
5aee7268 | Eric Shamow | def disabled?
|
|
not enabled?
|
|||
end
|
|||
1ba05a93 | Ohad Levy | # returns the list of puppetclasses a host is in.
|
|
c6eee281 | Ohad Levy | def puppetclasses_names
|
|
9fd7478e | Paul Kelly | return all_puppetclasses.collect {|c| c.name}
|
|
c6eee281 | Ohad Levy | end
|
|
b09b4515 | Ohad Levy | def all_puppetclasses
|
|
4d4b84f6 | Ohad Levy | return hostgroup.nil? ? puppetclasses : (hostgroup.classes + puppetclasses).uniq
|
|
b09b4515 | Ohad Levy | end
|
|
fce2cbc0 | Ohad Levy | ||
e22af92d | Ohad Levy | # provide information about each node, mainly used for puppet external nodes
|
|
# TODO: remove hard coded default parameters into some selectable values in the database.
|
|||
c6eee281 | Ohad Levy | def info
|
|
fce2cbc0 | Ohad Levy | # Static parameters
|
|
c6eee281 | Ohad Levy | param = {}
|
|
c7643fc9 | Ohad Levy | # maybe these should be moved to the common parameters, leaving them in for now
|
|
dbb7c2f2 | Paul Kelly | param["puppetmaster"] = puppetmaster.to_s
|
|
param["domainname"] = domain.fullname unless domain.nil? or domain.fullname.nil?
|
|||
052dc65f | Ohad Levy | param["hostgroup"] = hostgroup.to_label unless hostgroup.nil?
|
|
e20f5716 | Ohad Levy | param["root_pw"] = root_pass if SETTINGS[:unattended]
|
|
771129f2 | Ohad Levy | param["comment"] = comment unless comment.blank?
|
|
e20f5716 | Ohad Levy | param["foreman_env"] = environment.to_s unless environment.nil? or environment.name.nil?
|
|
if SETTINGS[:login]
|
|||
param["owner_name"] = owner.name
|
|||
param["owner_email"] = owner.is_a?(User) ? owner.mail : owner.users.map(&:mail)
|
|||
end
|
|||
76607ed5 | Ohad Levy | if Setting[:ignore_puppet_facts_for_provisioning]
|
|
d97375e0 | Ohad Levy | param["ip"] = ip
|
|
param["mac"] = mac
|
|||
end
|
|||
fce2cbc0 | Ohad Levy | param.update self.params
|
|
a6e10c72 | Jochen Schalanda | ||
info_hash = {}
|
|||
info_hash['classes'] = self.puppetclasses_names
|
|||
info_hash['parameters'] = param
|
|||
e20f5716 | Ohad Levy | info_hash['environment'] = param["foreman_env"]
|
|
a6e10c72 | Jochen Schalanda | ||
return info_hash
|
|||
c6eee281 | Ohad Levy | end
|
|
0ce8fa05 | Ohad Levy | def params
|
|
8ea536e3 | Ohad Levy | host_params.update(lookup_keys_params)
|
|
end
|
|||
d78436d6 | Ohad Levy | def clear_host_parameters_cache!
|
|
@cached_host_params = nil
|
|||
end
|
|||
b063a9bc | Paul Kelly | ||
8ea536e3 | Ohad Levy | def host_params
|
|
return cached_host_params unless cached_host_params.blank?
|
|||
hp = {}
|
|||
c7643fc9 | Ohad Levy | # read common parameters
|
|
8ea536e3 | Ohad Levy | CommonParameter.all.each {|p| hp.update Hash[p.name => p.value] }
|
|
c7643fc9 | Ohad Levy | # read domain parameters
|
|
8ea536e3 | Ohad Levy | domain.domain_parameters.each {|p| hp.update Hash[p.name => p.value] } unless domain.nil?
|
|
11782648 | Ohad Levy | # read OS parameters
|
|
8ea536e3 | Ohad Levy | operatingsystem.os_parameters.each {|p| hp.update Hash[p.name => p.value] } unless operatingsystem.nil?
|
|
c7643fc9 | Ohad Levy | # read group parameters only if a host belongs to a group
|
|
8ea536e3 | Ohad Levy | hp.update hostgroup.parameters unless hostgroup.nil?
|
|
086ec942 | Ohad Levy | # and now read host parameters, override if required
|
|
8ea536e3 | Ohad Levy | host_parameters.each {|p| hp.update Hash[p.name => p.value] }
|
|
@cached_host_params = hp
|
|||
end
|
|||
b063a9bc | Paul Kelly | ||
8ea536e3 | Ohad Levy | def lookup_keys_params
|
|
return cached_lookup_keys_params unless cached_lookup_keys_params.blank?
|
|||
p = {}
|
|||
b063a9bc | Paul Kelly | # lookup keys
|
|
if Setting["Enable_Smart_Variables_in_ENC"]
|
|||
klasses = puppetclasses.map(&:id)
|
|||
klasses += hostgroup.classes.map(&:id) if hostgroup
|
|||
LookupKey.all(:conditions => {:puppetclass_id =>klasses.flatten } ).each do |k|
|
|||
8ea536e3 | Ohad Levy | p[k.to_s] = k.value_for(self)
|
|
b063a9bc | Paul Kelly | end unless klasses.empty?
|
|
end
|
|||
8ea536e3 | Ohad Levy | @cached_lookup_keys_params = p
|
|
0ce8fa05 | Ohad Levy | end
|
|
363141af | Ohad Levy | def self.importHostAndFacts yaml
|
|
2dcf4736 | Ohad Levy | facts = YAML::load yaml
|
|
895a7680 | Ohad Levy | return false unless facts.is_a?(Puppet::Node::Facts)
|
|
2dcf4736 | Ohad Levy | ||
895a7680 | Ohad Levy | h=find_or_create_by_name(facts.name)
|
|
h.save(false) if h.new_record?
|
|||
h.importFacts(facts)
|
|||
363141af | Ohad Levy | end
|
|
# import host facts, required when running without storeconfigs.
|
|||
# expect a Puppet::Node::Facts
|
|||
def importFacts facts
|
|||
raise "invalid Fact" unless facts.is_a?(Puppet::Node::Facts)
|
|||
# we are not importing facts for hosts in build state (e.g. waiting for a re-installation)
|
|||
raise "Host is pending for Build" if build
|
|||
a7db5993 | Ohad Levy | time = facts.values[:_timestamp]
|
|
time = time.to_time if time.is_a?(String)
|
|||
895a7680 | Ohad Levy | ||
# we are not doing anything we already processed this fact (or a newer one)
|
|||
return true unless last_compile.nil? or (last_compile + 1.minute < time)
|
|||
self.last_compile = time
|
|||
# save all other facts - pre 0.25 it was called setfacts
|
|||
49f31762 | Ohad Levy | respond_to?("merge_facts") ? self.merge_facts(facts.values) : self.setfacts(facts.values)
|
|
save(false)
|
|||
895a7680 | Ohad Levy | ||
# we want to import other information only if this host was never installed via Foreman
|
|||
populateFieldsFromFacts if installed_at.nil?
|
|||
# we are saving here with no validations, as we want this process to be as fast
|
|||
# as possible, assuming we already have all the right settings in Foreman.
|
|||
# If we don't (e.g. we never install the server via Foreman, we populate the fields from facts
|
|||
# TODO: if it was installed by Foreman and there is a mismatch,
|
|||
# we should probably send out an alert.
|
|||
return self.save(false)
|
|||
rescue Exception => e
|
|||
logger.warn "Failed to save #{facts.name}: #{e}"
|
|||
e22af92d | Ohad Levy | end
|
|
ea4fd101 | Ohad Levy | def fv name
|
|
2dcf4736 | Ohad Levy | v=fact_values.first(:select => "fact_values.value", :joins => :fact_name,
|
|
9fd7478e | Paul Kelly | :conditions => "fact_names.name = '#{name}'")
|
|
749973b4 | Ohad Levy | v.value unless v.nil?
|
|
ea4fd101 | Ohad Levy | end
|
|
e22af92d | Ohad Levy | def populateFieldsFromFacts
|
|
76607ed5 | Ohad Levy | unless Setting[:ignore_puppet_facts_for_provisioning]
|
|
d568c4cc | Ohad Levy | self.mac = fv(:macaddress).downcase unless fv(:macaddress).blank?
|
|
d97375e0 | Ohad Levy | self.ip = fv(:ipaddress) if ip.nil?
|
|
end
|
|||
895a7680 | Ohad Levy | self.domain = Domain.find_or_create_by_name fv(:domain) unless fv(:domain).empty?
|
|
e22af92d | Ohad Levy | # On solaris architecture fact is harwareisa
|
|
363141af | Ohad Levy | if myarch=fv(:architecture) || fv(:hardwareisa)
|
|
895a7680 | Ohad Levy | self.arch=Architecture.find_or_create_by_name myarch unless myarch.empty?
|
|
363141af | Ohad Levy | end
|
|
d3fbfd7e | Paul Kelly | ||
# by default, puppet doesn't store an env name in the database
|
|||
env = fv(:environment) || Setting[:default_puppet_environment]
|
|||
if Setting[:update_environment_from_facts]
|
|||
self.environment = Environment.find_or_create_by_name env
|
|||
else
|
|||
self.environment ||= Environment.find_or_create_by_name env
|
|||
end
|
|||
e22af92d | Ohad Levy | ||
ea4fd101 | Ohad Levy | os_name = fv(:operatingsystem)
|
|
363141af | Ohad Levy | if orel = fv(:lsbdistrelease) || fv(:operatingsystemrelease)
|
|
major, minor = orel.split(".")
|
|||
51252890 | Frank Sweetser | minor ||= ""
|
|
363141af | Ohad Levy | self.os = Operatingsystem.find_or_create_by_name_and_major_and_minor os_name, major, minor
|
|
end
|
|||
2dcf4736 | Ohad Levy | ||
672f931d | Paul Kelly | unless self.model
|
|
modelname = fv(:productname) || fv(:model) || (fv(:is_virtual) == "true" ? fv(:virtual) : nil)
|
|||
self.model = Model.find_or_create_by_name(modelname.strip) unless modelname.empty?
|
|||
end
|
|||
8ac3ea75 | Ohad Levy | ||
363141af | Ohad Levy | # again we are saving without validations as input is required (e.g. partition tables)
|
|
2dcf4736 | Ohad Levy | self.save(false)
|
|
e22af92d | Ohad Levy | end
|
|
8613dec9 | Ohad Levy | # Called by build link in the list
|
|
# Build is set
|
|||
# The boot link and autosign entry are created
|
|||
# Any existing puppet certificates are deleted
|
|||
# Any facts are discarded
|
|||
def setBuild
|
|||
5617cefe | Ohad Levy | clearFacts
|
|
clearReports
|
|||
5942416f | Ohad Levy | ||
# ensures that the legacy TFTP code is not called when using a smart proxy.
|
|||
unless respond_to?(:tftp?) and tftp?
|
|||
return false unless GW::Tftp.create([mac, os.to_s.gsub(" ","-"), arch.name, serial])
|
|||
end
|
|||
5617cefe | Ohad Levy | self.build = true
|
|
self.save
|
|||
90b83222 | Ohad Levy | errors.empty?
|
|
8613dec9 | Ohad Levy | end
|
|
816465f2 | Ohad Levy | # this method accepts a puppets external node yaml output and generate a node in our setup
|
|
# it is assumed that you already have the node (e.g. imported by one of the rack tasks)
|
|||
def importNode nodeinfo
|
|||
727312c3 | Ohad Levy | myklasses= []
|
|
816465f2 | Ohad Levy | # puppet classes
|
|
nodeinfo["classes"].each do |klass|
|
|||
if pc = Puppetclass.find_by_name(klass)
|
|||
727312c3 | Ohad Levy | myklasses << pc
|
|
816465f2 | Ohad Levy | else
|
|
0da5bcf1 | Ohad Levy | error = "Failed to import #{klass} for #{name}: doesn't exists in our database - ignoring"
|
|
722ba6f1 | Ohad Levy | logger.warn error
|
|
cf2f7656 | Ohad Levy | $stdout.puts error
|
|
816465f2 | Ohad Levy | end
|
|
727312c3 | Ohad Levy | self.puppetclasses = myklasses
|
|
816465f2 | Ohad Levy | end
|
|
# parameters are a bit more tricky, as some classifiers provide the facts as parameters as well
|
|||
# not sure what is puppet priority about it, but we ignore it if has a fact with the same name.
|
|||
# additionally, we don't import any non strings values, as puppet don't know what to do with those as well.
|
|||
myparams = self.info["parameters"]
|
|||
nodeinfo["parameters"].each_pair do |param,value|
|
|||
next if fact_names.exists? :name => param
|
|||
next unless value.is_a?(String)
|
|||
# we already have this parameter
|
|||
next if myparams.has_key?(param) and myparams[param] == value
|
|||
unless (hp = self.host_parameters.create(:name => param, :value => value))
|
|||
logger.warn "Failed to import #{param}/#{value} for #{name}: #{hp.errors.full_messages.join(", ")}"
|
|||
cf2f7656 | Ohad Levy | $stdout.puts $!
|
|
816465f2 | Ohad Levy | end
|
|
end
|
|||
self.save
|
|||
end
|
|||
300c8b44 | Ohad Levy | # counts each association of a given host
|
|
# e.g. how many hosts belongs to each os
|
|||
# returns sorted hash
|
|||
def self.count_distribution assocication
|
|||
output = {}
|
|||
2c7da330 | Ohad Levy | count(:group => assocication).each do |k,v|
|
|
begin
|
|||
output[k.to_label] = v unless v == 0
|
|||
rescue
|
|||
logger.info "skipped #{k} as it has has no label"
|
|||
end
|
|||
end
|
|||
300c8b44 | Ohad Levy | output
|
|
end
|
|||
# counts each association of a given host for HABTM relationships
|
|||
# TODO: Merge these two into one method
|
|||
# e.g. how many hosts belongs to each os
|
|||
# returns sorted hash
|
|||
def self.count_habtm assocication
|
|||
output = {}
|
|||
Host.count(:include => assocication.pluralize, :group => "#{assocication}_id").to_a.each do |a|
|
|||
#Ugly Ugly Ugly - I guess I'm missing something basic here
|
|||
label = eval(assocication.camelize).send("find",a[0].to_i).to_label if a[0]
|
|||
output[label] = a[1]
|
|||
end
|
|||
output
|
|||
end
|
|||
9390e9cf | Ohad Levy | def resources_chart(timerange = 1.day.ago)
|
|
c6f1b718 | Ohad Levy | data = {}
|
|
3eecf845 | Amos Benari | data[:applied], data[:failed], data[:restarted], data[:failed_restarts], data[:skipped] = [],[],[],[],[]
|
|
e6c75845 | Ohad Levy | reports.recent(timerange).each do |r|
|
|
3eecf845 | Amos Benari | data[:applied] << "[ #{r.reported_at.to_i}000, #{r.applied} ]"
|
|
data[:failed] << "[ #{r.reported_at.to_i}000, #{r.failed} ]"
|
|||
data[:restarted] << "[ #{r.reported_at.to_i}000, #{r.restarted} ]"
|
|||
data[:failed_restarts] << "[ #{r.reported_at.to_i}000, #{r.failed_restarts} ]"
|
|||
data[:skipped] << "[ #{r.reported_at.to_i}000, #{r.skipped} ]"
|
|||
end
|
|||
return data
|
|||
end
|
|||
def runtime_chart(timerange = 1.day.ago)
|
|||
data = {}
|
|||
data[:config], data[:runtime] = [], []
|
|||
reports.recent(timerange).each do |r|
|
|||
data[:config] << "[ #{r.reported_at.to_i}000, #{r.config_retrieval} ]"
|
|||
data[:runtime] << "[ #{r.reported_at.to_i}000, #{r.runtime} ]"
|
|||
72e65b31 | Ohad Levy | end
|
|
c6f1b718 | Ohad Levy | return data
|
|
72e65b31 | Ohad Levy | end
|
|
300c8b44 | Ohad Levy | ||
5af52b3b | Ohad Levy | def classes_from_storeconfigs
|
|
6fc77b66 | Ohad Levy | klasses = resources.all(:conditions => {:restype => "Class"}, :select => :title, :order => :title)
|
|
9fd7478e | Paul Kelly | klasses.map!(&:title).delete(:main)
|
|
return klasses
|
|||
5af52b3b | Ohad Levy | end
|
|
4e85a944 | Ohad Levy | def can_be_build?
|
|
d5707b63 | Ohad Levy | managed? and SETTINGS[:unattended] ? build == false : false
|
|
4e85a944 | Ohad Levy | end
|
|
3c7750e1 | Ohad Levy | def facts_hash
|
|
hash = {}
|
|||
fact_values.all(:include => :fact_name).collect do |fact|
|
|||
hash[fact.fact_name.name] = fact.value
|
|||
hash
|
|||
end
|
|||
return hash
|
|||
end
|
|||
9fd7478e | Paul Kelly | def enforce_permissions operation
|
|
if operation == "edit" and new_record?
|
|||
return true # We get called again with the operation being set to create
|
|||
end
|
|||
current = User.current
|
|||
if (operation == "edit") or operation == "destroy"
|
|||
if current.allowed_to?("#{operation}_hosts".to_sym)
|
|||
return true if Host.my_hosts(current).include? self
|
|||
end
|
|||
else # create
|
|||
if current.allowed_to?(:create_hosts)
|
|||
# We are unconstrained
|
|||
return true if current.domains.empty? and current.hostgroups.empty?
|
|||
4ebe38c7 | Ohad Levy | # We are constrained and the constraint is matched
|
|
9fd7478e | Paul Kelly | return true if (!current.domains.empty? and current.domains.include?(domain)) or
|
|
(!current.hostgroups.empty? and current.hostgroups.include?(hostgroup))
|
|||
end
|
|||
end
|
|||
errors.add_to_base "You do not have permission to #{operation} this host"
|
|||
false
|
|||
end
|
|||
90b83222 | Ohad Levy | def sp_valid?
|
|
!sp_name.empty? and !sp_ip.empty? and !sp_mac.empty?
|
|||
end
|
|||
672f931d | Paul Kelly | def jumpstart?
|
|
operatingsystem.family == "Solaris" and architecture.name =~/Sparc/i rescue false
|
|||
end
|
|||
d5707b63 | Ohad Levy | def set_hostgroup_defaults
|
|
return unless hostgroup
|
|||
8dec3f5b | Ohad Levy | assign_hostgroup_attributes(%w{environment domain puppetmaster_name puppetproxy})
|
|
b64aaf5c | Ohad Levy | if SETTINGS[:unattended] and (new_record? or managed?)
|
|
assign_hostgroup_attributes(%w{operatingsystem medium architecture ptable root_pass subnet})
|
|||
self.ip ||= subnet.unused_ip if subnet
|
|||
b1116c90 | Ohad Levy | assign_hostgroup_attributes(Vm::PROPERTIES) if hostgroup.hypervisor?
|
|
end
|
|||
d5707b63 | Ohad Levy | end
|
|
c21e2678 | Ohad Levy | # returns a rundeck output
|
|
def rundeck
|
|||
3f1e5363 | Marcello de Sousa | rdecktags = puppetclasses_names.map{|k| "class=#{k}"}
|
|
unless self.params["rundeckfacts"].empty?
|
|||
rdecktags += self.params["rundeckfacts"].split(",").map{|rdf| "#{rdf}=#{fact(rdf)[0].value}"}
|
|||
end
|
|||
{ name => { "description" => comment, "hostname" => name, "nodename" => name,
|
|||
c21e2678 | Ohad Levy | "osArch" => arch.name, "osFamily" => os.family, "osName" => os.name,
|
|
3f1e5363 | Marcello de Sousa | "osVersion" => os.release, "tags" => rdecktags, "username" => self.params["rundeckuser"] || "root" }
|
|
c21e2678 | Ohad Levy | }
|
|
3f1e5363 | Marcello de Sousa | rescue => e
|
|
logger.warn "Failed to fetch rundeck info for #{to_s}: #{e}"
|
|||
{}
|
|||
c21e2678 | Ohad Levy | end
|
|
d3d91384 | Ohad Levy | private
|
|
# align common mac and ip address input
|
|||
ad36b317 | Ohad Levy | def normalize_addresses
|
|
c22d6db2 | Ohad Levy | # a helper for variable scoping
|
|
helper = []
|
|||
[self.mac,self.sp_mac].each do |m|
|
|||
d3d91384 | Ohad Levy | unless m.empty?
|
|
m.downcase!
|
|||
if m=~/[a-f0-9]{12}/
|
|||
m = m.gsub(/(..)/){|mh| mh + ":"}[/.{17}/]
|
|||
elsif mac=~/([a-f0-9]{1,2}:){5}[a-f0-9]{1,2}/
|
|||
m = m.split(":").map{|nibble| "%02x" % ("0x" + nibble)}.join(":")
|
|||
end
|
|||
end
|
|||
c22d6db2 | Ohad Levy | helper << m
|
|
d3d91384 | Ohad Levy | end
|
|
c22d6db2 | Ohad Levy | self.mac, self.sp_mac = helper
|
|
d3d91384 | Ohad Levy | ||
c22d6db2 | Ohad Levy | helper = []
|
|
[self.ip,self.sp_ip].each do |i|
|
|||
unless i.empty?
|
|||
i = i.split(".").map{|nibble| nibble.to_i}.join(".") if i=~/(\d{1,3}\.){3}\d{1,3}/
|
|||
d3d91384 | Ohad Levy | end
|
|
c22d6db2 | Ohad Levy | helper << i
|
|
d3d91384 | Ohad Levy | end
|
|
c22d6db2 | Ohad Levy | self.ip, self.sp_ip = helper
|
|
d3d91384 | Ohad Levy | end
|
|
a26b8b66 | Ohad Levy | # ensure that host name is fqdn
|
|
d5707b63 | Ohad Levy | # if the user inputted short name, the domain name will be appended
|
|
8613dec9 | Ohad Levy | # this is done to ensure compatibility with puppet storeconfigs
|
|
a26b8b66 | Ohad Levy | def normalize_hostname
|
|
61dc8cee | mattmoran76@gmail.com | # no hostname was given or a domain was selected, since this is before validation we need to ignore
|
|
# it and let the validations to produce an error
|
|||
3af5f2e6 | Ohad Levy | return if name.empty?
|
|
if domain.nil? and name.match(/\./)
|
|||
d5707b63 | Ohad Levy | # try to assign the domain automatically based on our existing domains from the host FQDN
|
|
3af5f2e6 | Ohad Levy | self.domain = Domain.all.select{|d| name.match(d.name)}.first rescue nil
|
|
else
|
|||
# if our host is in short name, append the domain name
|
|||
99f00455 | Frank Sweetser | self.name += ".#{domain}" unless name =~ /.#{domain}$/i
|
|
3af5f2e6 | Ohad Levy | end
|
|
a26b8b66 | Ohad Levy | end
|
|
b4c2016a | Ohad Levy | ||
d5707b63 | Ohad Levy | def assign_hostgroup_attributes attrs = []
|
|
attrs.each do |attr|
|
|||
eval("self.#{attr.to_s} ||= hostgroup.#{attr.to_s}")
|
|||
b4c2016a | Ohad Levy | end
|
|
end
|
|||
d5707b63 | Ohad Levy | # checks if the host association is a valid association for this host
|
|
f1ff5404 | Ohad Levy | def ensure_assoications
|
|
status = true
|
|||
%w{ ptable medium architecture}.each do |e|
|
|||
value = self.send(e.to_sym)
|
|||
next if value.blank?
|
|||
unless os.send(e.pluralize.to_sym).include?(value)
|
|||
errors.add(e, "#{value} does not belong to #{os} operating system")
|
|||
status = false
|
|||
end
|
|||
end if os
|
|||
d0e91cb5 | Ohad Levy | ||
puppetclasses.uniq.each do |e|
|
|||
unless environment.puppetclasses.include?(e)
|
|||
errors.add(:puppetclasses, "#{e} does not belong to the #{environment} environment")
|
|||
status = false
|
|||
end
|
|||
end if environment
|
|||
f1ff5404 | Ohad Levy | status
|
|
end
|
|||
9390e9cf | Ohad Levy | # 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
|
|||
d3d91384 | Ohad Levy | end
|