Skip to content

Commit ba5df7a

Browse files
Jesús Daniel Colmenares OviedoJesús Daniel Colmenares Oviedo
authored andcommitted
nuageinit: Improvements for nuageinit
- Fix 'pkg update' usage: - The function 'nuage:run_pkg_cmd(...)' adds the flag '-y', which does not make sense with some commands such as 'pkg update', causing an error when updating the repository catalogs. - Fix typo 'ssh-authorized-keys -> ssh_authorized_keys' in 'nuageinit(7)' man page. - Document 'ssh_authorized_keys' parameter. - Use device configuration ID when no 'match' rule is specified: - This is the default behavior of cloud-init when no match rule is specified, so the device is configured anyway (even if it does not exist). This greatly simplifies things, since in many cases 'if_vtnet(4)' is used, so there is no need to perform a comparison with the MAC address. - Document 'network' parameter: - Add example to 'EXAMPLES' section. - Set 'gateway[46]' only when 'addresses' is specified: - To comply with the cloud-init specification, 'gateway4' and 'gateway6' must only take effect when 'addresses' (or static configuration) is specified. - Use a separate function to check 'match' rules: - This way, we can easily add new logic to new types of rules. - Implement 'network.ethernets.{id}.match.name' parameter: - But unlike cloud-init, which works with glob expressions (although it depends on the network backend), this implementation takes advantage of Lua pattern-matching expressions. Also note that previously we were only concerned with one interface matching, however, to be cloud-init-compliant, we need to configure the matching interfaces (one or more). - Set default router only once. - Implement 'network.ethernets.{id}.wakeonlan' parameter. - Implement 'network.ethernets.{id}.set-name' parameter. - Implement 'network.ethernets.{id}.match.driver' parameter: - Rename 'get_ifaces(...)' function as 'get_ifaces_by_mac(...)'. - Add get_ifaces_by_driver(...) function. - Implement 'network.ethernets.{id}.mtu' parameter. - Implement 'nameservers' parameter. - Use 'resolvconf(8)' to manipulate 'resolv.conf(5)'. - Use 'tzsetup(8)' to set time zone. Reviewed by: bapt@ Approved by: bapt@ Differential Revision: https://reviews.freebsd.org/D51643
1 parent 0d9ef08 commit ba5df7a

File tree

7 files changed

+335
-34
lines changed

7 files changed

+335
-34
lines changed

libexec/nuageinit/nuage.lua

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,23 @@ local function chpasswd(obj)
451451
end
452452
end
453453

454+
local function settimezone(timezone)
455+
if timezone == nil then
456+
return
457+
end
458+
local root = os.getenv("NUAGE_FAKE_ROOTDIR")
459+
if not root then
460+
root = "/"
461+
end
462+
463+
f, _, rc = os.execute("tzsetup -s -C " .. root .. " " .. timezone)
464+
465+
if not f then
466+
warnmsg("Impossible to configure time zone ( rc = " .. rc .. " )")
467+
return
468+
end
469+
end
470+
454471
local function pkg_bootstrap()
455472
if os.getenv("NUAGE_RUN_TESTS") then
456473
return true
@@ -480,7 +497,7 @@ local function install_package(package)
480497
end
481498

482499
local function run_pkg_cmd(subcmd)
483-
local cmd = "pkg " .. subcmd .. " -y"
500+
local cmd = "env ASSUME_ALWAYS_YES=yes pkg " .. subcmd
484501
if os.getenv("NUAGE_RUN_TESTS") then
485502
print(cmd)
486503
return true
@@ -556,6 +573,7 @@ local n = {
556573
dirname = dirname,
557574
mkdir_p = mkdir_p,
558575
sethostname = sethostname,
576+
settimezone = settimezone,
559577
adduser = adduser,
560578
addgroup = addgroup,
561579
addsshkey = addsshkey,

libexec/nuageinit/nuageinit

Lines changed: 209 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,15 @@ local function open_config(name)
4646
return openat("/etc/rc.conf.d", name)
4747
end
4848

49-
local function get_ifaces()
49+
local function open_resolv_conf()
50+
return openat("/etc", "resolv.conf")
51+
end
52+
53+
local function open_resolvconf_conf()
54+
return openat("/etc", "resolvconf.conf")
55+
end
56+
57+
local function get_ifaces_by_mac()
5058
local parser = ucl.parser()
5159
-- grab ifaces
5260
local ns = io.popen("netstat -i --libxo json")
@@ -77,6 +85,10 @@ local function sethostname(obj)
7785
end
7886
end
7987

88+
local function settimezone(obj)
89+
nuage.settimezone(obj.timezone)
90+
end
91+
8092
local function groups(obj)
8193
if obj.groups == nil then return end
8294

@@ -171,6 +183,59 @@ local function ssh_authorized_keys(obj)
171183
end
172184
end
173185

186+
local function nameservers(interface, obj)
187+
local resolvconf_conf_handler = open_resolvconf_conf()
188+
189+
if obj.search then
190+
local with_space = false
191+
192+
resolvconf_conf_handler:write('search_domains="')
193+
194+
for _, d in ipairs(obj.search) do
195+
if with_space then
196+
resolvconf_conf_handler:write(" " .. d)
197+
else
198+
resolvconf_conf_handler:write(d)
199+
with_space = true
200+
end
201+
end
202+
203+
resolvconf_conf_handler:write('"\n')
204+
end
205+
206+
if obj.addresses then
207+
local with_space = false
208+
209+
resolvconf_conf_handler:write('name_servers="')
210+
211+
for _, a in ipairs(obj.addresses) do
212+
if with_space then
213+
resolvconf_conf_handler:write(" " .. a)
214+
else
215+
resolvconf_conf_handler:write(a)
216+
with_space = true
217+
end
218+
end
219+
220+
resolvconf_conf_handler:write('"\n')
221+
end
222+
223+
resolvconf_conf_handler:close()
224+
225+
local resolv_conf = root .. "/etc/resolv.conf"
226+
227+
resolv_conf_attr = lfs.attributes(resolv_conf)
228+
229+
if resolv_conf_attr == nil then
230+
resolv_conf_handler = open_resolv_conf()
231+
resolv_conf_handler:close()
232+
end
233+
234+
if not os.execute("resolvconf -a " .. interface .. " < " .. resolv_conf) then
235+
nuage.warn("Failed to execute resolvconf(8)")
236+
end
237+
end
238+
174239
local function install_packages(packages)
175240
if not nuage.pkg_bootstrap() then
176241
nuage.warn("Failed to bootstrap pkg, skip installing packages")
@@ -187,6 +252,85 @@ local function install_packages(packages)
187252
end
188253
end
189254

255+
local function list_ifaces()
256+
local proc = io.popen("ifconfig -l")
257+
local raw_ifaces = proc:read("*a")
258+
proc:close()
259+
local ifaces = {}
260+
for i in raw_ifaces:gmatch("[^%s]+") do
261+
table.insert(ifaces, i)
262+
end
263+
return ifaces
264+
end
265+
266+
local function get_ifaces_by_driver()
267+
local proc = io.popen("ifconfig -D")
268+
local drivers = {}
269+
local last_interface = nil
270+
for line in proc:lines() do
271+
local interface = line:match("^([%S]+): ")
272+
273+
if interface then
274+
last_interface = interface
275+
end
276+
277+
local driver = line:match("^[%s]+drivername: ([%S]+)$")
278+
279+
if driver then
280+
drivers[driver] = last_interface
281+
end
282+
end
283+
proc:close()
284+
285+
return drivers
286+
end
287+
288+
local function match_rules(rules)
289+
-- To comply with the cloud-init specification, all rules must match and a table
290+
-- with the matching interfaces must be returned. This changes the way we initially
291+
-- thought about our implementation, since at first we only needed one interface,
292+
-- but cloud-init performs actions on a group of matching interfaces.
293+
local interfaces = {}
294+
if rules.macaddress then
295+
local ifaces = get_ifaces_by_mac()
296+
local interface = ifaces[rules.macaddress]
297+
if not interface then
298+
nuage.warn("not interface matching by MAC address: " .. rules.macaddress)
299+
return
300+
end
301+
interfaces[interface] = 1
302+
end
303+
if rules.name then
304+
local match = false
305+
for _, i in pairs(list_ifaces()) do
306+
if i:match(rules.name) then
307+
match = true
308+
interfaces[i] = 1
309+
end
310+
end
311+
if not match then
312+
nuage.warn("not interface matching by name: " .. rules.name)
313+
return
314+
end
315+
end
316+
if rules.driver then
317+
local match = false
318+
local drivers = get_ifaces_by_driver()
319+
for d in pairs(drivers) do
320+
if d:match(rules.driver) then
321+
match = true
322+
interface = drivers[d]
323+
interfaces[interface] = 1
324+
end
325+
end
326+
if not match then
327+
nuage.warn("not interface matching by driver: " .. rules.driver)
328+
return
329+
end
330+
end
331+
return interfaces
332+
end
333+
190334
local function write_files(files, defer)
191335
if not files then
192336
return
@@ -210,41 +354,76 @@ end
210354
local function network_config(obj)
211355
if obj.network == nil then return end
212356

213-
local ifaces = get_ifaces()
214357
local network = open_config("network")
215358
local routing = open_config("routing")
216359
local ipv6 = {}
217-
for _, v in pairs(obj.network.ethernets) do
218-
if not v.match then
219-
goto next
360+
local set_defaultrouter = true
361+
local set_defaultrouter6 = true
362+
local set_nameservers = true
363+
for i, v in pairs(obj.network.ethernets) do
364+
local interfaces = {}
365+
if v.match then
366+
interfaces = match_rules(v.match)
367+
368+
if next(interfaces) == nil then
369+
goto next
370+
end
371+
else
372+
interfaces[i] = 1
220373
end
221-
if not v.match.macaddress then
222-
goto next
374+
local extra_opts = ""
375+
if v.wakeonlan then
376+
extra_opts = extra_opts .. " wol"
223377
end
224-
if not ifaces[v.match.macaddress] then
225-
nuage.warn("not interface matching: " .. v.match.macaddress)
226-
goto next
378+
if v.mtu then
379+
if type(v.mtu) == "number" then
380+
mtu = tostring(v.mtu)
381+
else
382+
mtu = v.mtu
383+
end
384+
if mtu:match("%d") then
385+
extra_opts = extra_opts .. " mtu " .. mtu
386+
else
387+
nuage.warn("MTU is not set because the specified value is invalid: " .. mtu)
388+
end
227389
end
228-
local interface = ifaces[v.match.macaddress]
229-
if v.dhcp4 then
230-
network:write("ifconfig_" .. interface .. '="DHCP"\n')
231-
elseif v.addresses then
232-
for _, a in pairs(v.addresses) do
233-
if a:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)") then
234-
network:write("ifconfig_" .. interface .. '="inet ' .. a .. '"\n')
235-
else
236-
network:write("ifconfig_" .. interface .. '_ipv6="inet6 ' .. a .. '"\n')
237-
ipv6[#ipv6 + 1] = interface
390+
for interface in pairs(interfaces) do
391+
if v.match and v.match.macaddress and v["set-name"] then
392+
local ifaces = get_ifaces_by_mac()
393+
local matched = ifaces[v.match.macaddress]
394+
if matched and matched == interface then
395+
network:write("ifconfig_" .. interface .. '_name=' .. v["set-name"] .. '\n')
396+
interface = v["set-name"]
397+
end
398+
end
399+
if v.dhcp4 then
400+
network:write("ifconfig_" .. interface .. '="DHCP"' .. extra_opts .. '\n')
401+
elseif v.addresses then
402+
for _, a in pairs(v.addresses) do
403+
if a:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)") then
404+
network:write("ifconfig_" .. interface .. '="inet ' .. a .. extra_opts .. '"\n')
405+
else
406+
network:write("ifconfig_" .. interface .. '_ipv6="inet6 ' .. a .. extra_opts .. '"\n')
407+
ipv6[#ipv6 + 1] = interface
408+
end
409+
end
410+
if set_nameservers and v.nameservers then
411+
set_nameservers = false
412+
nameservers(interface, v.nameservers)
413+
end
414+
if set_defaultrouter and v.gateway4 then
415+
set_defaultrouter = false
416+
routing:write('defaultrouter="' .. v.gateway4 .. '"\n')
417+
end
418+
if v.gateway6 then
419+
if set_defaultrouter6 then
420+
set_defaultrouter6 = false
421+
routing:write('ipv6_defaultrouter="' .. v.gateway6 .. '"\n')
422+
end
423+
routing:write("ipv6_route_" .. interface .. '="' .. v.gateway6)
424+
routing:write(" -prefixlen 128 -interface " .. interface .. '"\n')
238425
end
239426
end
240-
end
241-
if v.gateway4 then
242-
routing:write('defaultrouter="' .. v.gateway4 .. '"\n')
243-
end
244-
if v.gateway6 then
245-
routing:write('ipv6_defaultrouter="' .. v.gateway6 .. '"\n')
246-
routing:write("ipv6_route_" .. interface .. '="' .. v.gateway6)
247-
routing:write(" -prefixlen 128 -interface " .. interface .. '"\n')
248427
end
249428
::next::
250429
end
@@ -316,7 +495,7 @@ local function config2_network(p)
316495
end
317496
local obj = parser:get_object()
318497

319-
local ifaces = get_ifaces()
498+
local ifaces = get_ifaces_by_mac()
320499
if not ifaces then
321500
nuage.warn("no network interfaces found")
322501
return
@@ -468,6 +647,7 @@ f:close()
468647
if line == "#cloud-config" then
469648
local pre_network_calls = {
470649
sethostname,
650+
settimezone,
471651
groups,
472652
create_default_user,
473653
ssh_keys,

0 commit comments

Comments
 (0)