-
Notifications
You must be signed in to change notification settings - Fork 983
/
tftp.rb
200 lines (172 loc) · 6.37 KB
/
tftp.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
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
module Orchestration::TFTP
extend ActiveSupport::Concern
include Orchestration::Common
included do
after_validation :validate_tftp, :unless => :skip_orchestration?
after_validation :queue_tftp
before_destroy :queue_tftp_destroy
# required for pxe template url helpers
include Rails.application.routes.url_helpers
register_rebuild(:rebuild_tftp, N_('TFTP'))
end
def tftp_ready?
# host.managed? and managed? should always come first so that orchestration doesn't
# even get tested for such objects
(host.nil? || host&.managed?) && managed && provision? && (host&.operatingsystem && host.pxe_loader.present?) && !image_build?
end
def tftp?
tftp_ready? && !!(subnet && subnet.tftp?)
end
def tftp6?
tftp_ready? && !!(subnet6 && subnet6.tftp?)
end
def tftp
subnet.tftp_proxy if tftp?
end
def tftp6
subnet6.tftp_proxy if tftp6?
end
def rebuild_tftp
unless tftp? || tftp6?
logger.info "TFTP not supported for #{name} (#{ip}/#{ip6}), skipping orchestration rebuild"
return true
end
results = host.operatingsystem.template_kinds_for_tftp.map do |kind|
rebuild_tftp_kind_safe(kind)
end
results.all?
end
def rebuild_tftp_kind_safe(kind)
setTFTP(kind)
rescue => e
Foreman::Logging.exception "Failed to rebuild TFTP record for #{name} (#{ip}/#{ip6})", e, :level => :error
false
end
def generate_pxe_template(kind)
return build_pxe_render(kind) if build?
default_pxe_render(kind)
end
protected
def build_pxe_render(kind)
template = host.provisioning_template({:kind => kind})
return unless template.present?
host.render_template(template: template)
rescue => e
failure _("Unable to render %{kind} template '%{name}': %{e}") % { :kind => kind, :name => template.try(:name), :e => e }, e
end
def default_pxe_render(kind)
template_name = host.local_boot_template_name(kind)
# Safely return in case there's no template configured for the specified kind
return unless template_name.present?
template = ProvisioningTemplate.find_by_name(template_name)
raise Foreman::Exception.new(N_("Template '%s' was not found"), template_name) unless template
host.render_template(template: template)
rescue => e
failure _("Unable to render '%{name}' template: %{e}") % { :name => template_name, :e => e }, e
end
# Adds the host to the forward and reverse TFTP zones
# +returns+ : Boolean true on success
def setTFTP(kind)
content = generate_pxe_template(kind)
if content
logger.info "Deploying TFTP #{kind} configuration for #{host.name}"
each_unique_feasible_tftp_proxy do |proxy|
mac_addresses_for_provisioning.each do |mac_addr|
proxy.set(kind, mac_addr, :pxeconfig => content)
end
end
else
logger.info "Skipping TFTP #{kind} configuration for #{host.name}"
true
end
end
# Removes the host from the forward and reverse TFTP zones
# +returns+ : Boolean true on success
def delTFTP(kind)
logger.info "Delete the TFTP configuration for #{host.name}"
each_unique_feasible_tftp_proxy do |proxy|
mac_addresses_for_provisioning.each do |mac_addr|
proxy.delete(kind, mac_addr)
end
end
end
def setTFTPBootFiles
logger.info "Fetching required TFTP boot files for #{host.name}"
valid = []
host.operatingsystem.pxe_files(host.medium_provider).each do |bootfile_info|
bootfile_info.each do |prefix, path|
valid << each_unique_feasible_tftp_proxy do |proxy|
proxy.fetch_boot_file(:prefix => prefix.to_s, :path => path)
end
end
end
failure _("Failed to fetch boot files") unless valid.all?
valid.all?
end
# empty method for rollbacks
def delTFTPBootFiles
end
private
def validate_tftp
return unless tftp? || tftp6?
return unless host.operatingsystem
pxe_kind = host.operatingsystem.pxe_loader_kind(host)
if pxe_kind && host.provisioning_template({:kind => pxe_kind}).nil?
failure _("No %{template_kind} templates were found for this host, make sure you define at least one in your %{os} settings or change PXE loader") %
{ :template_kind => pxe_kind, :os => host.operatingsystem }
end
end
def queue_tftp
return log_orchestration_errors unless (tftp? || tftp6?) && no_errors
# Jumpstart builds require only minimal tftp services. They do require a tftp object to query for the boot_server.
return true if host.jumpstart?
new_record? ? queue_tftp_create : queue_tftp_update
end
def queue_tftp_create
host.operatingsystem.template_kinds.each do |kind|
queue.create(:name => _("Deploy TFTP %{kind} config for %{host}") % {:kind => kind, :host => self}, :priority => 20, :action => [self, :setTFTP, kind])
end
return unless build
queue.create(:name => _("Fetch TFTP boot files for %s") % self, :priority => 25, :action => [self, :setTFTPBootFiles])
end
def queue_tftp_update
set_tftp = false
# we switched build mode
set_tftp = true if old.host.build? != host.build?
# medium or arch changed
set_tftp = true if old.host.medium.try(:id) != host.medium.try(:id) || old.host.arch.try(:id) != host.arch.try(:id)
# operating system changed
set_tftp = true if host.operatingsystem && old.host.operatingsystem && (old.host.operatingsystem.name != host.operatingsystem.name || old.host.operatingsystem.try(:id) != host.operatingsystem.try(:id))
# MAC address changed
if mac != old.mac
set_tftp = true
# clean up old TFTP reservation file
queue_tftp_destroy(false, 19, old) if old.tftp?
end
queue_tftp_create if set_tftp
end
def queue_tftp_destroy(validate = true, priority = 20, host = self)
if validate
return unless (tftp? || tftp6?) && no_errors
return true if host.jumpstart?
end
host.operatingsystem.template_kinds.each do |kind|
queue.create(:name => _("Delete TFTP %{kind} config for %{host}") % {:kind => kind, :host => host}, :priority => priority, :action => [host, :delTFTP, kind])
end
end
def no_errors
errors.empty? && host.errors.empty?
end
def unique_feasible_tftp_proxies
proxies = []
proxies << tftp if tftp?
proxies << tftp6 if tftp6?
proxies.uniq { |p| p.url }
end
def each_unique_feasible_tftp_proxy
results = unique_feasible_tftp_proxies.map do |proxy|
yield(proxy)
end
results.all?
end
end