Merge pull request #1828 from dibdot/lxc_fix

luci-app-lxc: fix "plain-vanilla" integration, part 2
This commit is contained in:
Dirk Brenken 2018-05-27 20:52:31 +02:00 committed by GitHub
commit 31dfa80d4f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 92 additions and 71 deletions

View file

@ -14,73 +14,43 @@ Author: Petar Koretic <petar.koretic@sartura.hr>
]]-- ]]--
module("luci.controller.lxc", package.seeall)
local uci = require "luci.model.uci".cursor() local uci = require "luci.model.uci".cursor()
local util = require "luci.util" local util = require "luci.util"
local fs = require "nixio" local fs = require "nixio"
module("luci.controller.lxc", package.seeall)
function fork_exec(command)
local pid = fs.fork()
if pid > 0 then
return
elseif pid == 0 then
-- change to root dir
fs.chdir("/")
-- patch stdin, out, err to /dev/null
local null = fs.open("/dev/null", "w+")
if null then
fs.dup(null, fs.stderr)
fs.dup(null, fs.stdout)
fs.dup(null, fs.stdin)
if null:fileno() > 2 then
null:close()
end
end
-- replace with target command
fs.exec("/bin/sh", "-c", command)
end
end
function index() function index()
if not nixio.fs.access("/etc/config/lxc") then
return
end
page = node("admin", "services", "lxc") page = node("admin", "services", "lxc")
page.target = cbi("lxc") page.target = cbi("lxc")
page.title = _("LXC Containers") page.title = _("LXC Containers")
page.order = 70 page.order = 70
page = entry({"admin", "services", "lxc_create"}, call("lxc_create"), nil) entry({"admin", "services", "lxc_create"}, call("lxc_create"), nil).leaf = true
page.leaf = true entry({"admin", "services", "lxc_action"}, call("lxc_action"), nil).leaf = true
entry({"admin", "services", "lxc_get_downloadable"}, call("lxc_get_downloadable"), nil).leaf = true
page = entry({"admin", "services", "lxc_action"}, call("lxc_action"), nil) entry({"admin", "services", "lxc_configuration_get"}, call("lxc_configuration_get"), nil).leaf = true
page.leaf = true entry({"admin", "services", "lxc_configuration_set"}, call("lxc_configuration_set"), nil).leaf = true
page = entry({"admin", "services", "lxc_get_downloadable"}, call("lxc_get_downloadable"), nil)
page.leaf = true
page = entry({"admin", "services", "lxc_configuration_get"}, call("lxc_configuration_get"), nil)
page.leaf = true
page = entry({"admin", "services", "lxc_configuration_set"}, call("lxc_configuration_set"), nil)
page.leaf = true
end end
function lxc_get_downloadable() function lxc_get_downloadable()
local target = lxc_get_arch_target() local target = lxc_get_arch_target()
local templates = {} local templates = {}
local ssl_status = lxc_get_ssl_status()
local f = io.popen('sh /usr/share/lxc/templates/lxc-download --list --no-validate --server %s 2>/dev/null' local f = io.popen('sh /usr/share/lxc/templates/lxc-download --list %s --server %s 2>/dev/null'
% util.shellquote(uci:get("lxc", "lxc", "url")), 'r') %{ ssl_status, util.shellquote(uci:get("lxc", "lxc", "url")) }, 'r')
local line local line
for line in f:lines() do for line in f:lines() do
local dist, version, dist_target = line:match("^(%S+)%s+(%S+)%s+(%S+)%s+default%s+%S+$") local dist, version, dist_target = line:match("^(%S+)%s+(%S+)%s+(%S+)%s+default%s+%S+$")
if dist and version and dist_target == target then if dist and version and dist_target and dist_target == target then
templates[#templates+1] = "%s:%s" %{ dist, version } templates[#templates+1] = "%s:%s" %{ dist, version }
end end
end end
f:close() f:close()
luci.http.prepare_content("application/json") luci.http.prepare_content("application/json")
@ -90,23 +60,26 @@ end
function lxc_create(lxc_name, lxc_template) function lxc_create(lxc_name, lxc_template)
luci.http.prepare_content("text/plain") luci.http.prepare_content("text/plain")
if not pcall(dofile, "/etc/openwrt_release") then local check = lxc_get_config_path()
return luci.http.write("1") if not check then
return
end end
local lxc_dist, lxc_release = lxc_template:match("^(.+):(.+)$") local ssl_status = lxc_get_ssl_status()
local src_err
local lxc_dist, lxc_release = lxc_template:match("^(.+):(.+)$")
luci.http.write(util.ubus("lxc", "create", { luci.http.write(util.ubus("lxc", "create", {
name = lxc_name, name = lxc_name,
template = "download", template = "download",
args = { args = {
"--server", uci:get("lxc", "lxc", "url"), "--server", uci:get("lxc", "lxc", "url"),
"--no-validate",
"--dist", lxc_dist, "--dist", lxc_dist,
"--release", lxc_release, "--release", lxc_release,
"--arch", lxc_get_arch_target() "--arch", lxc_get_arch_target(),
ssl_status
} }
})) }), src_err)
end end
function lxc_action(lxc_action, lxc_name) function lxc_action(lxc_action, lxc_name)
@ -123,9 +96,25 @@ function lxc_get_config_path()
local ret = content:match('^%s*lxc.lxcpath%s*=%s*([^%s]*)') local ret = content:match('^%s*lxc.lxcpath%s*=%s*([^%s]*)')
if ret then if ret then
if nixio.fs.access(ret) then
local min_space = tonumber(uci:get("lxc", "lxc", "min_space")) or 100000
local free_space = tonumber(util.exec("df " ..ret.. " | awk '{if(NR==2)print $4}'"))
if free_space and free_space >= min_space then
local min_temp = tonumber(uci:get("lxc", "lxc", "min_temp")) or 100000
local free_temp = tonumber(util.exec("df /tmp | awk '{if(NR==2)print $4}'"))
if free_temp and free_temp >= min_temp then
return ret .. "/" return ret .. "/"
else else
return "/srv/lxc/" util.perror("lxc error: not enough temporary space (< " ..min_temp.. " KB)")
end
else
util.perror("lxc error: not enough space (< " ..min_space.. " KB)")
end
else
util.perror("lxc error: directory not found")
end
else
util.perror("lxc error: config path is empty")
end end
end end
@ -143,14 +132,15 @@ function lxc_configuration_set(lxc_name)
luci.http.prepare_content("text/plain") luci.http.prepare_content("text/plain")
local lxc_configuration = luci.http.formvalue("lxc_configuration") local lxc_configuration = luci.http.formvalue("lxc_configuration")
if lxc_configuration == nil then if lxc_configuration == nil then
return luci.http.write("1") util.perror("lxc error: config formvalue is empty")
return
end end
local f, err = io.open(lxc_get_config_path() .. lxc_name .. "/config","w+") local f, err = io.open(lxc_get_config_path() .. lxc_name .. "/config","w+")
if not f then if not f then
return luci.http.write("2") util.perror("lxc error: config file not found")
return
end end
f:write(lxc_configuration) f:write(lxc_configuration)
@ -168,13 +158,21 @@ function lxc_get_arch_target()
armv8 = "arm64", armv8 = "arm64",
x86_64 = "amd64" x86_64 = "amd64"
} }
local k, v local k, v
for k, v in pairs(target_map) do for k, v in pairs(target_map) do
if target:find(k) then if target:find("^" ..k.. "$") then
return v return v
end end
end end
return target return target
end end
function lxc_get_ssl_status()
local ssl_enabled = uci:get("lxc", "lxc", "ssl_enabled")
local ssl_status = "--no-validate"
if ssl_enabled and ssl_enabled == "1" then
ssl_status = ""
end
return ssl_status
end

View file

@ -14,20 +14,35 @@ Author: Petar Koretic <petar.koretic@sartura.hr>
]]-- ]]--
local fs = require "nixio.fs"
m = Map("lxc", translate("LXC Containers"), m = Map("lxc", translate("LXC Containers"),
translate("<b>Please note:</b> For LXC Containers you need a custom OpenWrt image.<br />") translate("<b>Please note:</b> For LXC Containers you need a custom OpenWrt image.<br />")
.. translate("The image should include at least support for 'kernel cgroups', 'kernel namespaces' and 'miscellaneous LXC related options'.")) .. translate("The image should include at least support for 'kernel cgroups', 'kernel namespaces' and 'miscellaneous LXC related options'."))
m:section(SimpleSection).template = "lxc"
if fs.access("/etc/config/lxc") then s = m:section(TypedSection, "lxc", translate("Options"))
m:section(SimpleSection).template = "lxc" s.anonymous = true
s = m:section(TypedSection, "lxc", translate("Options")) o1 = s:option(Value, "url", translate("Containers URL"))
s.anonymous = true o1:value("images.linuxcontainers.org")
s.addremove = false o1:value("repo.turris.cz/lxc", "repo.turris.cz/lxc (SSL req.)")
o1.default = "images.linuxcontainers.org"
o1.rmempty = false
s:option(Value, "url", translate("Containers URL")) o2 = s:option(Flag, "ssl_enabled", translate("Enable SSL"),
end translate("Enable optional SSL encryption support. This requires additional packages like 'wget', 'ca-certificates', 'gnupg' and 'gnupg-utils'."))
o2.default = o2.disabled
o2.rmempty = false
o3 = s:option(Value, "min_space", translate("Free Space Threshold"),
translate("Minimum required free space for LXC Container creation in KB"))
o3.default = "100000"
o3.datatype = "min(50000)"
o3.rmempty = false
o4 = s:option(Value, "min_temp", translate("Free Temp Threshold"),
translate("Minimum required free temp space for LXC Container creation in KB"))
o4.default = "100000"
o4.datatype = "min(50000)"
o4.rmempty = false
return m return m

View file

@ -107,7 +107,7 @@ table.cbi-section-table td,
function lxc_create(tr) function lxc_create(tr)
{ {
var lxc_name = tr.querySelector("#tx_name").value.replace(/\s/g,'') var lxc_name = tr.querySelector("#tx_name").value.replace(/[\s!@#$%^&*()+=\[\]{};':"\\|,<>\/?]/g,'')
var lxc_template = tr.querySelector("#s_template").value var lxc_template = tr.querySelector("#s_template").value
var bt_create = tr.querySelector("#bt_create") var bt_create = tr.querySelector("#bt_create")
@ -224,6 +224,14 @@ table.cbi-section-table td,
else if (action == "destroy") else if (action == "destroy")
{ {
var tr = self.parentNode.parentNode
var img = tr.querySelector('img')
if (img.getAttribute('src') != window.img["red"])
{
bt_action.disabled = false
return info_message(output_list,"Container is still running!")
}
if (!confirm("This will completely remove a stopped LXC container from disk. Are you sure?")) if (!confirm("This will completely remove a stopped LXC container from disk. Are you sure?"))
return return
@ -396,7 +404,7 @@ table.cbi-section-table td,
row.id = 'tr_holder' row.id = 'tr_holder'
var cell = row.insertCell(0); var cell = row.insertCell(0);
cell.colSpan = 3; cell.colSpan = 3;
cell.innerHTML = '<em><br />There are no templates for your architecture (<%=target%>) available, please select another Containers URL.</em>'; cell.innerHTML = '<em><br />There are no templates for your architecture (<%=target%>) available, please select another containers URL.</em>';
} }
function lxc_list_update() function lxc_list_update()