Class: Host::Base

Direct Known Subclasses

Managed

Constant Summary collapse

KERNEL_RELEASE_FACTS =
['kernelrelease', 'ansible_kernel', 'kernel::release', 'uname::release']
PRIMARY_INTERFACE_ATTRIBUTES =
[:name, :ip, :ip6, :mac,
:subnet, :subnet_id, :subnet_name,
:subnet6, :subnet6_id, :subnet6_name,
:domain, :domain_id, :domain_name,
:lookup_values_attributes].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from PxeLoaderSuggestion

#suggest_default_pxe_loader

Methods included from Facets::BaseHostExtensions

#apply_facet_attributes, #populate_facet_fields

Methods included from Foreman::TelemetryHelper

#telemetry_duration_histogram, #telemetry_increment_counter, #telemetry_observe_histogram, #telemetry_set_gauge

Methods included from Hostext::Ownership

#is_owned_by, #is_owned_by=, #owner, #owner_suggestion

Methods included from InterfaceCloning

#setup_object_clone

Methods included from DestroyFlag

#being_destroyed?

Methods included from Authorizable

#authorized?, #check_permissions_after_save

Methods included from PermissionName

#permission_name

Methods inherited from ApplicationRecord

graphql_type, #logger, logger

Methods included from AuditAssociations::AssociationsDefinitions

#audit_associations, #audited, #configure_dirty_associations, #normalize_associations

Constructor Details

#initialize(*args) ⇒ Base

primary interface is mandatory because of delegated methods so we build it if it's missing similar for provision interface we can't set name attribute until we have primary interface so we don't pass it to super initializer and we set name when we are sure that we have primary interface we can't create primary interface before calling super because args may contain nested interface attributes


87
88
89
90
91
92
93
94
95
# File 'app/models/host/base.rb', line 87

def initialize(*args)
  values_for_primary_interface = {}
  build_values_for_primary_interface!(values_for_primary_interface, args)

  super(*args)

  build_required_interfaces
  update_primary_interface_attributes(values_for_primary_interface)
end

Instance Attribute Details

#updated_virtualsObject


115
116
117
# File 'app/models/host/base.rb', line 115

def updated_virtuals
  @updated_virtuals ||= []
end

Class Method Details

.attributes_protected_by_defaultObject


119
120
121
# File 'app/models/host/base.rb', line 119

def self.attributes_protected_by_default
  super - [inheritance_column]
end

.import_host(hostname, certname = nil) ⇒ Object


123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'app/models/host/base.rb', line 123

def self.import_host(hostname, certname = nil)
  raise(::Foreman::Exception.new("Invalid Hostname, must be a String")) unless hostname.is_a?(String)

  # downcase everything
  hostname.try(:downcase!)
  certname.try(:downcase!)

  host = Host.find_by_certname(certname) if certname.present?
  host ||= Host.find_by_name(hostname)
  host ||= new(:name => hostname) # if no host was found, build a new one

  # if we were given a certname but found the Host by hostname we should update the certname
  # this also sets certname for newly created hosts
  host.certname = certname if certname.present?

  host
end

.taxonomy_conditionsObject


47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'app/models/host/base.rb', line 47

def self.taxonomy_conditions
  conditions = {}
  if Organization.current.nil? && User.current.present? && !User.current.admin?
    conditions[:organization_id] = User.current.organization_and_child_ids
  else
    conditions[:organization_id] = Organization.current&.subtree_ids
  end
  if Location.current.nil? && User.current.present? && !User.current.admin?
    conditions[:location_id] = User.current.location_and_child_ids
  else
    conditions[:location_id] = Location.current&.subtree_ids
  end
  conditions.compact
end

Instance Method Details

#==(comparison_object) ⇒ Object


233
234
235
236
237
238
# File 'app/models/host/base.rb', line 233

def ==(comparison_object)
  super ||
    comparison_object.is_a?(Host::Base) &&
    id.present? &&
    comparison_object.id == id
end

#attributes_to_import_from_factsObject


146
147
148
# File 'app/models/host/base.rb', line 146

def attributes_to_import_from_facts
  [:model]
end

#becomes(*args) ⇒ Object


301
302
303
304
305
306
307
# File 'app/models/host/base.rb', line 301

def becomes(*args)
  became = super
  became.drop_primary_interface_cache
  became.drop_provision_interface_cache
  became.interfaces = interfaces
  became
end

#bond_interfacesObject


275
276
277
# File 'app/models/host/base.rb', line 275

def bond_interfaces
  interfaces.bonds.is_managed.all
end

#bridge_interfacesObject


282
283
284
# File 'app/models/host/base.rb', line 282

def bridge_interfaces
  interfaces.bridges.is_managed.all
end

#drop_primary_interface_cacheObject


309
310
311
# File 'app/models/host/base.rb', line 309

def drop_primary_interface_cache
  @primary_interface = nil
end

#drop_provision_interface_cacheObject


313
314
315
# File 'app/models/host/base.rb', line 313

def drop_provision_interface_cache
  @provision_interface = nil
end

#dupObject


97
98
99
100
101
# File 'app/models/host/base.rb', line 97

def dup
  super.tap do |host|
    host.interfaces << primary_interface.dup if primary_interface.present?
  end
end

#facts_hashObject Also known as: facts


224
225
226
227
228
229
230
# File 'app/models/host/base.rb', line 224

def facts_hash
  hash = {}
  fact_values.includes(:fact_name).collect do |fact|
    hash[fact.fact_name.name] = fact.value
  end
  hash
end

#import_facts(facts, source_proxy = nil) ⇒ Object


141
142
143
144
# File 'app/models/host/base.rb', line 141

def import_facts(facts, source_proxy = nil)
  Foreman::Deprecation.deprecation_warning('2.5', 'Use HostFactImporter#import_facts method instead of Host#import_facts')
  HostFactImporter.new(self).import_facts(facts, source_proxy)
end

#import_missing_idsObject


325
326
327
328
# File 'app/models/host/base.rb', line 325

def import_missing_ids
  tax_location.import_missing_ids     if location
  tax_organization.import_missing_ids if organization
end

#interfaces_with_identifier(identifiers) ⇒ Object


291
292
293
# File 'app/models/host/base.rb', line 291

def interfaces_with_identifier(identifiers)
  interfaces.is_managed.where(:identifier => identifiers).all
end

#lookup_value_matchObject


333
334
335
# File 'app/models/host/base.rb', line 333

def lookup_value_match
  "fqdn=#{fqdn || name}"
end

#managed_interfacesObject


267
268
269
# File 'app/models/host/base.rb', line 267

def managed_interfaces
  interfaces.managed.is_managed.all
end

#matching?Boolean

Returns:

  • (Boolean)

317
318
319
# File 'app/models/host/base.rb', line 317

def matching?
  missing_ids.empty?
end

#missing_idsObject


321
322
323
# File 'app/models/host/base.rb', line 321

def missing_ids
  Array.wrap(tax_location.try(:missing_ids)) + Array.wrap(tax_organization.try(:missing_ids))
end

#orchestrated?Boolean

Returns:

  • (Boolean)

348
349
350
# File 'app/models/host/base.rb', line 348

def orchestrated?
  self.class.included_modules.include?(Orchestration)
end

#overwrite=(value) ⇒ Object

We have to coerce the value back to boolean. It is not done for us by the framework.


245
246
247
# File 'app/models/host/base.rb', line 245

def overwrite=(value)
  @overwrite = value.to_s == "true"
end

#overwrite?Boolean

Returns:

  • (Boolean)

240
241
242
# File 'app/models/host/base.rb', line 240

def overwrite?
  @overwrite ||= false
end

#populate_fields_from_facts(parser, type, source_proxy) ⇒ Object


156
157
158
159
160
161
162
# File 'app/models/host/base.rb', line 156

def populate_fields_from_facts(parser, type, source_proxy)
  # we must create interface if it's missing so we can store domain
  build_required_interfaces(managed: false, type: primary_interface_type(parser))
  set_non_empty_values(parser, attributes_to_import_from_facts)
  set_interfaces(parser) if parser.parse_interfaces?
  set_comment(parser) if parser.has_comment?
end

#primary_interfaceObject


252
253
254
# File 'app/models/host/base.rb', line 252

def primary_interface
  get_interface_by_flag(:primary)
end

#primary_interface_type(parser) ⇒ Object


150
151
152
153
154
# File 'app/models/host/base.rb', line 150

def primary_interface_type(parser)
  return unless parser.parse_interfaces?
  identifier, = parser.suggested_primary_interface(self)
  interface_class(identifier).to_s
end

#provision_interfaceObject


259
260
261
# File 'app/models/host/base.rb', line 259

def provision_interface
  get_interface_by_flag(:provision)
end

#reload(*args) ⇒ Object


295
296
297
298
299
# File 'app/models/host/base.rb', line 295

def reload(*args)
  drop_primary_interface_cache
  drop_provision_interface_cache
  super
end

#render_template(template:, **params) ⇒ Object


352
353
354
# File 'app/models/host/base.rb', line 352

def render_template(template:, **params)
  template.render(host: self, **params)
end

#set_comment(parser) ⇒ Object


211
212
213
# File 'app/models/host/base.rb', line 211

def set_comment(parser)
  self.comment = parser.comment
end

#set_interfaces(parser) ⇒ Object


171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'app/models/host/base.rb', line 171

def set_interfaces(parser)
  # if host has no information in primary interface we try to match it and update it
  # instead of creating new interface, suggested primary interface mac and identifier
  # is saved to primary interface so we match it in updating code below
  if !managed? && primary_interface.mac.blank? && primary_interface.identifier.blank?
    identifier, values = parser.suggested_primary_interface(self)
    if values.present?
      logger.debug "Suggested #{identifier} NIC as a primary interface."
      primary_interface.mac = Net::Validations.normalize_mac(values[:macaddress])
      interface_klass = interface_class(identifier)
      # bridge interfaces are not attached to parent interface so save would not be possible
      if interface_klass != Nic::Bridge
        primary_interface.virtual = !!values[:virtual]
        primary_interface.attached_to = values[:attached_to] || ''
        primary_interface.tag = values[:tag] || ''
      end
    end
    primary_interface.update_attribute(:identifier, identifier)
    primary_interface.save!
  end

  changed_count = 0
  parser.interfaces.each do |name, attributes|
    iface = get_interface_scope(name, attributes).try(:first) || interface_class(name).new(:managed => false)
    # create or update existing interface
    changed_count += 1 if set_interface(attributes, name, iface)
  end

  ipmi = parser.ipmi_interface
  if ipmi.present?
    existing = interfaces.find_by(:mac => ipmi[:macaddress], :type => Nic::BMC.name)
    iface = existing || Nic::BMC.new(:managed => false)
    iface.provider ||= 'IPMI'
    changed_count += 1 if set_interface(ipmi, 'ipmi', iface)
  end
  telemetry_increment_counter(:importer_facts_count_interfaces, changed_count, type: parser.class_name_humanized)

  interfaces.reload
end

#set_non_empty_values(parser, methods) ⇒ Object


164
165
166
167
168
169
# File 'app/models/host/base.rb', line 164

def set_non_empty_values(parser, methods)
  methods.each do |attr|
    value = parser.send(attr)
    send("#{attr}=", value) if value.present?
  end
end

#setup_cloneObject

we must also clone interfaces objects so we can detect their attribute changes method is public because it's used when we run orchestration from interface side


339
340
341
342
# File 'app/models/host/base.rb', line 339

def setup_clone
  return if new_record?
  @old = super { |clone| clone.interfaces = interfaces.map { |i| setup_object_clone(i) } }
end

#skip_orchestration?Boolean

Returns:

  • (Boolean)

344
345
346
# File 'app/models/host/base.rb', line 344

def skip_orchestration?
  false
end