aboutsummaryrefslogtreecommitdiff
path: root/libexec/nuageinit/nuageinit
diff options
context:
space:
mode:
Diffstat (limited to 'libexec/nuageinit/nuageinit')
-rwxr-xr-xlibexec/nuageinit/nuageinit312
1 files changed, 312 insertions, 0 deletions
diff --git a/libexec/nuageinit/nuageinit b/libexec/nuageinit/nuageinit
new file mode 100755
index 000000000000..08224061d1b1
--- /dev/null
+++ b/libexec/nuageinit/nuageinit
@@ -0,0 +1,312 @@
+#!/usr/libexec/flua
+
+-- SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+--
+-- Copyright(c) 2022 Baptiste Daroussin <bapt@FreeBSD.org>
+
+local nuage = require("nuage")
+local yaml = require("yaml")
+
+if #arg ~= 2 then
+ nuage.err("Usage ".. arg[0] .." <cloud-init directory> [config-2|nocloud]")
+end
+local path = arg[1]
+local citype = arg[2]
+local ucl = require("ucl")
+
+local default_user = {
+ name = "freebsd",
+ homedir = "/home/freebsd",
+ groups = "wheel",
+ gecos = "FreeBSD User",
+ shell = "/bin/sh",
+ plain_text_passwd = "freebsd"
+}
+
+local root = os.getenv("NUAGE_FAKE_ROOTDIR")
+if not root then
+ root = ""
+end
+
+local function open_config(name)
+ nuage.mkdir_p(root .. "/etc/rc.conf.d")
+ local f,err = io.open(root .. "/etc/rc.conf.d/" .. name, "w")
+ if not f then
+ nuage.err("nuageinit: unable to open "..name.." config: " .. err)
+ end
+ return f
+end
+
+local function get_ifaces()
+ local parser = ucl.parser()
+ -- grab ifaces
+ local ns = io.popen('netstat -i --libxo json')
+ local netres = ns:read("*a")
+ ns:close()
+ local res,err = parser:parse_string(netres)
+ if not res then
+ nuage.warn("Error parsing netstat -i --libxo json outout: " .. err)
+ return nil
+ end
+ local ifaces = parser:get_object()
+ local myifaces = {}
+ for _,iface in pairs(ifaces["statistics"]["interface"]) do
+ if iface["network"]:match("<Link#%d>") then
+ local s = iface["address"]
+ myifaces[s:lower()] = iface["name"]
+ end
+ end
+ return myifaces
+end
+
+local function config2_network(p)
+ local parser = ucl.parser()
+ local f = io.open(p .. "/network_data.json")
+ if not f then
+ -- silently return no network configuration is provided
+ return
+ end
+ f:close()
+ local res,err = parser:parse_file(p .. "/network_data.json")
+ if not res then
+ nuage.warn("nuageinit: error parsing network_data.json: " .. err)
+ return
+ end
+ local obj = parser:get_object()
+
+ local ifaces = get_ifaces()
+ if not ifaces then
+ nuage.warn("nuageinit: no network interfaces found")
+ return
+ end
+ local mylinks = {}
+ for _,v in pairs(obj["links"]) do
+ local s = v["ethernet_mac_address"]:lower()
+ mylinks[v["id"]] = ifaces[s]
+ end
+
+ nuage.mkdir_p(root .. "/etc/rc.conf.d")
+ local network = open_config("network")
+ local routing = open_config("routing")
+ local ipv6 = {}
+ local ipv6_routes = {}
+ local ipv4 = {}
+ for _,v in pairs(obj["networks"]) do
+ local interface = mylinks[v["link"]]
+ if v["type"] == "ipv4_dhcp" then
+ network:write("ifconfig_"..interface.."=\"DHCP\"\n")
+ end
+ if v["type"] == "ipv4" then
+ network:write("ifconfig_"..interface.."=\"inet "..v["ip_address"].." netmask " .. v["netmask"] .. "\"\n")
+ if v["gateway"] then
+ routing:write("defaultrouter=\""..v["gateway"].."\"\n")
+ end
+ if v["routes"] then
+ for i,r in ipairs(v["routes"]) do
+ local rname = "cloudinit" .. i .. "_" .. interface
+ if v["gateway"] and v["gateway"] == r["gateway"] then goto next end
+ if r["network"] == "0.0.0.0" then
+ routing:write("defaultrouter=\""..r["gateway"].."\"\n")
+ goto next
+ end
+ routing:write("route_".. rname .. "=\"-net ".. r["network"] .. " ")
+ routing:write(r["gateway"] .. " " .. r["netmask"] .. "\"\n")
+ ipv4[#ipv4 + 1] = rname
+ ::next::
+ end
+ end
+ end
+ if v["type"] == "ipv6" then
+ ipv6[#ipv6+1] = interface
+ ipv6_routes[#ipv6_routes+1] = interface
+ network:write("ifconfig_"..interface.."_ipv6=\"inet6 "..v["ip_address"].."\"\n")
+ if v["gateway"] then
+ routing:write("ipv6_defaultrouter=\""..v["gateway"].."\"\n")
+ routing:write("ipv6_route_"..interface.."=\""..v["gateway"])
+ routing:write(" -prefixlen 128 -interface "..interface.."\"\n")
+ end
+ -- TODO compute the prefixlen for the routes
+ --if v["routes"] then
+ -- for i,r in ipairs(v["routes"]) do
+ -- local rname = "cloudinit" .. i .. "_" .. mylinks[v["link"]]
+ -- -- skip all the routes which are already covered by the default gateway, some provider
+ -- -- still list plenty of them.
+ -- if v["gateway"] == r["gateway"] then goto next end
+ -- routing:write("ipv6_route_" .. rname .. "\"\n")
+ -- ipv6_routes[#ipv6_routes+1] = rname
+ -- ::next::
+ -- end
+ --end
+ end
+ end
+ if #ipv4 > 0 then
+ routing:write("static_routes=\"")
+ routing:write(table.concat(ipv4, " ") .. "\"\n")
+ end
+ if #ipv6 > 0 then
+ network:write("ipv6_network_interfaces=\"")
+ network:write(table.concat(ipv6, " ") .. "\"\n")
+ network:write("ipv6_default_interface=\""..ipv6[1].."\"\n")
+ end
+ if #ipv6_routes > 0 then
+ routing:write("ipv6_static_routes=\"")
+ routing:write(table.concat(ipv6, " ") .. "\"\n")
+ end
+ network:close()
+ routing:close()
+end
+
+if citype == "config-2" then
+ local parser = ucl.parser()
+ local res,err = parser:parse_file(path..'/meta_data.json')
+
+ if not res then
+ nuage.err("nuageinit: error parsing config-2: meta_data.json: " .. err)
+ end
+ local obj = parser:get_object()
+ local sshkeys = obj["public_keys"]
+ if sshkeys then
+ local homedir = nuage.adduser(default_user)
+ for _,v in pairs(sshkeys) do
+ nuage.addsshkey(root .. homedir, v)
+ end
+ end
+ nuage.sethostname(obj["hostname"])
+
+ -- network
+ config2_network(path)
+elseif citype == "nocloud" then
+ local f,err = io.open(path.."/meta-data")
+ if err then
+ nuage.err("nuageinit: error parsing nocloud meta-data: ".. err)
+ end
+ local obj = yaml.eval(f:read("*a"))
+ f:close()
+ if not obj then
+ nuage.err("nuageinit: error parsing nocloud meta-data")
+ end
+ local hostname = obj['local-hostname']
+ if not hostname then
+ hostname = obj['hostname']
+ end
+ if hostname then
+ nuage.sethostname(hostname)
+ end
+else
+ nuage.err("Unknown cloud init type: ".. citype)
+end
+
+-- deal with user-data
+local f = io.open(path..'/user-data', "r")
+if not f then
+ os.exit(0)
+end
+local line = f:read('*l')
+f:close()
+if line == "#cloud-config" then
+ f = io.open(path.."/user-data")
+ local obj = yaml.eval(f:read("*a"))
+ f:close()
+ if not obj then
+ nuage.err("nuageinit: error parsing cloud-config file: user-data")
+ end
+ if obj.groups then
+ for n,g in pairs(obj.groups) do
+ if (type(g) == "string") then
+ local r = nuage.addgroup({name = g})
+ if not r then
+ nuage.warn("nuageinit: failed to add group: ".. g)
+ end
+ elseif type(g) == "table" then
+ for k,v in pairs(g) do
+ nuage.addgroup({name = k, members = v})
+ end
+ else
+ nuage.warn("nuageinit: invalid type : "..type(g).." for users entry number "..n);
+ end
+ end
+ end
+ if obj.users then
+ for n,u in pairs(obj.users) do
+ if type(u) == "string" then
+ if u == "default" then
+ nuage.adduser(default_user)
+ else
+ nuage.adduser({name = u})
+ end
+ elseif type(u) == "table" then
+ -- ignore users without a username
+ if u.name == nil then
+ goto unext
+ end
+ local homedir = nuage.adduser(u)
+ if u.ssh_authorized_keys then
+ for _,v in ipairs(u.ssh_authorized_keys) do
+ nuage.addsshkey(homedir, v)
+ end
+ end
+ else
+ nuage.warn("nuageinit: invalid type : "..type(u).." for users entry number "..n);
+ end
+ ::unext::
+ end
+ else
+ -- default user if none are defined
+ nuage.adduser(default_user)
+ end
+ if obj.ssh_authorized_keys then
+ local homedir = nuage.adduser(default_user)
+ for _,k in ipairs(obj.ssh_authorized_keys) do
+ nuage.addsshkey(homedir, k)
+ end
+ end
+ if obj.network then
+ local ifaces = get_ifaces()
+ nuage.mkdir_p(root .. "/etc/rc.conf.d")
+ local network = open_config("network")
+ local routing = open_config("routing")
+ local ipv6={}
+ for _,v in pairs(obj.network.ethernets) do
+ if not v.match then goto next end
+ if not v.match.macaddress then goto next end
+ if not ifaces[v.match.macaddress] then
+ nuage.warn("nuageinit: not interface matching: "..v.match.macaddress)
+ goto next
+ end
+ local interface = ifaces[v.match.macaddress]
+ if v.dhcp4 then
+ network:write("ifconfig_"..interface.."=\"DHCP\"\n")
+ elseif v.addresses then
+ for _,a in pairs(v.addresses) do
+ if a:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)") then
+ network:write("ifconfig_"..interface.."=\"inet "..a.."\"\n")
+ else
+ network:write("ifconfig_"..interface.."_ipv6=\"inet6 "..a.."\"\n")
+ ipv6[#ipv6 +1] = interface
+ end
+ end
+ end
+ if v.gateway4 then
+ routing:write("defaultrouter=\""..v.gateway4.."\"\n")
+ end
+ if v.gateway6 then
+ routing:write("ipv6_defaultrouter=\""..v.gateway6.."\"\n")
+ routing:write("ipv6_route_"..interface.."=\""..v.gateway6)
+ routing:write(" -prefixlen 128 -interface "..interface.."\"\n")
+ end
+ ::next::
+ end
+ if #ipv6 > 0 then
+ network:write("ipv6_network_interfaces=\"")
+ network:write(table.concat(ipv6, " ") .. "\"\n")
+ network:write("ipv6_default_interface=\""..ipv6[1].."\"\n")
+ end
+ network:close()
+ routing:close()
+ end
+else
+ local res,err = os.execute(path..'/user-data')
+ if not res then
+ nuage.err("nuageinit: error executing user-data script: ".. err)
+ end
+end