summaryrefslogtreecommitdiffhomepage
path: root/applications/luci-app-dockerman/luasrc/model
diff options
context:
space:
mode:
authorFlorian Eckert <fe@dev.tdt.de>2020-07-30 08:07:08 +0200
committerGitHub <noreply@github.com>2020-07-30 08:07:08 +0200
commit8f54db8cc34c1ef1b8c4a2c691f69e0df13bfde9 (patch)
tree465039082c7aaba97b3b6f247b1125a9b2ec3e89 /applications/luci-app-dockerman/luasrc/model
parent815d255276c0b5b9ba13aca15a3994c206adbc77 (diff)
parentc724c20b83ce5b6495d64c2cfce32041a2191c49 (diff)
Merge pull request #4283 from TDT-AG/pr/20200717-luci-app-dockerman
luci-app-dockerman: refactoring and update coding style
Diffstat (limited to 'applications/luci-app-dockerman/luasrc/model')
-rw-r--r--applications/luci-app-dockerman/luasrc/model/cbi/dockerman/container.lua1237
-rw-r--r--applications/luci-app-dockerman/luasrc/model/cbi/dockerman/containers.lua360
-rw-r--r--applications/luci-app-dockerman/luasrc/model/cbi/dockerman/images.lua431
-rw-r--r--applications/luci-app-dockerman/luasrc/model/cbi/dockerman/networks.lua234
-rw-r--r--applications/luci-app-dockerman/luasrc/model/cbi/dockerman/newcontainer.lua1447
-rw-r--r--applications/luci-app-dockerman/luasrc/model/cbi/dockerman/newnetwork.lua411
-rw-r--r--applications/luci-app-dockerman/luasrc/model/cbi/dockerman/overview.lua237
-rw-r--r--applications/luci-app-dockerman/luasrc/model/cbi/dockerman/volumes.lua208
-rw-r--r--applications/luci-app-dockerman/luasrc/model/docker.lua727
9 files changed, 2990 insertions, 2302 deletions
diff --git a/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/container.lua b/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/container.lua
index 7c0c969336..9ea006396a 100644
--- a/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/container.lua
+++ b/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/container.lua
@@ -4,585 +4,778 @@ Copyright 2019 lisaac <https://github.com/lisaac/luci-app-dockerman>
]]--
require "luci.util"
+
local docker = require "luci.model.docker"
local dk = docker.new()
+
container_id = arg[1]
local action = arg[2] or "info"
-local images, networks, container_info
-if not container_id then return end
-local res = dk.containers:inspect({id = container_id})
-if res.code < 300 then container_info = res.body else return end
+local m, s, o
+local images, networks, container_info, res
+
+if not container_id then
+ return
+end
+
+res = dk.containers:inspect({id = container_id})
+if res.code < 300 then
+ container_info = res.body
+else
+ return
+end
+
res = dk.networks:list()
-if res.code < 300 then networks = res.body else return end
+if res.code < 300 then
+ networks = res.body
+else
+ return
+end
local get_ports = function(d)
- local data
- if d.HostConfig and d.HostConfig.PortBindings then
- for inter, out in pairs(d.HostConfig.PortBindings) do
- data = (data and (data .. "<br>") or "") .. out[1]["HostPort"] .. ":" .. inter
- end
- end
- return data
+ local data
+
+ if d.HostConfig and d.HostConfig.PortBindings then
+ for inter, out in pairs(d.HostConfig.PortBindings) do
+ data = (data and (data .. "<br>") or "") .. out[1]["HostPort"] .. ":" .. inter
+ end
+ end
+
+ return data
end
local get_env = function(d)
- local data
- if d.Config and d.Config.Env then
- for _,v in ipairs(d.Config.Env) do
- data = (data and (data .. "<br>") or "") .. v
- end
- end
- return data
+ local data
+
+ if d.Config and d.Config.Env then
+ for _,v in ipairs(d.Config.Env) do
+ data = (data and (data .. "<br>") or "") .. v
+ end
+ end
+
+ return data
end
local get_command = function(d)
- local data
- if d.Config and d.Config.Cmd then
- for _,v in ipairs(d.Config.Cmd) do
- data = (data and (data .. " ") or "") .. v
- end
- end
- return data
+ local data
+
+ if d.Config and d.Config.Cmd then
+ for _,v in ipairs(d.Config.Cmd) do
+ data = (data and (data .. " ") or "") .. v
+ end
+ end
+
+ return data
end
local get_mounts = function(d)
- local data
- if d.Mounts then
- for _,v in ipairs(d.Mounts) do
- local v_sorce_d, v_dest_d
- local v_sorce = ""
- local v_dest = ""
- for v_sorce_d in v["Source"]:gmatch('[^/]+') do
- if v_sorce_d and #v_sorce_d > 12 then
- v_sorce = v_sorce .. "/" .. v_sorce_d:sub(1,12) .. "..."
- else
- v_sorce = v_sorce .."/".. v_sorce_d
- end
- end
- for v_dest_d in v["Destination"]:gmatch('[^/]+') do
- if v_dest_d and #v_dest_d > 12 then
- v_dest = v_dest .. "/" .. v_dest_d:sub(1,12) .. "..."
- else
- v_dest = v_dest .."/".. v_dest_d
- end
- end
- data = (data and (data .. "<br>") or "") .. v_sorce .. ":" .. v["Destination"] .. (v["Mode"] ~= "" and (":" .. v["Mode"]) or "")
- end
- end
- return data
+ local data
+
+ if d.Mounts then
+ for _,v in ipairs(d.Mounts) do
+ local v_sorce_d, v_dest_d
+ local v_sorce = ""
+ local v_dest = ""
+ for v_sorce_d in v["Source"]:gmatch('[^/]+') do
+ if v_sorce_d and #v_sorce_d > 12 then
+ v_sorce = v_sorce .. "/" .. v_sorce_d:sub(1,12) .. "..."
+ else
+ v_sorce = v_sorce .."/".. v_sorce_d
+ end
+ end
+ for v_dest_d in v["Destination"]:gmatch('[^/]+') do
+ if v_dest_d and #v_dest_d > 12 then
+ v_dest = v_dest .. "/" .. v_dest_d:sub(1,12) .. "..."
+ else
+ v_dest = v_dest .."/".. v_dest_d
+ end
+ end
+ data = (data and (data .. "<br>") or "") .. v_sorce .. ":" .. v["Destination"] .. (v["Mode"] ~= "" and (":" .. v["Mode"]) or "")
+ end
+ end
+
+ return data
end
local get_device = function(d)
- local data
- if d.HostConfig and d.HostConfig.Devices then
- for _,v in ipairs(d.HostConfig.Devices) do
- data = (data and (data .. "<br>") or "") .. v["PathOnHost"] .. ":" .. v["PathInContainer"] .. (v["CgroupPermissions"] ~= "" and (":" .. v["CgroupPermissions"]) or "")
- end
- end
- return data
+ local data
+
+ if d.HostConfig and d.HostConfig.Devices then
+ for _,v in ipairs(d.HostConfig.Devices) do
+ data = (data and (data .. "<br>") or "") .. v["PathOnHost"] .. ":" .. v["PathInContainer"] .. (v["CgroupPermissions"] ~= "" and (":" .. v["CgroupPermissions"]) or "")
+ end
+ end
+
+ return data
end
local get_links = function(d)
- local data
- if d.HostConfig and d.HostConfig.Links then
- for _,v in ipairs(d.HostConfig.Links) do
- data = (data and (data .. "<br>") or "") .. v
- end
- end
- return data
+ local data
+
+ if d.HostConfig and d.HostConfig.Links then
+ for _,v in ipairs(d.HostConfig.Links) do
+ data = (data and (data .. "<br>") or "") .. v
+ end
+ end
+
+ return data
end
local get_tmpfs = function(d)
- local data
- if d.HostConfig and d.HostConfig.Tmpfs then
- for k, v in pairs(d.HostConfig.Tmpfs) do
- data = (data and (data .. "<br>") or "") .. k .. (v~="" and ":" or "")..v
- end
- end
- return data
+ local data
+
+ if d.HostConfig and d.HostConfig.Tmpfs then
+ for k, v in pairs(d.HostConfig.Tmpfs) do
+ data = (data and (data .. "<br>") or "") .. k .. (v~="" and ":" or "")..v
+ end
+ end
+
+ return data
end
local get_dns = function(d)
- local data
- if d.HostConfig and d.HostConfig.Dns then
- for _, v in ipairs(d.HostConfig.Dns) do
- data = (data and (data .. "<br>") or "") .. v
- end
- end
- return data
+ local data
+
+ if d.HostConfig and d.HostConfig.Dns then
+ for _, v in ipairs(d.HostConfig.Dns) do
+ data = (data and (data .. "<br>") or "") .. v
+ end
+ end
+
+ return data
end
local get_sysctl = function(d)
- local data
- if d.HostConfig and d.HostConfig.Sysctls then
- for k, v in pairs(d.HostConfig.Sysctls) do
- data = (data and (data .. "<br>") or "") .. k..":"..v
- end
- end
- return data
+ local data
+
+ if d.HostConfig and d.HostConfig.Sysctls then
+ for k, v in pairs(d.HostConfig.Sysctls) do
+ data = (data and (data .. "<br>") or "") .. k..":"..v
+ end
+ end
+
+ return data
end
local get_networks = function(d)
- local data={}
- if d.NetworkSettings and d.NetworkSettings.Networks and type(d.NetworkSettings.Networks) == "table" then
- for k,v in pairs(d.NetworkSettings.Networks) do
- data[k] = v.IPAddress or ""
- end
- end
- return data
+ local data={}
+
+ if d.NetworkSettings and d.NetworkSettings.Networks and type(d.NetworkSettings.Networks) == "table" then
+ for k,v in pairs(d.NetworkSettings.Networks) do
+ data[k] = v.IPAddress or ""
+ end
+ end
+
+ return data
end
local start_stop_remove = function(m, cmd)
- docker:clear_status()
- docker:append_status("Containers: " .. cmd .. " " .. container_id .. "...")
- local res
- if cmd ~= "upgrade" then
- res = dk.containers[cmd](dk, {id = container_id})
- else
- res = dk.containers_upgrade(dk, {id = container_id})
- end
- if res and res.code >= 300 then
- docker:append_status("code:" .. res.code.." ".. (res.body.message and res.body.message or res.message))
- luci.http.redirect(luci.dispatcher.build_url("admin/docker/container/"..container_id))
- else
- docker:clear_status()
- if cmd ~= "remove" and cmd ~= "upgrade" then
- luci.http.redirect(luci.dispatcher.build_url("admin/docker/container/"..container_id))
- else
- luci.http.redirect(luci.dispatcher.build_url("admin/docker/containers"))
- end
- end
+ local res
+
+ docker:clear_status()
+ docker:append_status("Containers: " .. cmd .. " " .. container_id .. "...")
+
+ if cmd ~= "upgrade" then
+ res = dk.containers[cmd](dk, {id = container_id})
+ else
+ res = dk.containers_upgrade(dk, {id = container_id})
+ end
+
+ if res and res.code >= 300 then
+ docker:append_status("code:" .. res.code.." ".. (res.body.message and res.body.message or res.message))
+ luci.http.redirect(luci.dispatcher.build_url("admin/docker/container/"..container_id))
+ else
+ docker:clear_status()
+ if cmd ~= "remove" and cmd ~= "upgrade" then
+ luci.http.redirect(luci.dispatcher.build_url("admin/docker/container/"..container_id))
+ else
+ luci.http.redirect(luci.dispatcher.build_url("admin/docker/containers"))
+ end
+ end
end
m=SimpleForm("docker", container_info.Name:sub(2), translate("Docker Container") )
m.redirect = luci.dispatcher.build_url("admin/docker/containers")
--- m:append(Template("dockerman/container"))
-docker_status = m:section(SimpleSection)
-docker_status.template = "dockerman/apply_widget"
-docker_status.err=docker:read_status()
-docker_status.err=docker_status.err and docker_status.err:gsub("\n","<br>"):gsub(" ","&nbsp;")
-if docker_status.err then docker:clear_status() end
-
-
-action_section = m:section(Table,{{}})
-action_section.notitle=true
-action_section.rowcolors=false
-action_section.template = "cbi/nullsection"
-
-btnstart=action_section:option(Button, "_start")
-btnstart.template = "dockerman/cbi/inlinebutton"
-btnstart.inputtitle=translate("Start")
-btnstart.inputstyle = "apply"
-btnstart.forcewrite = true
-btnrestart=action_section:option(Button, "_restart")
-btnrestart.template = "dockerman/cbi/inlinebutton"
-btnrestart.inputtitle=translate("Restart")
-btnrestart.inputstyle = "reload"
-btnrestart.forcewrite = true
-btnstop=action_section:option(Button, "_stop")
-btnstop.template = "dockerman/cbi/inlinebutton"
-btnstop.inputtitle=translate("Stop")
-btnstop.inputstyle = "reset"
-btnstop.forcewrite = true
-btnkill=action_section:option(Button, "_kill")
-btnkill.template = "dockerman/cbi/inlinebutton"
-btnkill.inputtitle=translate("Kill")
-btnkill.inputstyle = "reset"
-btnkill.forcewrite = true
-btnupgrade=action_section:option(Button, "_upgrade")
-btnupgrade.template = "dockerman/cbi/inlinebutton"
-btnupgrade.inputtitle=translate("Upgrade")
-btnupgrade.inputstyle = "reload"
-btnstop.forcewrite = true
-btnduplicate=action_section:option(Button, "_duplicate")
-btnduplicate.template = "dockerman/cbi/inlinebutton"
-btnduplicate.inputtitle=translate("Duplicate/Edit")
-btnduplicate.inputstyle = "add"
-btnstop.forcewrite = true
-btnremove=action_section:option(Button, "_remove")
-btnremove.template = "dockerman/cbi/inlinebutton"
-btnremove.inputtitle=translate("Remove")
-btnremove.inputstyle = "remove"
-btnremove.forcewrite = true
-
-btnstart.write = function(self, section)
- start_stop_remove(m,"start")
+
+s = m:section(SimpleSection)
+s.template = "dockerman/apply_widget"
+s.err=docker:read_status()
+s.err=s.err and s.err:gsub("\n","<br>"):gsub(" ","&nbsp;")
+if s.err then
+ docker:clear_status()
+end
+
+s = m:section(Table,{{}})
+s.notitle=true
+s.rowcolors=false
+s.template = "cbi/nullsection"
+
+o = s:option(Button, "_start")
+o.template = "dockerman/cbi/inlinebutton"
+o.inputtitle=translate("Start")
+o.inputstyle = "apply"
+o.forcewrite = true
+o.write = function(self, section)
+ start_stop_remove(m,"start")
end
-btnrestart.write = function(self, section)
- start_stop_remove(m,"restart")
+
+o = s:option(Button, "_restart")
+o.template = "dockerman/cbi/inlinebutton"
+o.inputtitle=translate("Restart")
+o.inputstyle = "reload"
+o.forcewrite = true
+o.write = function(self, section)
+ start_stop_remove(m,"restart")
end
-btnupgrade.write = function(self, section)
- start_stop_remove(m,"upgrade")
+
+o = s:option(Button, "_stop")
+o.template = "dockerman/cbi/inlinebutton"
+o.inputtitle=translate("Stop")
+o.inputstyle = "reset"
+o.forcewrite = true
+o.write = function(self, section)
+ start_stop_remove(m,"stop")
end
-btnremove.write = function(self, section)
- start_stop_remove(m,"remove")
+
+o = s:option(Button, "_kill")
+o.template = "dockerman/cbi/inlinebutton"
+o.inputtitle=translate("Kill")
+o.inputstyle = "reset"
+o.forcewrite = true
+o.write = function(self, section)
+ start_stop_remove(m,"kill")
end
-btnstop.write = function(self, section)
- start_stop_remove(m,"stop")
+
+o = s:option(Button, "_upgrade")
+o.template = "dockerman/cbi/inlinebutton"
+o.inputtitle=translate("Upgrade")
+o.inputstyle = "reload"
+o.forcewrite = true
+o.write = function(self, section)
+ start_stop_remove(m,"upgrade")
end
-btnkill.write = function(self, section)
- start_stop_remove(m,"kill")
+
+o = s:option(Button, "_duplicate")
+o.template = "dockerman/cbi/inlinebutton"
+o.inputtitle=translate("Duplicate/Edit")
+o.inputstyle = "add"
+o.forcewrite = true
+o.write = function(self, section)
+ luci.http.redirect(luci.dispatcher.build_url("admin/docker/newcontainer/duplicate/"..container_id))
end
-btnduplicate.write = function(self, section)
- luci.http.redirect(luci.dispatcher.build_url("admin/docker/newcontainer/duplicate/"..container_id))
+
+o = s:option(Button, "_remove")
+o.template = "dockerman/cbi/inlinebutton"
+o.inputtitle=translate("Remove")
+o.inputstyle = "remove"
+o.forcewrite = true
+o.write = function(self, section)
+ start_stop_remove(m,"remove")
end
-tab_section = m:section(SimpleSection)
-tab_section.template = "dockerman/container"
-
-if action == "info" then
- m.submit = false
- m.reset = false
- table_info = {
- ["01name"] = {_key = translate("Name"), _value = container_info.Name:sub(2) or "-", _button=translate("Update")},
- ["02id"] = {_key = translate("ID"), _value = container_info.Id or "-"},
- ["03image"] = {_key = translate("Image"), _value = container_info.Config.Image .. "<br>" .. container_info.Image},
- ["04status"] = {_key = translate("Status"), _value = container_info.State and container_info.State.Status or "-"},
- ["05created"] = {_key = translate("Created"), _value = container_info.Created or "-"},
- }
- table_info["06start"] = container_info.State.Status == "running" and {_key = translate("Start Time"), _value = container_info.State and container_info.State.StartedAt or "-"} or {_key = translate("Finish Time"), _value = container_info.State and container_info.State.FinishedAt or "-"}
- table_info["07healthy"] = {_key = translate("Healthy"), _value = container_info.State and container_info.State.Health and container_info.State.Health.Status or "-"}
- table_info["08restart"] = {_key = translate("Restart Policy"), _value = container_info.HostConfig and container_info.HostConfig.RestartPolicy and container_info.HostConfig.RestartPolicy.Name or "-", _button=translate("Update")}
- table_info["081user"] = {_key = translate("User"), _value = container_info.Config and (container_info.Config.User ~="" and container_info.Config.User or "-") or "-"}
- table_info["09mount"] = {_key = translate("Mount/Volume"), _value = get_mounts(container_info) or "-"}
- table_info["10cmd"] = {_key = translate("Command"), _value = get_command(container_info) or "-"}
- table_info["11env"] = {_key = translate("Env"), _value = get_env(container_info) or "-"}
- table_info["12ports"] = {_key = translate("Ports"), _value = get_ports(container_info) or "-"}
- table_info["13links"] = {_key = translate("Links"), _value = get_links(container_info) or "-"}
- table_info["14device"] = {_key = translate("Device"), _value = get_device(container_info) or "-"}
- table_info["15tmpfs"] = {_key = translate("Tmpfs"), _value = get_tmpfs(container_info) or "-"}
- table_info["16dns"] = {_key = translate("DNS"), _value = get_dns(container_info) or "-"}
- table_info["17sysctl"] = {_key = translate("Sysctl"), _value = get_sysctl(container_info) or "-"}
- info_networks = get_networks(container_info)
- list_networks = {}
- for _, v in ipairs (networks) do
- if v.Name then
- local parent = v.Options and v.Options.parent or nil
- local ip = v.IPAM and v.IPAM.Config and v.IPAM.Config[1] and v.IPAM.Config[1].Subnet or nil
- ipv6 = v.IPAM and v.IPAM.Config and v.IPAM.Config[2] and v.IPAM.Config[2].Subnet or nil
- local network_name = v.Name .. " | " .. v.Driver .. (parent and (" | " .. parent) or "") .. (ip and (" | " .. ip) or "").. (ipv6 and (" | " .. ipv6) or "")
- list_networks[v.Name] = network_name
- end
- end
-
- if type(info_networks)== "table" then
- for k,v in pairs(info_networks) do
- table_info["14network"..k] = {
- _key = translate("Network"), _value = k.. (v~="" and (" | ".. v) or ""), _button=translate("Disconnect")
- }
- list_networks[k]=nil
- end
- end
-
- table_info["15connect"] = {_key = translate("Connect Network"), _value = list_networks ,_opts = "", _button=translate("Connect")}
-
-
- d_info = m:section(Table,table_info)
- d_info.nodescr=true
- d_info.formvalue=function(self, section)
- return table_info
- end
- dv_key = d_info:option(DummyValue, "_key", translate("Info"))
- dv_key.width = "20%"
- dv_value = d_info:option(ListValue, "_value")
- dv_value.render = function(self, section, scope)
- if table_info[section]._key == translate("Name") then
- self:reset_values()
- self.template = "cbi/value"
- self.size = 30
- self.keylist = {}
- self.vallist = {}
- self.default=table_info[section]._value
- Value.render(self, section, scope)
- elseif table_info[section]._key == translate("Restart Policy") then
- self.template = "cbi/lvalue"
- self:reset_values()
- self.size = nil
- self:value("no", "No")
- self:value("unless-stopped", "Unless stopped")
- self:value("always", "Always")
- self:value("on-failure", "On failure")
- self.default=table_info[section]._value
- ListValue.render(self, section, scope)
- elseif table_info[section]._key == translate("Connect Network") then
- self.template = "cbi/lvalue"
- self:reset_values()
- self.size = nil
- for k,v in pairs(list_networks) do
- if k ~= "host" then
- self:value(k,v)
- end
- end
- self.default=table_info[section]._value
- ListValue.render(self, section, scope)
- else
- self:reset_values()
- self.rawhtml=true
- self.template = "cbi/dvalue"
- self.default=table_info[section]._value
- DummyValue.render(self, section, scope)
- end
- end
- dv_value.forcewrite = true -- for write function using simpleform
- dv_value.write = function(self, section, value)
- table_info[section]._value=value
- end
- dv_value.validate = function(self, value)
- return value
- end
- dv_opts = d_info:option(Value, "_opts")
- dv_opts.forcewrite = true -- for write function using simpleform
- dv_opts.write = function(self, section, value)
-
- table_info[section]._opts=value
- end
- dv_opts.validate = function(self, value)
- return value
- end
- dv_opts.render = function(self, section, scope)
- if table_info[section]._key==translate("Connect Network") then
- self.template = "cbi/value"
- self.keylist = {}
- self.vallist = {}
- self.placeholder = "10.1.1.254"
- self.datatype = "ip4addr"
- self.default=table_info[section]._opts
- Value.render(self, section, scope)
- else
- self.rawhtml=true
- self.template = "cbi/dvalue"
- self.default=table_info[section]._opts
- DummyValue.render(self, section, scope)
- end
- end
- btn_update = d_info:option(Button, "_button")
- btn_update.forcewrite = true
- btn_update.render = function(self, section, scope)
- if table_info[section]._button and table_info[section]._value ~= nil then
- btn_update.inputtitle=table_info[section]._button
- self.template = "cbi/button"
- self.inputstyle = "edit"
- Button.render(self, section, scope)
- else
- self.template = "cbi/dvalue"
- self.default=""
- DummyValue.render(self, section, scope)
- end
- end
- btn_update.write = function(self, section, value)
- local res
- docker:clear_status()
- if section == "01name" then
- docker:append_status("Containers: rename " .. container_id .. "...")
- local new_name = table_info[section]._value
- res = dk.containers:rename({id = container_id, query = {name=new_name}})
- elseif section == "08restart" then
- docker:append_status("Containers: update " .. container_id .. "...")
- local new_restart = table_info[section]._value
- res = dk.containers:update({id = container_id, body = {RestartPolicy = {Name = new_restart}}})
- elseif table_info[section]._key == translate("Network") then
- local _,_,leave_network = table_info[section]._value:find("(.-) | .+")
- leave_network = leave_network or table_info[section]._value
- docker:append_status("Network: disconnect " .. leave_network .. container_id .. "...")
- res = dk.networks:disconnect({name = leave_network, body = {Container = container_id}})
- elseif section == "15connect" then
- local connect_network = table_info[section]._value
- local network_opiton
- if connect_network ~= "none" and connect_network ~= "bridge" and connect_network ~= "host" then
- network_opiton = table_info[section]._opts ~= "" and {
- IPAMConfig={
- IPv4Address=table_info[section]._opts
- }
- } or nil
- end
- docker:append_status("Network: connect " .. connect_network .. container_id .. "...")
- res = dk.networks:connect({name = connect_network, body = {Container = container_id, EndpointConfig= network_opiton}})
- end
- if res and res.code > 300 then
- docker:append_status("code:" .. res.code.." ".. (res.body.message and res.body.message or res.message))
- else
- docker:clear_status()
- end
- luci.http.redirect(luci.dispatcher.build_url("admin/docker/container/"..container_id.."/info"))
- end
-
--- info end
+s = m:section(SimpleSection)
+s.template = "dockerman/container"
+
+if action == "info" then
+ m.submit = false
+ m.reset = false
+ table_info = {
+ ["01name"] = {
+ _key = translate("Name"),
+ _value = container_info.Name:sub(2) or "-",
+ _button=translate("Update")
+ },
+ ["02id"] = {
+ _key = translate("ID"),
+ _value = container_info.Id or "-"
+ },
+ ["03image"] = {
+ _key = translate("Image"),
+ _value = container_info.Config.Image .. "<br>" .. container_info.Image
+ },
+ ["04status"] = {
+ _key = translate("Status"),
+ _value = container_info.State and container_info.State.Status or "-"
+ },
+ ["05created"] = {
+ _key = translate("Created"),
+ _value = container_info.Created or "-"
+ },
+ }
+
+ if container_info.State.Status == "running" then
+ table_info["06start"] = {
+ _key = translate("Start Time"),
+ _value = container_info.State and container_info.State.StartedAt or "-"
+ }
+ else
+ table_info["06start"] = {
+ _key = translate("Finish Time"),
+ _value = container_info.State and container_info.State.FinishedAt or "-"
+ }
+ end
+
+ table_info["07healthy"] = {
+ _key = translate("Healthy"),
+ _value = container_info.State and container_info.State.Health and container_info.State.Health.Status or "-"
+ }
+ table_info["08restart"] = {
+ _key = translate("Restart Policy"),
+ _value = container_info.HostConfig and container_info.HostConfig.RestartPolicy and container_info.HostConfig.RestartPolicy.Name or "-",
+ _button=translate("Update")
+ }
+ table_info["081user"] = {
+ _key = translate("User"),
+ _value = container_info.Config and (container_info.Config.User ~="" and container_info.Config.User or "-") or "-"
+ }
+ table_info["09mount"] = {
+ _key = translate("Mount/Volume"),
+ _value = get_mounts(container_info) or "-"
+ }
+ table_info["10cmd"] = {
+ _key = translate("Command"),
+ _value = get_command(container_info) or "-"
+ }
+ table_info["11env"] = {
+ _key = translate("Env"),
+ _value = get_env(container_info) or "-"
+ }
+ table_info["12ports"] = {
+ _key = translate("Ports"),
+ _value = get_ports(container_info) or "-"
+ }
+ table_info["13links"] = {
+ _key = translate("Links"),
+ _value = get_links(container_info) or "-"
+ }
+ table_info["14device"] = {
+ _key = translate("Device"),
+ _value = get_device(container_info) or "-"
+ }
+ table_info["15tmpfs"] = {
+ _key = translate("Tmpfs"),
+ _value = get_tmpfs(container_info) or "-"
+ }
+ table_info["16dns"] = {
+ _key = translate("DNS"),
+ _value = get_dns(container_info) or "-"
+ }
+ table_info["17sysctl"] = {
+ _key = translate("Sysctl"),
+ _value = get_sysctl(container_info) or "-"
+ }
+
+ info_networks = get_networks(container_info)
+ list_networks = {}
+ for _, v in ipairs (networks) do
+ if v.Name then
+ local parent = v.Options and v.Options.parent or nil
+ local ip = v.IPAM and v.IPAM.Config and v.IPAM.Config[1] and v.IPAM.Config[1].Subnet or nil
+ ipv6 = v.IPAM and v.IPAM.Config and v.IPAM.Config[2] and v.IPAM.Config[2].Subnet or nil
+ local network_name = v.Name .. " | " .. v.Driver .. (parent and (" | " .. parent) or "") .. (ip and (" | " .. ip) or "").. (ipv6 and (" | " .. ipv6) or "")
+ list_networks[v.Name] = network_name
+ end
+ end
+
+ if type(info_networks)== "table" then
+ for k,v in pairs(info_networks) do
+ table_info["14network"..k] = {
+ _key = translate("Network"),
+ value = k.. (v~="" and (" | ".. v) or ""),
+ _button=translate("Disconnect")
+ }
+ list_networks[k]=nil
+ end
+ end
+
+ table_info["15connect"] = {
+ _key = translate("Connect Network"),
+ _value = list_networks ,_opts = "",
+ _button=translate("Connect")
+ }
+
+ s = m:section(Table,table_info)
+ s.nodescr=true
+ s.formvalue=function(self, section)
+ return table_info
+ end
+
+ o = s:option(DummyValue, "_key", translate("Info"))
+ o.width = "20%"
+
+ o = s:option(ListValue, "_value")
+ o.render = function(self, section, scope)
+ if table_info[section]._key == translate("Name") then
+ self:reset_values()
+ self.template = "cbi/value"
+ self.size = 30
+ self.keylist = {}
+ self.vallist = {}
+ self.default=table_info[section]._value
+ Value.render(self, section, scope)
+ elseif table_info[section]._key == translate("Restart Policy") then
+ self.template = "cbi/lvalue"
+ self:reset_values()
+ self.size = nil
+ self:value("no", "No")
+ self:value("unless-stopped", "Unless stopped")
+ self:value("always", "Always")
+ self:value("on-failure", "On failure")
+ self.default=table_info[section]._value
+ ListValue.render(self, section, scope)
+ elseif table_info[section]._key == translate("Connect Network") then
+ self.template = "cbi/lvalue"
+ self:reset_values()
+ self.size = nil
+ for k,v in pairs(list_networks) do
+ if k ~= "host" then
+ self:value(k,v)
+ end
+ end
+ self.default=table_info[section]._value
+ ListValue.render(self, section, scope)
+ else
+ self:reset_values()
+ self.rawhtml=true
+ self.template = "cbi/dvalue"
+ self.default=table_info[section]._value
+ DummyValue.render(self, section, scope)
+ end
+ end
+ o.forcewrite = true
+ o.write = function(self, section, value)
+ table_info[section]._value=value
+ end
+ o.validate = function(self, value)
+ return value
+ end
+
+ o = s:option(Value, "_opts")
+ o.forcewrite = true
+ o.write = function(self, section, value)
+ table_info[section]._opts=value
+ end
+ o.validate = function(self, value)
+ return value
+ end
+ o.render = function(self, section, scope)
+ if table_info[section]._key==translate("Connect Network") then
+ self.template = "cbi/value"
+ self.keylist = {}
+ self.vallist = {}
+ self.placeholder = "10.1.1.254"
+ self.datatype = "ip4addr"
+ self.default=table_info[section]._opts
+ Value.render(self, section, scope)
+ else
+ self.rawhtml=true
+ self.template = "cbi/dvalue"
+ self.default=table_info[section]._opts
+ DummyValue.render(self, section, scope)
+ end
+ end
+
+ o = s:option(Button, "_button")
+ o.forcewrite = true
+ o.render = function(self, section, scope)
+ if table_info[section]._button and table_info[section]._value ~= nil then
+ self.inputtitle=table_info[section]._button
+ self.template = "cbi/button"
+ self.inputstyle = "edit"
+ Button.render(self, section, scope)
+ else
+ self.template = "cbi/dvalue"
+ self.default=""
+ DummyValue.render(self, section, scope)
+ end
+ end
+ o.write = function(self, section, value)
+ local res
+
+ docker:clear_status()
+
+ if section == "01name" then
+ docker:append_status("Containers: rename " .. container_id .. "...")
+ local new_name = table_info[section]._value
+ res = dk.containers:rename({
+ id = container_id,
+ query = {
+ name=new_name
+ }
+ })
+ elseif section == "08restart" then
+ docker:append_status("Containers: update " .. container_id .. "...")
+ local new_restart = table_info[section]._value
+ res = dk.containers:update({
+ id = container_id,
+ body = {
+ RestartPolicy = {
+ Name = new_restart
+ }
+ }
+ })
+ elseif table_info[section]._key == translate("Network") then
+ local _,_,leave_network
+
+ _, _, leave_network = table_info[section]._value:find("(.-) | .+")
+ leave_network = leave_network or table_info[section]._value
+ docker:append_status("Network: disconnect " .. leave_network .. container_id .. "...")
+ res = dk.networks:disconnect({
+ name = leave_network,
+ body = {
+ Container = container_id
+ }
+ })
+ elseif section == "15connect" then
+ local connect_network = table_info[section]._value
+ local network_opiton
+ if connect_network ~= "none"
+ and connect_network ~= "bridge"
+ and connect_network ~= "host" then
+
+ network_opiton = table_info[section]._opts ~= "" and {
+ IPAMConfig={
+ IPv4Address=table_info[section]._opts
+ }
+ } or nil
+ end
+ docker:append_status("Network: connect " .. connect_network .. container_id .. "...")
+ res = dk.networks:connect({
+ name = connect_network,
+ body = {
+ Container = container_id,
+ EndpointConfig= network_opiton
+ }
+ })
+ end
+
+ if res and res.code > 300 then
+ docker:append_status("code:" .. res.code.." ".. (res.body.message and res.body.message or res.message))
+ else
+ docker:clear_status()
+ end
+ luci.http.redirect(luci.dispatcher.build_url("admin/docker/container/"..container_id.."/info"))
+ end
elseif action == "resources" then
- local resources_section= m:section(SimpleSection)
- d = resources_section:option( Value, "cpus", translate("CPUs"), translate("Number of CPUs. Number is a fractional number. 0.000 means no limit."))
- d.placeholder = "1.5"
- d.rmempty = true
- d.datatype="ufloat"
- d.default = container_info.HostConfig.NanoCpus / (10^9)
-
- d = resources_section:option(Value, "cpushares", translate("CPU Shares Weight"), translate("CPU shares relative weight, if 0 is set, the system will ignore the value and use the default of 1024."))
- d.placeholder = "1024"
- d.rmempty = true
- d.datatype="uinteger"
- d.default = container_info.HostConfig.CpuShares
-
- d = resources_section:option(Value, "memory", translate("Memory"), translate("Memory limit (format: <number>[<unit>]). Number is a positive integer. Unit can be one of b, k, m, or g. Minimum is 4M."))
- d.placeholder = "128m"
- d.rmempty = true
- d.default = container_info.HostConfig.Memory ~=0 and ((container_info.HostConfig.Memory / 1024 /1024) .. "M") or 0
-
- d = resources_section:option(Value, "blkioweight", translate("Block IO Weight"), translate("Block IO weight (relative weight) accepts a weight value between 10 and 1000."))
- d.placeholder = "500"
- d.rmempty = true
- d.datatype="uinteger"
- d.default = container_info.HostConfig.BlkioWeight
-
- m.handle = function(self, state, data)
- if state == FORM_VALID then
- local memory = data.memory
- if memory and memory ~= 0 then
- _,_,n,unit = memory:find("([%d%.]+)([%l%u]+)")
- if n then
- unit = unit and unit:sub(1,1):upper() or "B"
- if unit == "M" then
- memory = tonumber(n) * 1024 * 1024
- elseif unit == "G" then
- memory = tonumber(n) * 1024 * 1024 * 1024
- elseif unit == "K" then
- memory = tonumber(n) * 1024
- else
- memory = tonumber(n)
- end
- end
- end
- request_body = {
- BlkioWeight = tonumber(data.blkioweight),
- NanoCPUs = tonumber(data.cpus)*10^9,
- Memory = tonumber(memory),
- CpuShares = tonumber(data.cpushares)
- }
- docker:write_status("Containers: update " .. container_id .. "...")
- local res = dk.containers:update({id = container_id, body = request_body})
- if res and res.code >= 300 then
- docker:append_status("code:" .. res.code.." ".. (res.body.message and res.body.message or res.message))
- else
- docker:clear_status()
- end
- luci.http.redirect(luci.dispatcher.build_url("admin/docker/container/"..container_id.."/resources"))
- end
- end
+ s = m:section(SimpleSection)
+ o = s:option( Value, "cpus",
+ translate("CPUs"),
+ translate("Number of CPUs. Number is a fractional number. 0.000 means no limit."))
+ o.placeholder = "1.5"
+ o.rmempty = true
+ o.datatype="ufloat"
+ o.default = container_info.HostConfig.NanoCpus / (10^9)
+
+ o = s:option(Value, "cpushares",
+ translate("CPU Shares Weight"),
+ translate("CPU shares relative weight, if 0 is set, the system will ignore the value and use the default of 1024."))
+ o.placeholder = "1024"
+ o.rmempty = true
+ o.datatype="uinteger"
+ o.default = container_info.HostConfig.CpuShares
+
+ o = s:option(Value, "memory",
+ translate("Memory"),
+ translate("Memory limit (format: <number>[<unit>]). Number is a positive integer. Unit can be one of b, k, m, or g. Minimum is 4M."))
+ o.placeholder = "128m"
+ o.rmempty = true
+ o.default = container_info.HostConfig.Memory ~=0 and ((container_info.HostConfig.Memory / 1024 /1024) .. "M") or 0
+
+ o = s:option(Value, "blkioweight",
+ translate("Block IO Weight"),
+ translate("Block IO weight (relative weight) accepts a weight value between 10 and 1000."))
+ o.placeholder = "500"
+ o.rmempty = true
+ o.datatype="uinteger"
+ o.default = container_info.HostConfig.BlkioWeight
+
+ m.handle = function(self, state, data)
+ if state == FORM_VALID then
+ local memory = data.memory
+ if memory and memory ~= 0 then
+ _,_,n,unit = memory:find("([%d%.]+)([%l%u]+)")
+ if n then
+ unit = unit and unit:sub(1,1):upper() or "B"
+ if unit == "M" then
+ memory = tonumber(n) * 1024 * 1024
+ elseif unit == "G" then
+ memory = tonumber(n) * 1024 * 1024 * 1024
+ elseif unit == "K" then
+ memory = tonumber(n) * 1024
+ else
+ memory = tonumber(n)
+ end
+ end
+ end
+
+ request_body = {
+ BlkioWeight = tonumber(data.blkioweight),
+ NanoCPUs = tonumber(data.cpus)*10^9,
+ Memory = tonumber(memory),
+ CpuShares = tonumber(data.cpushares)
+ }
+
+ docker:write_status("Containers: update " .. container_id .. "...")
+ local res = dk.containers:update({id = container_id, body = request_body})
+ if res and res.code >= 300 then
+ docker:append_status("code:" .. res.code.." ".. (res.body.message and res.body.message or res.message))
+ else
+ docker:clear_status()
+ end
+ luci.http.redirect(luci.dispatcher.build_url("admin/docker/container/"..container_id.."/resources"))
+ end
+ end
+
elseif action == "file" then
- local filesection= m:section(SimpleSection)
- m.submit = false
- m.reset = false
- filesection.template = "dockerman/container_file"
- filesection.container = container_id
+ s = m:section(SimpleSection)
+ s.template = "dockerman/container_file"
+ s.container = container_id
+ m.submit = false
+ m.reset = false
elseif action == "inspect" then
- local inspectsection= m:section(SimpleSection)
- inspectsection.syslog = luci.jsonc.stringify(container_info, true)
- inspectsection.title = translate("Container Inspect")
- inspectsection.template = "dockerman/logs"
- m.submit = false
- m.reset = false
+ s = m:section(SimpleSection)
+ s.syslog = luci.jsonc.stringify(container_info, true)
+ s.title = translate("Container Inspect")
+ s.template = "dockerman/logs"
+ m.submit = false
+ m.reset = false
elseif action == "logs" then
- local logsection= m:section(SimpleSection)
- local logs = ""
- local query ={
- stdout = 1,
- stderr = 1,
- tail = 1000
- }
- local logs = dk.containers:logs({id = container_id, query = query})
- if logs.code == 200 then
- logsection.syslog=logs.body
- else
- logsection.syslog="Get Logs ERROR\n"..logs.code..": "..logs.body
- end
- logsection.title=translate("Container Logs")
- logsection.template = "dockerman/logs"
- m.submit = false
- m.reset = false
+ local logs = ""
+ local query ={
+ stdout = 1,
+ stderr = 1,
+ tail = 1000
+ }
+
+ s = m:section(SimpleSection)
+
+ logs = dk.containers:logs({id = container_id, query = query})
+ if logs.code == 200 then
+ s.syslog=logs.body
+ else
+ s.syslog="Get Logs ERROR\n"..logs.code..": "..logs.body
+ end
+
+ s.title=translate("Container Logs")
+ s.template = "dockerman/logs"
+ m.submit = false
+ m.reset = false
elseif action == "console" then
- m.submit = false
- m.reset = false
- local cmd_docker = luci.util.exec("which docker"):match("^.+docker") or nil
- local cmd_ttyd = luci.util.exec("which ttyd"):match("^.+ttyd") or nil
- if cmd_docker and cmd_ttyd and container_info.State.Status == "running" then
- local consolesection= m:section(SimpleSection)
- local cmd = "/bin/sh"
- local uid
- local vcommand = consolesection:option(Value, "command", translate("Command"))
- vcommand:value("/bin/sh", "/bin/sh")
- vcommand:value("/bin/ash", "/bin/ash")
- vcommand:value("/bin/bash", "/bin/bash")
- vcommand.default = "/bin/sh"
- vcommand.forcewrite = true
- vcommand.write = function(self, section, value)
- cmd = value
- end
- local vuid = consolesection:option(Value, "uid", translate("UID"))
- vuid.forcewrite = true
- vuid.write = function(self, section, value)
- uid = value
- end
- local btn_connect = consolesection:option(Button, "connect")
- btn_connect.render = function(self, section, scope)
- self.inputstyle = "add"
- self.title = " "
- self.inputtitle = translate("Connect")
- Button.render(self, section, scope)
- end
- btn_connect.write = function(self, section)
- local cmd_docker = luci.util.exec("which docker"):match("^.+docker") or nil
- local cmd_ttyd = luci.util.exec("which ttyd"):match("^.+ttyd") or nil
- if not cmd_docker or not cmd_ttyd or cmd_docker:match("^%s+$") or cmd_ttyd:match("^%s+$") then return end
- local kill_ttyd = 'netstat -lnpt | grep ":7682[ \t].*ttyd$" | awk \'{print $NF}\' | awk -F\'/\' \'{print "kill -9 " $1}\' | sh > /dev/null'
- luci.util.exec(kill_ttyd)
- local hosts
- local uci = (require "luci.model.uci").cursor()
- local remote = uci:get("dockerman", "local", "remote_endpoint")
- local socket_path = (remote == "false" or not remote) and uci:get("dockerman", "local", "socket_path") or nil
- local host = (remote == "true") and uci:get("dockerman", "local", "remote_host") or nil
- local port = (remote == "true") and uci:get("dockerman", "local", "remote_port") or nil
- if remote and host and port then
- hosts = host .. ':'.. port
- elseif socket_path then
- hosts = "unix://" .. socket_path
- else
- return
- end
- local start_cmd = cmd_ttyd .. ' -d 2 --once -p 7682 '.. cmd_docker .. ' -H "'.. hosts ..'" exec -it ' .. (uid and uid ~= "" and (" -u ".. uid .. ' ') or "").. container_id .. ' ' .. cmd .. ' &'
- os.execute(start_cmd)
- local console = consolesection:option(DummyValue, "console")
- console.container_id = container_id
- console.template = "dockerman/container_console"
- end
- end
+ m.submit = false
+ m.reset = false
+ local cmd_docker = luci.util.exec("which docker"):match("^.+docker") or nil
+ local cmd_ttyd = luci.util.exec("which ttyd"):match("^.+ttyd") or nil
+
+ if cmd_docker and cmd_ttyd and container_info.State.Status == "running" then
+ local cmd = "/bin/sh"
+ local uid
+
+ s = m:section(SimpleSection)
+
+ o = s:option(Value, "command", translate("Command"))
+ o:value("/bin/sh", "/bin/sh")
+ o:value("/bin/ash", "/bin/ash")
+ o:value("/bin/bash", "/bin/bash")
+ o.default = "/bin/sh"
+ o.forcewrite = true
+ o.write = function(self, section, value)
+ cmd = value
+ end
+
+ o = s:option(Value, "uid", translate("UID"))
+ o.forcewrite = true
+ o.write = function(self, section, value)
+ uid = value
+ end
+
+ o = s:option(Button, "connect")
+ o.render = function(self, section, scope)
+ self.inputstyle = "add"
+ self.title = " "
+ self.inputtitle = translate("Connect")
+ Button.render(self, section, scope)
+ end
+ o.write = function(self, section)
+ local cmd_docker = luci.util.exec("which docker"):match("^.+docker") or nil
+ local cmd_ttyd = luci.util.exec("which ttyd"):match("^.+ttyd") or nil
+
+ if not cmd_docker or not cmd_ttyd or cmd_docker:match("^%s+$") or cmd_ttyd:match("^%s+$")then
+ return
+ end
+
+ local kill_ttyd = 'netstat -lnpt | grep ":7682[ \t].*ttyd$" | awk \'{print $NF}\' | awk -F\'/\' \'{print "kill -9 " $1}\' | sh > /dev/null'
+ luci.util.exec(kill_ttyd)
+ local hosts
+ local uci = (require "luci.model.uci").cursor()
+ local remote = uci:get_bool("dockerd", "globals", "remote_endpoint")
+ local host = nil
+ local port = nil
+ local socket = nil
+
+ if remote then
+ host = uci:get("dockerd", "globals", "remote_host") or nil
+ port = uci:get("dockerd", "globals", "remote_port") or nil
+ else
+ socket = uci:get("dockerd", "globals", "socket_path") or nil
+ end
+
+ if remote and host and port then
+ hosts = host .. ':'.. port
+ elseif socket_path then
+ hosts = socket_path
+ else
+ return
+ end
+
+ local start_cmd = cmd_ttyd .. ' -d 2 --once -p 7682 '.. cmd_docker .. ' -H "'.. hosts ..'" exec -it ' .. (uid and uid ~= "" and (" -u ".. uid .. ' ') or "").. container_id .. ' ' .. cmd .. ' &'
+ os.execute(start_cmd)
+
+ o = s:option(DummyValue, "console")
+ o.container_id = container_id
+ o.template = "dockerman/container_console"
+ end
+ end
elseif action == "stats" then
- local response = dk.containers:top({id = container_id, query = {ps_args="-aux"}})
- local container_top
- if response.code == 200 then
- container_top=response.body
- else
- response = dk.containers:top({id = container_id})
- if response.code == 200 then
- container_top=response.body
- end
- end
-
- if type(container_top) == "table" then
- container_top=response.body
- stat_section = m:section(SimpleSection)
- stat_section.container_id = container_id
- stat_section.template = "dockerman/container_stats"
- table_stats = {cpu={key=translate("CPU Useage"),value='-'},memory={key=translate("Memory Useage"),value='-'}}
- stat_section = m:section(Table, table_stats, translate("Stats"))
- stat_section:option(DummyValue, "key", translate("Stats")).width="33%"
- stat_section:option(DummyValue, "value")
- top_section= m:section(Table, container_top.Processes, translate("TOP"))
- for i, v in ipairs(container_top.Titles) do
- top_section:option(DummyValue, i, translate(v))
- end
-end
-m.submit = false
-m.reset = false
+ local response = dk.containers:top({id = container_id, query = {ps_args="-aux"}})
+ local container_top
+
+ if response.code == 200 then
+ container_top=response.body
+ else
+ response = dk.containers:top({id = container_id})
+ if response.code == 200 then
+ container_top=response.body
+ end
+ end
+
+ if type(container_top) == "table" then
+ s = m:section(SimpleSection)
+ s.container_id = container_id
+ s.template = "dockerman/container_stats"
+ table_stats = {
+ cpu={
+ key=translate("CPU Useage"),
+ value='-'
+ },
+ memory={
+ key=translate("Memory Useage"),
+ value='-'
+ }
+ }
+
+ container_top = response.body
+ s = m:section(Table, table_stats, translate("Stats"))
+ s:option(DummyValue, "key", translate("Stats")).width="33%"
+ s:option(DummyValue, "value")
+ top_section = m:section(Table, container_top.Processes, translate("TOP"))
+ for i, v in ipairs(container_top.Titles) do
+ top_section:option(DummyValue, i, translate(v))
+ end
+ end
+
+ m.submit = false
+ m.reset = false
end
return m
diff --git a/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/containers.lua b/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/containers.lua
index 2187de4662..cb1d2d943f 100644
--- a/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/containers.lua
+++ b/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/containers.lua
@@ -3,193 +3,229 @@ LuCI - Lua Configuration Interface
Copyright 2019 lisaac <https://github.com/lisaac/luci-app-dockerman>
]]--
-require "luci.util"
local http = require "luci.http"
-local uci = luci.model.uci.cursor()
local docker = require "luci.model.docker"
+
+local m, s, o
+local images, networks, containers, res
+
local dk = docker.new()
+res = dk.images:list()
+if res.code <300 then
+ images = res.body
+else
+ return
+end
-local images, networks, containers
-local res = dk.images:list()
-if res.code <300 then images = res.body else return end
res = dk.networks:list()
-if res.code <300 then networks = res.body else return end
-res = dk.containers:list({query = {all=true}})
-if res.code <300 then containers = res.body else return end
+if res.code <300 then
+ networks = res.body
+else
+ return
+end
+
+res = dk.containers:list({
+ query = {
+ all=true
+ }
+})
+if res.code <300 then
+ containers = res.body
+else
+ return
+end
local urlencode = luci.http.protocol and luci.http.protocol.urlencode or luci.util.urlencode
function get_containers()
- local data = {}
- if type(containers) ~= "table" then return nil end
- for i, v in ipairs(containers) do
- local index = v.Created .. v.Id
- data[index]={}
- data[index]["_selected"] = 0
- data[index]["_id"] = v.Id:sub(1,12)
- data[index]["name"] = v.Names[1]:sub(2)
- data[index]["_name"] = '<a href='..luci.dispatcher.build_url("admin/docker/container/"..v.Id)..' class="dockerman_link" title="'..translate("Container detail")..'">'.. v.Names[1]:sub(2).."</a>"
- data[index]["_status"] = v.Status
- if v.Status:find("^Up") then
- data[index]["_status"] = '<font color="green">'.. data[index]["_status"] .. "</font>"
- else
- data[index]["_status"] = '<font color="red">'.. data[index]["_status"] .. "</font>"
- end
- if (type(v.NetworkSettings) == "table" and type(v.NetworkSettings.Networks) == "table") then
- for networkname, netconfig in pairs(v.NetworkSettings.Networks) do
- data[index]["_network"] = (data[index]["_network"] ~= nil and (data[index]["_network"] .." | ") or "").. networkname .. (netconfig.IPAddress ~= "" and (": " .. netconfig.IPAddress) or "")
- end
- end
- -- networkmode = v.HostConfig.NetworkMode ~= "default" and v.HostConfig.NetworkMode or "bridge"
- -- data[index]["_network"] = v.NetworkSettings.Networks[networkmode].IPAddress or nil
- -- local _, _, image = v.Image:find("^sha256:(.+)")
- -- if image ~= nil then
- -- image=image:sub(1,12)
- -- end
- if v.Ports and next(v.Ports) ~= nil then
- data[index]["_ports"] = nil
- for _,v2 in ipairs(v.Ports) do
- data[index]["_ports"] = (data[index]["_ports"] and (data[index]["_ports"] .. ", ") or "")
- .. ((v2.PublicPort and v2.Type and v2.Type == "tcp") and ('<a href="javascript:void(0);" onclick="window.open((window.location.origin.match(/^(.+):\\d+$/) && window.location.origin.match(/^(.+):\\d+$/)[1] || window.location.origin) + \':\' + '.. v2.PublicPort ..', \'_blank\');">') or "")
- .. (v2.PublicPort and (v2.PublicPort .. ":") or "") .. (v2.PrivatePort and (v2.PrivatePort .."/") or "") .. (v2.Type and v2.Type or "")
- .. ((v2.PublicPort and v2.Type and v2.Type == "tcp")and "</a>" or "")
- end
- end
- for ii,iv in ipairs(images) do
- if iv.Id == v.ImageID then
- data[index]["_image"] = iv.RepoTags and iv.RepoTags[1] or (iv.RepoDigests[1]:gsub("(.-)@.+", "%1") .. ":<none>")
- end
- end
-
- data[index]["_image_id"] = v.ImageID:sub(8,20)
- data[index]["_command"] = v.Command
- end
- return data
+ local data = {}
+
+ if type(containers) ~= "table" then
+ return nil
+ end
+
+ for i, v in ipairs(containers) do
+ local index = v.Created .. v.Id
+
+ data[index]={}
+ data[index]["_selected"] = 0
+ data[index]["_id"] = v.Id:sub(1,12)
+ data[index]["name"] = v.Names[1]:sub(2)
+ data[index]["_name"] = '<a href='..luci.dispatcher.build_url("admin/docker/container/"..v.Id)..' class="dockerman_link" title="'..translate("Container detail")..'">'.. v.Names[1]:sub(2).."</a>"
+ data[index]["_status"] = v.Status
+
+ if v.Status:find("^Up") then
+ data[index]["_status"] = '<font color="green">'.. data[index]["_status"] .. "</font>"
+ else
+ data[index]["_status"] = '<font color="red">'.. data[index]["_status"] .. "</font>"
+ end
+
+ if (type(v.NetworkSettings) == "table" and type(v.NetworkSettings.Networks) == "table") then
+ for networkname, netconfig in pairs(v.NetworkSettings.Networks) do
+ data[index]["_network"] = (data[index]["_network"] ~= nil and (data[index]["_network"] .." | ") or "").. networkname .. (netconfig.IPAddress ~= "" and (": " .. netconfig.IPAddress) or "")
+ end
+ end
+
+ if v.Ports and next(v.Ports) ~= nil then
+ data[index]["_ports"] = nil
+ for _,v2 in ipairs(v.Ports) do
+ data[index]["_ports"] = (data[index]["_ports"] and (data[index]["_ports"] .. ", ") or "")
+ .. ((v2.PublicPort and v2.Type and v2.Type == "tcp") and ('<a href="javascript:void(0);" onclick="window.open((window.location.origin.match(/^(.+):\\d+$/) && window.location.origin.match(/^(.+):\\d+$/)[1] || window.location.origin) + \':\' + '.. v2.PublicPort ..', \'_blank\');">') or "")
+ .. (v2.PublicPort and (v2.PublicPort .. ":") or "") .. (v2.PrivatePort and (v2.PrivatePort .."/") or "") .. (v2.Type and v2.Type or "")
+ .. ((v2.PublicPort and v2.Type and v2.Type == "tcp")and "</a>" or "")
+ end
+ end
+
+ for ii,iv in ipairs(images) do
+ if iv.Id == v.ImageID then
+ data[index]["_image"] = iv.RepoTags and iv.RepoTags[1] or (iv.RepoDigests[1]:gsub("(.-)@.+", "%1") .. ":<none>")
+ end
+ end
+
+ data[index]["_image_id"] = v.ImageID:sub(8,20)
+ data[index]["_command"] = v.Command
+ end
+
+ return data
end
-local c_lists = get_containers()
--- list Containers
--- m = Map("docker", translate("Docker"))
+local container_list = get_containers()
+
m = SimpleForm("docker", translate("Docker"))
m.submit=false
m.reset=false
-docker_status = m:section(SimpleSection)
-docker_status.template = "dockerman/apply_widget"
-docker_status.err=docker:read_status()
-docker_status.err=docker_status.err and docker_status.err:gsub("\n","<br>"):gsub(" ","&nbsp;")
-if docker_status.err then docker:clear_status() end
-
-c_table = m:section(Table, c_lists, translate("Containers"))
-c_table.nodescr=true
--- v.template = "cbi/tblsection"
--- v.sortable = true
-container_selecter = c_table:option(Flag, "_selected","")
-container_selecter.disabled = 0
-container_selecter.enabled = 1
-container_selecter.default = 0
-
-container_id = c_table:option(DummyValue, "_id", translate("ID"))
-container_id.width="10%"
-container_name = c_table:option(DummyValue, "_name", translate("Container Name"))
-container_name.rawhtml = true
-container_status = c_table:option(DummyValue, "_status", translate("Status"))
-container_status.width="15%"
-container_status.rawhtml=true
-container_ip = c_table:option(DummyValue, "_network", translate("Network"))
-container_ip.width="15%"
-container_ports = c_table:option(DummyValue, "_ports", translate("Ports"))
-container_ports.width="10%"
-container_ports.rawhtml = true
-container_image = c_table:option(DummyValue, "_image", translate("Image"))
-container_image.width="10%"
-container_command = c_table:option(DummyValue, "_command", translate("Command"))
-container_command.width="20%"
-
-container_selecter.write=function(self, section, value)
- c_lists[section]._selected = value
+s = m:section(SimpleSection)
+s.template = "dockerman/apply_widget"
+s.err=docker:read_status()
+s.err=s.err and s.err:gsub("\n","<br>"):gsub(" ","&nbsp;")
+if s.err then
+ docker:clear_status()
+end
+
+s = m:section(Table, container_list, translate("Containers"))
+s.nodescr=true
+
+o = s:option(Flag, "_selected","")
+o.disabled = 0
+o.enabled = 1
+o.default = 0
+o.write=function(self, section, value)
+ container_list[section]._selected = value
end
+o = s:option(DummyValue, "_id", translate("ID"))
+o.width="10%"
+
+o = s:option(DummyValue, "_name", translate("Container Name"))
+o.rawhtml = true
+
+o = s:option(DummyValue, "_status", translate("Status"))
+o.width="15%"
+o.rawhtml=true
+
+o = s:option(DummyValue, "_network", translate("Network"))
+o.width="15%"
+
+o = s:option(DummyValue, "_ports", translate("Ports"))
+o.width="10%"
+o.rawhtml = true
+
+o = s:option(DummyValue, "_image", translate("Image"))
+o.width="10%"
+
+o = s:option(DummyValue, "_command", translate("Command"))
+o.width="20%"
+
local start_stop_remove = function(m,cmd)
- local c_selected = {}
- -- 遍历table中sectionid
- local c_table_sids = c_table:cfgsections()
- for _, c_table_sid in ipairs(c_table_sids) do
- -- 得到选中项的名字
- if c_lists[c_table_sid]._selected == 1 then
- c_selected[#c_selected+1] = c_lists[c_table_sid].name --container_name:cfgvalue(c_table_sid)
- end
- end
- if #c_selected >0 then
- docker:clear_status()
- local success = true
- for _,cont in ipairs(c_selected) do
- docker:append_status("Containers: " .. cmd .. " " .. cont .. "...")
- local res = dk.containers[cmd](dk, {id = cont})
- if res and res.code >= 300 then
- success = false
- docker:append_status("code:" .. res.code.." ".. (res.body.message and res.body.message or res.message).. "\n")
- else
- docker:append_status("done\n")
- end
- end
- if success then docker:clear_status() end
- luci.http.redirect(luci.dispatcher.build_url("admin/docker/containers"))
- end
+ local container_selected = {}
+
+ for k in pairs(container_list) do
+ if container_list[k]._selected == 1 then
+ container_selected[#container_selected + 1] = container_list[k].name
+ end
+ end
+
+ if #container_selected > 0 then
+ local success = true
+
+ docker:clear_status()
+ for _, cont in ipairs(container_selected) do
+ docker:append_status("Containers: " .. cmd .. " " .. cont .. "...")
+ local res = dk.containers[cmd](dk, {id = cont})
+ if res and res.code >= 300 then
+ success = false
+ docker:append_status("code:" .. res.code.." ".. (res.body.message and res.body.message or res.message).. "\n")
+ else
+ docker:append_status("done\n")
+ end
+ end
+
+ if success then
+ docker:clear_status()
+ end
+
+ luci.http.redirect(luci.dispatcher.build_url("admin/docker/containers"))
+ end
end
-action_section = m:section(Table,{{}})
-action_section.notitle=true
-action_section.rowcolors=false
-action_section.template="cbi/nullsection"
-
-btnnew=action_section:option(Button, "_new")
-btnnew.inputtitle= translate("New")
-btnnew.template = "dockerman/cbi/inlinebutton"
-btnnew.inputstyle = "add"
-btnnew.forcewrite = true
-btnstart=action_section:option(Button, "_start")
-btnstart.template = "dockerman/cbi/inlinebutton"
-btnstart.inputtitle=translate("Start")
-btnstart.inputstyle = "apply"
-btnstart.forcewrite = true
-btnrestart=action_section:option(Button, "_restart")
-btnrestart.template = "dockerman/cbi/inlinebutton"
-btnrestart.inputtitle=translate("Restart")
-btnrestart.inputstyle = "reload"
-btnrestart.forcewrite = true
-btnstop=action_section:option(Button, "_stop")
-btnstop.template = "dockerman/cbi/inlinebutton"
-btnstop.inputtitle=translate("Stop")
-btnstop.inputstyle = "reset"
-btnstop.forcewrite = true
-btnkill=action_section:option(Button, "_kill")
-btnkill.template = "dockerman/cbi/inlinebutton"
-btnkill.inputtitle=translate("Kill")
-btnkill.inputstyle = "reset"
-btnkill.forcewrite = true
-btnremove=action_section:option(Button, "_remove")
-btnremove.template = "dockerman/cbi/inlinebutton"
-btnremove.inputtitle=translate("Remove")
-btnremove.inputstyle = "remove"
-btnremove.forcewrite = true
-btnnew.write = function(self, section)
- luci.http.redirect(luci.dispatcher.build_url("admin/docker/newcontainer"))
+s = m:section(Table,{{}})
+s.notitle=true
+s.rowcolors=false
+s.template="cbi/nullsection"
+
+o = s:option(Button, "_new")
+o.inputtitle= translate("New")
+o.template = "dockerman/cbi/inlinebutton"
+o.inputstyle = "add"
+o.forcewrite = true
+o.write = function(self, section)
+ luci.http.redirect(luci.dispatcher.build_url("admin/docker/newcontainer"))
end
-btnstart.write = function(self, section)
- start_stop_remove(m,"start")
+
+o = s:option(Button, "_start")
+o.template = "dockerman/cbi/inlinebutton"
+o.inputtitle=translate("Start")
+o.inputstyle = "apply"
+o.forcewrite = true
+o.write = function(self, section)
+ start_stop_remove(m,"start")
end
-btnrestart.write = function(self, section)
- start_stop_remove(m,"restart")
+
+o = s:option(Button, "_restart")
+o.template = "dockerman/cbi/inlinebutton"
+o.inputtitle=translate("Restart")
+o.inputstyle = "reload"
+o.forcewrite = true
+o.write = function(self, section)
+ start_stop_remove(m,"restart")
end
-btnremove.write = function(self, section)
- start_stop_remove(m,"remove")
+
+o = s:option(Button, "_stop")
+o.template = "dockerman/cbi/inlinebutton"
+o.inputtitle=translate("Stop")
+o.inputstyle = "reset"
+o.forcewrite = true
+o.write = function(self, section)
+ start_stop_remove(m,"stop")
end
-btnstop.write = function(self, section)
- start_stop_remove(m,"stop")
+
+o = s:option(Button, "_kill")
+o.template = "dockerman/cbi/inlinebutton"
+o.inputtitle=translate("Kill")
+o.inputstyle = "reset"
+o.forcewrite = true
+o.write = function(self, section)
+ start_stop_remove(m,"kill")
end
-btnkill.write = function(self, section)
- start_stop_remove(m,"kill")
+
+o = s:option(Button, "_remove")
+o.template = "dockerman/cbi/inlinebutton"
+o.inputtitle=translate("Remove")
+o.inputstyle = "remove"
+o.forcewrite = true
+o.write = function(self, section)
+ start_stop_remove(m,"remove")
end
return m
diff --git a/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/images.lua b/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/images.lua
index 29d4a63573..01a7e3f237 100644
--- a/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/images.lua
+++ b/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/images.lua
@@ -3,221 +3,272 @@ LuCI - Lua Configuration Interface
Copyright 2019 lisaac <https://github.com/lisaac/luci-app-dockerman>
]]--
-require "luci.util"
-local uci = luci.model.uci.cursor()
local docker = require "luci.model.docker"
local dk = docker.new()
-local containers, images
-local res = dk.images:list()
-if res.code <300 then images = res.body else return end
-res = dk.containers:list({query = {all=true}})
-if res.code <300 then containers = res.body else return end
+local containers, images, res
+local m, s, o
+
+res = dk.images:list()
+if res.code < 300 then
+ images = res.body
+else
+ return
+end
+
+res = dk.containers:list({
+ query = {
+ all=true
+ }
+})
+if res.code < 300 then
+ containers = res.body
+else
+ return
+end
function get_images()
- local data = {}
- for i, v in ipairs(images) do
- local index = v.Created .. v.Id
- data[index]={}
- data[index]["_selected"] = 0
- data[index]["id"] = v.Id:sub(8)
- data[index]["_id"] = '<a href="javascript:new_tag(\''..v.Id:sub(8,20)..'\')" class="dockerman-link" title="'..translate("New tag")..'">' .. v.Id:sub(8,20) .. '</a>'
- if v.RepoTags and next(v.RepoTags)~=nil then
- for i, v1 in ipairs(v.RepoTags) do
- data[index]["_tags"] =(data[index]["_tags"] and ( data[index]["_tags"] .. "<br>" )or "") .. ((v1:match("<none>") or (#v.RepoTags == 1)) and v1 or ('<a href="javascript:un_tag(\''..v1..'\')" class="dockerman_link" title="'..translate("Remove tag")..'" >' .. v1 .. '</a>'))
- if not data[index]["tag"] then
- data[index]["tag"] = v1--:match("<none>") and nil or v1
- end
- end
- else
- data[index]["_tags"] = v.RepoDigests[1] and v.RepoDigests[1]:match("^(.-)@.+")
- data[index]["_tags"] = (data[index]["_tags"] and data[index]["_tags"] or "<none>" ).. ":<none>"
- end
- data[index]["_tags"] = data[index]["_tags"]:gsub("<none>","&lt;none&gt;")
- -- data[index]["_tags"] = '<a href="javascript:handle_tag(\''..data[index]["_id"]..'\')">' .. data[index]["_tags"] .. '</a>'
- for ci,cv in ipairs(containers) do
- if v.Id == cv.ImageID then
- data[index]["_containers"] = (data[index]["_containers"] and (data[index]["_containers"] .. " | ") or "")..
- '<a href='..luci.dispatcher.build_url("admin/docker/container/"..cv.Id)..' class="dockerman_link" title="'..translate("Container detail")..'">'.. cv.Names[1]:sub(2).."</a>"
- end
- end
- data[index]["_size"] = string.format("%.2f", tostring(v.Size/1024/1024)).."MB"
- data[index]["_created"] = os.date("%Y/%m/%d %H:%M:%S",v.Created)
- end
- return data
+ local data = {}
+
+ for i, v in ipairs(images) do
+ local index = v.Created .. v.Id
+
+ data[index]={}
+ data[index]["_selected"] = 0
+ data[index]["id"] = v.Id:sub(8)
+ data[index]["_id"] = '<a href="javascript:new_tag(\''..v.Id:sub(8,20)..'\')" class="dockerman-link" title="'..translate("New tag")..'">' .. v.Id:sub(8,20) .. '</a>'
+
+ if v.RepoTags and next(v.RepoTags)~=nil then
+ for i, v1 in ipairs(v.RepoTags) do
+ data[index]["_tags"] =(data[index]["_tags"] and ( data[index]["_tags"] .. "<br>" )or "") .. ((v1:match("<none>") or (#v.RepoTags == 1)) and v1 or ('<a href="javascript:un_tag(\''..v1..'\')" class="dockerman_link" title="'..translate("Remove tag")..'" >' .. v1 .. '</a>'))
+
+ if not data[index]["tag"] then
+ data[index]["tag"] = v1
+ end
+ end
+ else
+ data[index]["_tags"] = v.RepoDigests[1] and v.RepoDigests[1]:match("^(.-)@.+")
+ data[index]["_tags"] = (data[index]["_tags"] and data[index]["_tags"] or "<none>" ).. ":<none>"
+ end
+
+ data[index]["_tags"] = data[index]["_tags"]:gsub("<none>","&lt;none&gt;")
+ for ci,cv in ipairs(containers) do
+ if v.Id == cv.ImageID then
+ data[index]["_containers"] = (data[index]["_containers"] and (data[index]["_containers"] .. " | ") or "")..
+ '<a href='..luci.dispatcher.build_url("admin/docker/container/"..cv.Id)..' class="dockerman_link" title="'..translate("Container detail")..'">'.. cv.Names[1]:sub(2).."</a>"
+ end
+ end
+
+ data[index]["_size"] = string.format("%.2f", tostring(v.Size/1024/1024)).."MB"
+ data[index]["_created"] = os.date("%Y/%m/%d %H:%M:%S",v.Created)
+ end
+
+ return data
end
local image_list = get_images()
--- m = Map("docker", translate("Docker"))
m = SimpleForm("docker", translate("Docker"))
m.submit=false
m.reset=false
-local pull_value={_image_tag_name="", _registry="index.docker.io"}
-local pull_section = m:section(SimpleSection, translate("Pull Image"))
-pull_section.template="cbi/nullsection"
-local tag_name = pull_section:option(Value, "_image_tag_name")
-tag_name.template = "dockerman/cbi/inlinevalue"
-tag_name.placeholder="lisaac/luci:latest"
-local action_pull = pull_section:option(Button, "_pull")
-action_pull.inputtitle= translate("Pull")
-action_pull.template = "dockerman/cbi/inlinebutton"
-action_pull.inputstyle = "add"
-tag_name.write = function(self, section, value)
- local hastag = value:find(":")
- if not hastag then
- value = value .. ":latest"
- end
- pull_value["_image_tag_name"] = value
+local pull_value={
+ _image_tag_name="",
+ _registry="index.docker.io"
+}
+
+s = m:section(SimpleSection, translate("Pull Image"))
+s.template="cbi/nullsection"
+
+o = s:option(Value, "_image_tag_name")
+o.template = "dockerman/cbi/inlinevalue"
+o.placeholder="lisaac/luci:latest"
+o.write = function(self, section, value)
+ local hastag = value:find(":")
+
+ if not hastag then
+ value = value .. ":latest"
+ end
+ pull_value["_image_tag_name"] = value
end
-action_pull.write = function(self, section)
- local tag = pull_value["_image_tag_name"]
- local json_stringify = luci.jsonc and luci.jsonc.stringify
- if tag and tag ~= "" then
- docker:write_status("Images: " .. "pulling" .. " " .. tag .. "...\n")
- -- local x_auth = nixio.bin.b64encode(json_stringify({serveraddress= server})) , header={["X-Registry-Auth"] = x_auth}
- local res = dk.images:create({query = {fromImage=tag}}, docker.pull_image_show_status_cb)
- -- {"errorDetail": {"message": "failed to register layer: ApplyLayer exit status 1 stdout: stderr: write \/docker: no space left on device" }, "error": "failed to register layer: ApplyLayer exit status 1 stdout: stderr: write \/docker: no space left on device" }
- if res and res.code == 200 and (res.body[#res.body] and not res.body[#res.body].error and res.body[#res.body].status and (res.body[#res.body].status == "Status: Downloaded newer image for ".. tag)) then
- docker:clear_status()
- else
- docker:append_status("code:" .. res.code.." ".. (res.body[#res.body] and res.body[#res.body].error or (res.body.message or res.message)).. "\n")
- end
- else
- docker:append_status("code: 400 please input the name of image name!")
- end
- luci.http.redirect(luci.dispatcher.build_url("admin/docker/images"))
+
+o = s:option(Button, "_pull")
+o.inputtitle= translate("Pull")
+o.template = "dockerman/cbi/inlinebutton"
+o.inputstyle = "add"
+o.write = function(self, section)
+ local tag = pull_value["_image_tag_name"]
+ local json_stringify = luci.jsonc and luci.jsonc.stringify
+
+ if tag and tag ~= "" then
+ docker:write_status("Images: " .. "pulling" .. " " .. tag .. "...\n")
+ local res = dk.images:create({query = {fromImage=tag}}, docker.pull_image_show_status_cb)
+
+ if res and res.code == 200 and (res.body[#res.body] and not res.body[#res.body].error and res.body[#res.body].status and (res.body[#res.body].status == "Status: Downloaded newer image for ".. tag)) then
+ docker:clear_status()
+ else
+ docker:append_status("code:" .. res.code.." ".. (res.body[#res.body] and res.body[#res.body].error or (res.body.message or res.message)).. "\n")
+ end
+ else
+ docker:append_status("code: 400 please input the name of image name!")
+ end
+
+ luci.http.redirect(luci.dispatcher.build_url("admin/docker/images"))
end
-local import_section = m:section(SimpleSection, translate("Import Images"))
-local im = import_section:option(DummyValue, "_image_import")
-im.template = "dockerman/images_import"
-
-local image_table = m:section(Table, image_list, translate("Images"))
-
-local image_selecter = image_table:option(Flag, "_selected","")
-image_selecter.disabled = 0
-image_selecter.enabled = 1
-image_selecter.default = 0
-
-local image_id = image_table:option(DummyValue, "_id", translate("ID"))
-image_id.rawhtml = true
-image_table:option(DummyValue, "_tags", translate("RepoTags")).rawhtml = true
-image_table:option(DummyValue, "_containers", translate("Containers")).rawhtml = true
-image_table:option(DummyValue, "_size", translate("Size"))
-image_table:option(DummyValue, "_created", translate("Created"))
-image_selecter.write = function(self, section, value)
- image_list[section]._selected = value
+s = m:section(SimpleSection, translate("Import Images"))
+
+o = s:option(DummyValue, "_image_import")
+o.template = "dockerman/images_import"
+
+s = m:section(Table, image_list, translate("Images"))
+
+o = s:option(Flag, "_selected","")
+o.disabled = 0
+o.enabled = 1
+o.default = 0
+o.write = function(self, section, value)
+ image_list[section]._selected = value
end
+o = s:option(DummyValue, "_tags", translate("RepoTags"))
+o.rawhtml = true
+
+o = s:option(DummyValue, "_containers", translate("Containers"))
+o.rawhtml = true
+
+o = s:option(DummyValue, "_size", translate("Size"))
+
+o = s:option(DummyValue, "_created", translate("Created"))
+
+o = s:option(DummyValue, "_id", translate("ID"))
+o.rawhtml = true
+
local remove_action = function(force)
- local image_selected = {}
- -- 遍历table中sectionid
- local image_table_sids = image_table:cfgsections()
- for _, image_table_sid in ipairs(image_table_sids) do
- -- 得到选中项的名字
- if image_list[image_table_sid]._selected == 1 then
- image_selected[#image_selected+1] = (image_list[image_table_sid]["_tags"]:match("<br>") or image_list[image_table_sid]["_tags"]:match("&lt;none&gt;")) and image_list[image_table_sid].id or image_list[image_table_sid].tag
- end
- end
- if next(image_selected) ~= nil then
- local success = true
- docker:clear_status()
- for _,img in ipairs(image_selected) do
- docker:append_status("Images: " .. "remove" .. " " .. img .. "...")
- local query
- if force then query = {force = true} end
- local msg = dk.images:remove({id = img, query = query})
- if msg.code ~= 200 then
- docker:append_status("code:" .. msg.code.." ".. (msg.body.message and msg.body.message or msg.message).. "\n")
- success = false
- else
- docker:append_status("done\n")
- end
- end
- if success then docker:clear_status() end
- luci.http.redirect(luci.dispatcher.build_url("admin/docker/images"))
- end
+ local image_selected = {}
+
+ for k in pairs(image_list) do
+ if image_list[k]._selected == 1 then
+ image_selected[#image_selected+1] = (image_list[k]["_tags"]:match("<br>") or image_list[k]["_tags"]:match("&lt;none&gt;")) and image_list[k].id or image_list[k].tag
+ end
+ end
+
+ if next(image_selected) ~= nil then
+ local success = true
+
+ docker:clear_status()
+ for _, img in ipairs(image_selected) do
+ local query
+ docker:append_status("Images: " .. "remove" .. " " .. img .. "...")
+
+ if force then
+ query = {force = true}
+ end
+
+ local msg = dk.images:remove({
+ id = img,
+ query = query
+ })
+ if msg.code ~= 200 then
+ docker:append_status("code:" .. msg.code.." ".. (msg.body.message and msg.body.message or msg.message).. "\n")
+ success = false
+ else
+ docker:append_status("done\n")
+ end
+ end
+
+ if success then
+ docker:clear_status()
+ end
+
+ luci.http.redirect(luci.dispatcher.build_url("admin/docker/images"))
+ end
end
-local docker_status = m:section(SimpleSection)
-docker_status.template = "dockerman/apply_widget"
-docker_status.err = docker:read_status()
-docker_status.err = docker_status.err and docker_status.err:gsub("\n","<br>"):gsub(" ","&nbsp;")
-if docker_status.err then docker:clear_status() end
-
-local action = m:section(Table,{{}})
-action.notitle=true
-action.rowcolors=false
-action.template="cbi/nullsection"
-
-local btnremove = action:option(Button, "remove")
-btnremove.inputtitle= translate("Remove")
-btnremove.template = "dockerman/cbi/inlinebutton"
-btnremove.inputstyle = "remove"
-btnremove.forcewrite = true
-btnremove.write = function(self, section)
- remove_action()
+s = m:section(SimpleSection)
+s.template = "dockerman/apply_widget"
+s.err = docker:read_status()
+s.err = s.err and s.err:gsub("\n","<br>"):gsub(" ","&nbsp;")
+if s.err then
+ docker:clear_status()
end
-local btnforceremove = action:option(Button, "forceremove")
-btnforceremove.inputtitle= translate("Force Remove")
-btnforceremove.template = "dockerman/cbi/inlinebutton"
-btnforceremove.inputstyle = "remove"
-btnforceremove.forcewrite = true
-btnforceremove.write = function(self, section)
- remove_action(true)
+s = m:section(Table,{{}})
+s.notitle=true
+s.rowcolors=false
+s.template="cbi/nullsection"
+
+o = s:option(Button, "remove")
+o.inputtitle= translate("Remove")
+o.template = "dockerman/cbi/inlinebutton"
+o.inputstyle = "remove"
+o.forcewrite = true
+o.write = function(self, section)
+ remove_action()
+end
+
+o = s:option(Button, "forceremove")
+o.inputtitle= translate("Force Remove")
+o.template = "dockerman/cbi/inlinebutton"
+o.inputstyle = "remove"
+o.forcewrite = true
+o.write = function(self, section)
+ remove_action(true)
end
-local btnsave = action:option(Button, "save")
-btnsave.inputtitle= translate("Save")
-btnsave.template = "dockerman/cbi/inlinebutton"
-btnsave.inputstyle = "edit"
-btnsave.forcewrite = true
-btnsave.write = function (self, section)
- local image_selected = {}
- local image_table_sids = image_table:cfgsections()
- for _, image_table_sid in ipairs(image_table_sids) do
- if image_list[image_table_sid]._selected == 1 then
- image_selected[#image_selected+1] = image_list[image_table_sid].id --image_id:cfgvalue(image_table_sid)
- end
- end
- if next(image_selected) ~= nil then
- local names
- for _,img in ipairs(image_selected) do
- names = names and (names .. "&names=".. img) or img
- end
- local first
- local cb = function(res, chunk)
- if res.code == 200 then
- if not first then
- first = true
- luci.http.header('Content-Disposition', 'inline; filename="images.tar"')
- luci.http.header('Content-Type', 'application\/x-tar')
- end
- luci.ltn12.pump.all(chunk, luci.http.write)
- else
- if not first then
- first = true
- luci.http.prepare_content("text/plain")
- end
- luci.ltn12.pump.all(chunk, luci.http.write)
- end
- end
- docker:write_status("Images: " .. "save" .. " " .. table.concat(image_selected, "\n") .. "...")
- local msg = dk.images:get({query = {names = names}}, cb)
- if msg.code ~= 200 then
- docker:append_status("code:" .. msg.code.." ".. (msg.body.message and msg.body.message or msg.message).. "\n")
- success = false
- else
- docker:clear_status()
- end
- end
+o = s:option(Button, "save")
+o.inputtitle= translate("Save")
+o.template = "dockerman/cbi/inlinebutton"
+o.inputstyle = "edit"
+o.forcewrite = true
+o.write = function (self, section)
+ local image_selected = {}
+
+ for k in pairs(image_list) do
+ if image_list[k]._selected == 1 then
+ image_selected[#image_selected + 1] = image_list[k].id
+ end
+ end
+
+ if next(image_selected) ~= nil then
+ local names, first
+
+ for _, img in ipairs(image_selected) do
+ names = names and (names .. "&names=".. img) or img
+ end
+
+ local cb = function(res, chunk)
+ if res.code == 200 then
+ if not first then
+ first = true
+ luci.http.header('Content-Disposition', 'inline; filename="images.tar"')
+ luci.http.header('Content-Type', 'application\/x-tar')
+ end
+ luci.ltn12.pump.all(chunk, luci.http.write)
+ else
+ if not first then
+ first = true
+ luci.http.prepare_content("text/plain")
+ end
+ luci.ltn12.pump.all(chunk, luci.http.write)
+ end
+ end
+
+ docker:write_status("Images: " .. "save" .. " " .. table.concat(image_selected, "\n") .. "...")
+ local msg = dk.images:get({query = {names = names}}, cb)
+
+ if msg.code ~= 200 then
+ docker:append_status("code:" .. msg.code.." ".. (msg.body.message and msg.body.message or msg.message).. "\n")
+ success = false
+ else
+ docker:clear_status()
+ end
+ end
end
-local btnload = action:option(Button, "load")
-btnload.inputtitle= translate("Load")
-btnload.template = "dockerman/images_load"
-btnload.inputstyle = "add"
+o = s:option(Button, "load")
+o.inputtitle= translate("Load")
+o.template = "dockerman/images_load"
+o.inputstyle = "add"
+
return m
diff --git a/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/networks.lua b/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/networks.lua
index e8e392f6ab..4cea32915d 100644
--- a/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/networks.lua
+++ b/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/networks.lua
@@ -3,128 +3,150 @@ LuCI - Lua Configuration Interface
Copyright 2019 lisaac <https://github.com/lisaac/luci-app-dockerman>
]]--
-require "luci.util"
-local uci = luci.model.uci.cursor()
local docker = require "luci.model.docker"
-local dk = docker.new()
-local networks
-local res = dk.networks:list()
-if res.code < 300 then networks = res.body else return end
+
+local m, s, o
+local networks, dk, res
+
+dk = docker.new()
+res = dk.networks:list()
+if res.code < 300 then
+ networks = res.body
+else
+ return
+end
local get_networks = function ()
- local data = {}
-
- if type(networks) ~= "table" then return nil end
- for i, v in ipairs(networks) do
- local index = v.Created .. v.Id
- data[index]={}
- data[index]["_selected"] = 0
- data[index]["_id"] = v.Id:sub(1,12)
- data[index]["_name"] = v.Name
- data[index]["_driver"] = v.Driver
- if v.Driver == "bridge" then
- data[index]["_interface"] = v.Options["com.docker.network.bridge.name"]
- elseif v.Driver == "macvlan" then
- data[index]["_interface"] = v.Options.parent
- end
- data[index]["_subnet"] = v.IPAM and v.IPAM.Config[1] and v.IPAM.Config[1].Subnet or nil
- data[index]["_gateway"] = v.IPAM and v.IPAM.Config[1] and v.IPAM.Config[1].Gateway or nil
- end
- return data
+ local data = {}
+
+ if type(networks) ~= "table" then
+ return nil
+ end
+
+ for i, v in ipairs(networks) do
+ local index = v.Created .. v.Id
+
+ data[index]={}
+ data[index]["_selected"] = 0
+ data[index]["_id"] = v.Id:sub(1,12)
+ data[index]["_name"] = v.Name
+ data[index]["_driver"] = v.Driver
+
+ if v.Driver == "bridge" then
+ data[index]["_interface"] = v.Options["com.docker.network.bridge.name"]
+ elseif v.Driver == "macvlan" then
+ data[index]["_interface"] = v.Options.parent
+ end
+
+ data[index]["_subnet"] = v.IPAM and v.IPAM.Config[1] and v.IPAM.Config[1].Subnet or nil
+ data[index]["_gateway"] = v.IPAM and v.IPAM.Config[1] and v.IPAM.Config[1].Gateway or nil
+ end
+
+ return data
end
local network_list = get_networks()
--- m = Map("docker", translate("Docker"))
+
m = SimpleForm("docker", translate("Docker"))
m.submit=false
m.reset=false
-network_table = m:section(Table, network_list, translate("Networks"))
-network_table.nodescr=true
-
-network_selecter = network_table:option(Flag, "_selected","")
-network_selecter.template = "dockerman/cbi/xfvalue"
-network_id = network_table:option(DummyValue, "_id", translate("ID"))
-network_selecter.disabled = 0
-network_selecter.enabled = 1
-network_selecter.default = 0
-network_selecter.render = function(self, section, scope)
- self.disable = 0
- if network_list[section]["_name"] == "bridge" or network_list[section]["_name"] == "none" or network_list[section]["_name"] == "host" then
- self.disable = 1
- end
- Flag.render(self, section, scope)
+s = m:section(Table, network_list, translate("Networks"))
+s.nodescr=true
+
+o = s:option(Flag, "_selected","")
+o.template = "dockerman/cbi/xfvalue"
+o.disabled = 0
+o.enabled = 1
+o.default = 0
+o.render = function(self, section, scope)
+ self.disable = 0
+ if network_list[section]["_name"] == "bridge" or network_list[section]["_name"] == "none" or network_list[section]["_name"] == "host" then
+ self.disable = 1
+ end
+ Flag.render(self, section, scope)
+end
+o.write = function(self, section, value)
+ network_list[section]._selected = value
end
-network_name = network_table:option(DummyValue, "_name", translate("Network Name"))
-network_driver = network_table:option(DummyValue, "_driver", translate("Driver"))
-network_interface = network_table:option(DummyValue, "_interface", translate("Parent Interface"))
-network_subnet = network_table:option(DummyValue, "_subnet", translate("Subnet"))
-network_gateway = network_table:option(DummyValue, "_gateway", translate("Gateway"))
+o = s:option(DummyValue, "_id", translate("ID"))
-network_selecter.write = function(self, section, value)
- network_list[section]._selected = value
+o = s:option(DummyValue, "_name", translate("Network Name"))
+
+o = s:option(DummyValue, "_driver", translate("Driver"))
+
+o = s:option(DummyValue, "_interface", translate("Parent Interface"))
+
+o = s:option(DummyValue, "_subnet", translate("Subnet"))
+
+o = s:option(DummyValue, "_gateway", translate("Gateway"))
+
+s = m:section(SimpleSection)
+s.template = "dockerman/apply_widget"
+s.err = docker:read_status()
+s.err = s.err and s.err:gsub("\n","<br>"):gsub(" ","&nbsp;")
+if s.err then
+ docker:clear_status()
end
-docker_status = m:section(SimpleSection)
-docker_status.template = "dockerman/apply_widget"
-docker_status.err=docker:read_status()
-docker_status.err=docker_status.err and docker_status.err:gsub("\n","<br>"):gsub(" ","&nbsp;")
-if docker_status.err then docker:clear_status() end
-
-action = m:section(Table,{{}})
-action.notitle=true
-action.rowcolors=false
-action.template="cbi/nullsection"
-btnnew=action:option(Button, "_new")
-btnnew.inputtitle= translate("New")
-btnnew.template = "dockerman/cbi/inlinebutton"
-btnnew.notitle=true
-btnnew.inputstyle = "add"
-btnnew.forcewrite = true
-btnnew.write = function(self, section)
- luci.http.redirect(luci.dispatcher.build_url("admin/docker/newnetwork"))
+s = m:section(Table,{{}})
+s.notitle=true
+s.rowcolors=false
+s.template="cbi/nullsection"
+
+o = s:option(Button, "_new")
+o.inputtitle= translate("New")
+o.template = "dockerman/cbi/inlinebutton"
+o.notitle=true
+o.inputstyle = "add"
+o.forcewrite = true
+o.write = function(self, section)
+ luci.http.redirect(luci.dispatcher.build_url("admin/docker/newnetwork"))
end
-btnremove = action:option(Button, "_remove")
-btnremove.inputtitle= translate("Remove")
-btnremove.template = "dockerman/cbi/inlinebutton"
-btnremove.inputstyle = "remove"
-btnremove.forcewrite = true
-btnremove.write = function(self, section)
- local network_selected = {}
- local network_name_selected = {}
- local network_driver_selected = {}
- -- 遍历table中sectionid
- local network_table_sids = network_table:cfgsections()
- for _, network_table_sid in ipairs(network_table_sids) do
- -- 得到选中项的名字
- if network_list[network_table_sid]._selected == 1 then
- network_selected[#network_selected+1] = network_list[network_table_sid]._id --network_name:cfgvalue(network_table_sid)
- network_name_selected[#network_name_selected+1] = network_list[network_table_sid]._name
- network_driver_selected[#network_driver_selected+1] = network_list[network_table_sid]._driver
- end
- end
- if next(network_selected) ~= nil then
- local success = true
- docker:clear_status()
- for ii, net in ipairs(network_selected) do
- docker:append_status("Networks: " .. "remove" .. " " .. net .. "...")
- local res = dk.networks["remove"](dk, {id = net})
- if res and res.code >= 300 then
- docker:append_status("code:" .. res.code.." ".. (res.body.message and res.body.message or res.message).. "\n")
- success = false
- else
- docker:append_status("done\n")
- if network_driver_selected[ii] == "macvlan" then
- docker.remove_macvlan_interface(network_name_selected[ii])
- end
- end
- end
- if success then
- docker:clear_status()
- end
- luci.http.redirect(luci.dispatcher.build_url("admin/docker/networks"))
- end
+
+o = s:option(Button, "_remove")
+o.inputtitle= translate("Remove")
+o.template = "dockerman/cbi/inlinebutton"
+o.inputstyle = "remove"
+o.forcewrite = true
+o.write = function(self, section)
+ local network_selected = {}
+ local network_name_selected = {}
+ local network_driver_selected = {}
+
+ for k in pairs(network_list) do
+ if network_list[k]._selected == 1 then
+ network_selected[#network_selected + 1] = network_list[k]._id
+ network_name_selected[#network_name_selected + 1] = network_list[k]._name
+ network_driver_selected[#network_driver_selected + 1] = network_list[k]._driver
+ end
+ end
+
+ if next(network_selected) ~= nil then
+ local success = true
+ docker:clear_status()
+
+ for ii, net in ipairs(network_selected) do
+ docker:append_status("Networks: " .. "remove" .. " " .. net .. "...")
+ local res = dk.networks["remove"](dk, {id = net})
+
+ if res and res.code >= 300 then
+ docker:append_status("code:" .. res.code.." ".. (res.body.message and res.body.message or res.message).. "\n")
+ success = false
+ else
+ docker:append_status("done\n")
+ if network_driver_selected[ii] == "macvlan" then
+ docker.remove_macvlan_interface(network_name_selected[ii])
+ end
+ end
+ end
+
+ if success then
+ docker:clear_status()
+ end
+ luci.http.redirect(luci.dispatcher.build_url("admin/docker/networks"))
+ end
end
return m
diff --git a/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/newcontainer.lua b/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/newcontainer.lua
index 324fc6dd70..0ee344370e 100644
--- a/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/newcontainer.lua
+++ b/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/newcontainer.lua
@@ -3,298 +3,502 @@ LuCI - Lua Configuration Interface
Copyright 2019 lisaac <https://github.com/lisaac/luci-app-dockerman>
]]--
-require "luci.util"
-local uci = luci.model.uci.cursor()
local docker = require "luci.model.docker"
+
+local m, s, o
+
local dk = docker.new()
+
local cmd_line = table.concat(arg, '/')
local create_body = {}
local images = dk.images:list().body
local networks = dk.networks:list().body
-local containers = dk.containers:list({query = {all=true}}).body
+local containers = dk.containers:list({
+ query = {
+ all=true
+ }
+}).body
local is_quot_complete = function(str)
- require "math"
- if not str then return true end
- local num = 0, w
- for w in str:gmatch("\"") do
- num = num + 1
- end
- if math.fmod(num, 2) ~= 0 then return false end
- num = 0
- for w in str:gmatch("\'") do
- num = num + 1
- end
- if math.fmod(num, 2) ~= 0 then return false end
- return true
+ local num = 0, w
+ require "math"
+
+ if not str then
+ return true
+ end
+
+ local num = 0, w
+ for w in str:gmatch("\"") do
+ num = num + 1
+ end
+
+ if math.fmod(num, 2) ~= 0 then
+ return false
+ end
+
+ num = 0
+ for w in str:gmatch("\'") do
+ num = num + 1
+ end
+
+ if math.fmod(num, 2) ~= 0 then
+ return false
+ end
+
+ return true
+end
+
+function contains(list, x)
+ for _, v in pairs(list) do
+ if v == x then
+ return true
+ end
+ end
+ return false
end
local resolve_cli = function(cmd_line)
- local config = {advance = 1}
- local key_no_val = '|t|d|i|tty|rm|read_only|interactive|init|help|detach|privileged|P|publish_all|'
- local key_with_val = '|sysctl|add_host|a|attach|blkio_weight_device|cap_add|cap_drop|device|device_cgroup_rule|device_read_bps|device_read_iops|device_write_bps|device_write_iops|dns|dns_option|dns_search|e|env|env_file|expose|group_add|l|label|label_file|link|link_local_ip|log_driver|log_opt|network_alias|p|publish|security_opt|storage_opt|tmpfs|v|volume|volumes_from|blkio_weight|cgroup_parent|cidfile|cpu_period|cpu_quota|cpu_rt_period|cpu_rt_runtime|c|cpu_shares|cpus|cpuset_cpus|cpuset_mems|detach_keys|disable_content_trust|domainname|entrypoint|gpus|health_cmd|health_interval|health_retries|health_start_period|health_timeout|h|hostname|ip|ip6|ipc|isolation|kernel_memory|log_driver|mac_address|m|memory|memory_reservation|memory_swap|memory_swappiness|mount|name|network|no_healthcheck|oom_kill_disable|oom_score_adj|pid|pids_limit|restart|runtime|shm_size|sig_proxy|stop_signal|stop_timeout|ulimit|u|user|userns|uts|volume_driver|w|workdir|'
- local key_abb = {net='network',a='attach',c='cpu-shares',d='detach',e='env',h='hostname',i='interactive',l='label',m='memory',p='publish',P='publish_all',t='tty',u='user',v='volume',w='workdir'}
- local key_with_list = '|sysctl|add_host|a|attach|blkio_weight_device|cap_add|cap_drop|device|device_cgroup_rule|device_read_bps|device_read_iops|device_write_bps|device_write_iops|dns|dns_option|dns_search|e|env|env_file|expose|group_add|l|label|label_file|link|link_local_ip|log_driver|log_opt|network_alias|p|publish|security_opt|storage_opt|tmpfs|v|volume|volumes_from|'
- local key = nil
- local _key = nil
- local val = nil
- local is_cmd = false
-
- cmd_line = cmd_line:match("^DOCKERCLI%s+(.+)")
- for w in cmd_line:gmatch("[^%s]+") do
- if w =='\\' then
- elseif not key and not _key and not is_cmd then
- --key=val
- key, val = w:match("^%-%-([%lP%-]-)=(.+)")
- if not key then
- --key val
- key = w:match("^%-%-([%lP%-]+)")
- if not key then
- -- -v val
- key = w:match("^%-([%lP%-]+)")
- if key then
- -- for -dit
- if key:match("i") or key:match("t") or key:match("d") then
- if key:match("i") then
- config[key_abb["i"]] = true
- key:gsub("i", "")
- end
- if key:match("t") then
- config[key_abb["t"]] = true
- key:gsub("t", "")
- end
- if key:match("d") then
- config[key_abb["d"]] = true
- key:gsub("d", "")
- end
- if key:match("P") then
- config[key_abb["P"]] = true
- key:gsub("P", "")
- end
- if key == "" then key = nil end
- end
- end
- end
- end
- if key then
- key = key:gsub("-","_")
- key = key_abb[key] or key
- if key_no_val:match("|"..key.."|") then
- config[key] = true
- val = nil
- key = nil
- elseif key_with_val:match("|"..key.."|") then
- -- if key == "cap_add" then config.privileged = true end
- else
- key = nil
- val = nil
- end
- else
- config.image = w
- key = nil
- val = nil
- is_cmd = true
- end
- elseif (key or _key) and not is_cmd then
- if key == "mount" then
- -- we need resolve mount options here
- -- type=bind,source=/source,target=/app
- local _type = w:match("^type=([^,]+),") or "bind"
- local source = (_type ~= "tmpfs") and (w:match("source=([^,]+),") or w:match("src=([^,]+),")) or ""
- local target = w:match(",target=([^,]+)") or w:match(",dst=([^,]+)") or w:match(",destination=([^,]+)") or ""
- local ro = w:match(",readonly") and "ro" or nil
- if source and target then
- if _type ~= "tmpfs" then
- -- bind or volume
- local bind_propagation = (_type == "bind") and w:match(",bind%-propagation=([^,]+)") or nil
- val = source..":"..target .. ((ro or bind_propagation) and (":" .. (ro and ro or "") .. (((ro and bind_propagation) and "," or "") .. (bind_propagation and bind_propagation or ""))or ""))
- else
- -- tmpfs
- local tmpfs_mode = w:match(",tmpfs%-mode=([^,]+)") or nil
- local tmpfs_size = w:match(",tmpfs%-size=([^,]+)") or nil
- key = "tmpfs"
- val = target .. ((tmpfs_mode or tmpfs_size) and (":" .. (tmpfs_mode and ("mode=" .. tmpfs_mode) or "") .. ((tmpfs_mode and tmpfs_size) and "," or "") .. (tmpfs_size and ("size=".. tmpfs_size) or "")) or "")
- if not config[key] then config[key] = {} end
- table.insert( config[key], val )
- key = nil
- val = nil
- end
- end
- else
- val = w
- end
- elseif is_cmd then
- config["command"] = (config["command"] and (config["command"] .. " " )or "") .. w
- end
- if (key or _key) and val then
- key = _key or key
- if key_with_list:match("|"..key.."|") then
- if not config[key] then config[key] = {} end
- if _key then
- config[key][#config[key]] = config[key][#config[key]] .. " " .. w
- else
- table.insert( config[key], val )
- end
- if is_quot_complete(config[key][#config[key]]) then
- -- clear quotation marks
- config[key][#config[key]] = config[key][#config[key]]:gsub("[\"\']", "")
- _key = nil
- else
- _key = key
- end
- else
- config[key] = (config[key] and (config[key] .. " ") or "") .. val
- if is_quot_complete(config[key]) then
- -- clear quotation marks
- config[key] = config[key]:gsub("[\"\']", "")
- _key = nil
- else
- _key = key
- end
- end
- key = nil
- val = nil
- end
- end
- return config
+ local config = {
+ advance = 1
+ }
+
+ local key_no_val = {
+ 't',
+ 'd',
+ 'i',
+ 'tty',
+ 'rm',
+ 'read_only',
+ 'interactive',
+ 'init',
+ 'help',
+ 'detach',
+ 'privileged',
+ 'P',
+ 'publish_all',
+ }
+
+ local key_with_val = {
+ 'sysctl',
+ 'add_host',
+ 'a',
+ 'attach',
+ 'blkio_weight_device',
+ 'cap_add',
+ 'cap_drop',
+ 'device',
+ 'device_cgroup_rule',
+ 'device_read_bps',
+ 'device_read_iops',
+ 'device_write_bps',
+ 'device_write_iops',
+ 'dns',
+ 'dns_option',
+ 'dns_search',
+ 'e',
+ 'env',
+ 'env_file',
+ 'expose',
+ 'group_add',
+ 'l',
+ 'label',
+ 'label_file',
+ 'link',
+ 'link_local_ip',
+ 'log_driver',
+ 'log_opt',
+ 'network_alias',
+ 'p',
+ 'publish',
+ 'security_opt',
+ 'storage_opt',
+ 'tmpfs',
+ 'v',
+ 'volume',
+ 'volumes_from',
+ 'blkio_weight',
+ 'cgroup_parent',
+ 'cidfile',
+ 'cpu_period',
+ 'cpu_quota',
+ 'cpu_rt_period',
+ 'cpu_rt_runtime',
+ 'c',
+ 'cpu_shares',
+ 'cpus',
+ 'cpuset_cpus',
+ 'cpuset_mems',
+ 'detach_keys',
+ 'disable_content_trust',
+ 'domainname',
+ 'entrypoint',
+ 'gpus',
+ 'health_cmd',
+ 'health_interval',
+ 'health_retries',
+ 'health_start_period',
+ 'health_timeout',
+ 'h',
+ 'hostname',
+ 'ip',
+ 'ip6',
+ 'ipc',
+ 'isolation',
+ 'kernel_memory',
+ 'log_driver',
+ 'mac_address',
+ 'm',
+ 'memory',
+ 'memory_reservation',
+ 'memory_swap',
+ 'memory_swappiness',
+ 'mount',
+ 'name',
+ 'network',
+ 'no_healthcheck',
+ 'oom_kill_disable',
+ 'oom_score_adj',
+ 'pid',
+ 'pids_limit',
+ 'restart',
+ 'runtime',
+ 'shm_size',
+ 'sig_proxy',
+ 'stop_signal',
+ 'stop_timeout',
+ 'ulimit',
+ 'u',
+ 'user',
+ 'userns',
+ 'uts',
+ 'volume_driver',
+ 'w',
+ 'workdir'
+ }
+
+ local key_abb = {
+ net='network',
+ a='attach',
+ c='cpu-shares',
+ d='detach',
+ e='env',
+ h='hostname',
+ i='interactive',
+ l='label',
+ m='memory',
+ p='publish',
+ P='publish_all',
+ t='tty',
+ u='user',
+ v='volume',
+ w='workdir'
+ }
+
+ local key_with_list = {
+ 'sysctl',
+ 'add_host',
+ 'a',
+ 'attach',
+ 'blkio_weight_device',
+ 'cap_add',
+ 'cap_drop',
+ 'device',
+ 'device_cgroup_rule',
+ 'device_read_bps',
+ 'device_read_iops',
+ 'device_write_bps',
+ 'device_write_iops',
+ 'dns',
+ 'dns_optiondns_search',
+ 'e',
+ 'env',
+ 'env_file',
+ 'expose',
+ 'group_add',
+ 'l',
+ 'label',
+ 'label_file',
+ 'link',
+ 'link_local_ip',
+ 'log_driver',
+ 'log_opt',
+ 'network_alias',
+ 'p',
+ 'publish',
+ 'security_opt',
+ 'storage_opt',
+ 'tmpfs',
+ 'v',
+ 'volume',
+ 'volumes_from',
+ }
+
+ local key = nil
+ local _key = nil
+ local val = nil
+ local is_cmd = false
+
+ cmd_line = cmd_line:match("^DOCKERCLI%s+(.+)")
+ for w in cmd_line:gmatch("[^%s]+") do
+ if w =='\\' then
+ elseif not key and not _key and not is_cmd then
+ --key=val
+ key, val = w:match("^%-%-([%lP%-]-)=(.+)")
+ if not key then
+ --key val
+ key = w:match("^%-%-([%lP%-]+)")
+ if not key then
+ -- -v val
+ key = w:match("^%-([%lP%-]+)")
+ if key then
+ -- for -dit
+ if key:match("i") or key:match("t") or key:match("d") then
+ if key:match("i") then
+ config[key_abb["i"]] = true
+ key:gsub("i", "")
+ end
+ if key:match("t") then
+ config[key_abb["t"]] = true
+ key:gsub("t", "")
+ end
+ if key:match("d") then
+ config[key_abb["d"]] = true
+ key:gsub("d", "")
+ end
+ if key:match("P") then
+ config[key_abb["P"]] = true
+ key:gsub("P", "")
+ end
+ if key == "" then
+ key = nil
+ end
+ end
+ end
+ end
+ end
+ if key then
+ key = key:gsub("-","_")
+ key = key_abb[key] or key
+ if contains(key_no_val, key) then
+ config[key] = true
+ val = nil
+ key = nil
+ elseif contains(key_with_val, key) then
+ -- if key == "cap_add" then config.privileged = true end
+ else
+ key = nil
+ val = nil
+ end
+ else
+ config.image = w
+ key = nil
+ val = nil
+ is_cmd = true
+ end
+ elseif (key or _key) and not is_cmd then
+ if key == "mount" then
+ -- we need resolve mount options here
+ -- type=bind,source=/source,target=/app
+ local _type = w:match("^type=([^,]+),") or "bind"
+ local source = (_type ~= "tmpfs") and (w:match("source=([^,]+),") or w:match("src=([^,]+),")) or ""
+ local target = w:match(",target=([^,]+)") or w:match(",dst=([^,]+)") or w:match(",destination=([^,]+)") or ""
+ local ro = w:match(",readonly") and "ro" or nil
+
+ if source and target then
+ if _type ~= "tmpfs" then
+ local bind_propagation = (_type == "bind") and w:match(",bind%-propagation=([^,]+)") or nil
+ val = source..":"..target .. ((ro or bind_propagation) and (":" .. (ro and ro or "") .. (((ro and bind_propagation) and "," or "") .. (bind_propagation and bind_propagation or ""))or ""))
+ else
+ local tmpfs_mode = w:match(",tmpfs%-mode=([^,]+)") or nil
+ local tmpfs_size = w:match(",tmpfs%-size=([^,]+)") or nil
+ key = "tmpfs"
+ val = target .. ((tmpfs_mode or tmpfs_size) and (":" .. (tmpfs_mode and ("mode=" .. tmpfs_mode) or "") .. ((tmpfs_mode and tmpfs_size) and "," or "") .. (tmpfs_size and ("size=".. tmpfs_size) or "")) or "")
+ if not config[key] then
+ config[key] = {}
+ end
+ table.insert( config[key], val )
+ key = nil
+ val = nil
+ end
+ end
+ else
+ val = w
+ end
+ elseif is_cmd then
+ config["command"] = (config["command"] and (config["command"] .. " " )or "") .. w
+ end
+ if (key or _key) and val then
+ key = _key or key
+ if contains(key_with_list, key) then
+ if not config[key] then
+ config[key] = {}
+ end
+ if _key then
+ config[key][#config[key]] = config[key][#config[key]] .. " " .. w
+ else
+ table.insert( config[key], val )
+ end
+ if is_quot_complete(config[key][#config[key]]) then
+ config[key][#config[key]] = config[key][#config[key]]:gsub("[\"\']", "")
+ _key = nil
+ else
+ _key = key
+ end
+ else
+ config[key] = (config[key] and (config[key] .. " ") or "") .. val
+ if is_quot_complete(config[key]) then
+ config[key] = config[key]:gsub("[\"\']", "")
+ _key = nil
+ else
+ _key = key
+ end
+ end
+ key = nil
+ val = nil
+ end
+ end
+
+ return config
end
--- reslvo default config
+
local default_config = {}
+
if cmd_line and cmd_line:match("^DOCKERCLI.+") then
- default_config = resolve_cli(cmd_line)
+ default_config = resolve_cli(cmd_line)
elseif cmd_line and cmd_line:match("^duplicate/[^/]+$") then
- local container_id = cmd_line:match("^duplicate/(.+)")
- create_body = dk:containers_duplicate_config({id = container_id}) or {}
- if not create_body.HostConfig then create_body.HostConfig = {} end
- if next(create_body) ~= nil then
- default_config.name = nil
- default_config.image = create_body.Image
- default_config.hostname = create_body.Hostname
- default_config.tty = create_body.Tty and true or false
- default_config.interactive = create_body.OpenStdin and true or false
- default_config.privileged = create_body.HostConfig.Privileged and true or false
- default_config.restart = create_body.HostConfig.RestartPolicy and create_body.HostConfig.RestartPolicy.name or nil
- -- default_config.network = create_body.HostConfig.NetworkMode == "default" and "bridge" or create_body.HostConfig.NetworkMode
- -- if container has leave original network, and add new network, .HostConfig.NetworkMode is INcorrect, so using first child of .NetworkingConfig.EndpointsConfig
- default_config.network = create_body.NetworkingConfig and create_body.NetworkingConfig.EndpointsConfig and next(create_body.NetworkingConfig.EndpointsConfig) or nil
- default_config.ip = default_config.network and default_config.network ~= "bridge" and default_config.network ~= "host" and default_config.network ~= "null" and create_body.NetworkingConfig.EndpointsConfig[default_config.network].IPAMConfig and create_body.NetworkingConfig.EndpointsConfig[default_config.network].IPAMConfig.IPv4Address or nil
- default_config.link = create_body.HostConfig.Links
- default_config.env = create_body.Env
- default_config.dns = create_body.HostConfig.Dns
- default_config.volume = create_body.HostConfig.Binds
- default_config.cap_add = create_body.HostConfig.CapAdd
- default_config.publish_all = create_body.HostConfig.PublishAllPorts
-
- if create_body.HostConfig.Sysctls and type(create_body.HostConfig.Sysctls) == "table" then
- default_config.sysctl = {}
- for k, v in pairs(create_body.HostConfig.Sysctls) do
- table.insert( default_config.sysctl, k.."="..v )
- end
- end
-
- if create_body.HostConfig.LogConfig and create_body.HostConfig.LogConfig.Config and type(create_body.HostConfig.LogConfig.Config) == "table" then
- default_config.log_opt = {}
- for k, v in pairs(create_body.HostConfig.LogConfig.Config) do
- table.insert( default_config.log_opt, k.."="..v )
- end
- end
-
- if create_body.HostConfig.PortBindings and type(create_body.HostConfig.PortBindings) == "table" then
- default_config.publish = {}
- for k, v in pairs(create_body.HostConfig.PortBindings) do
- table.insert( default_config.publish, v[1].HostPort..":"..k:match("^(%d+)/.+").."/"..k:match("^%d+/(.+)") )
- end
- end
-
- default_config.user = create_body.User or nil
- default_config.command = create_body.Cmd and type(create_body.Cmd) == "table" and table.concat(create_body.Cmd, " ") or nil
- default_config.advance = 1
- default_config.cpus = create_body.HostConfig.NanoCPUs
- default_config.cpu_shares = create_body.HostConfig.CpuShares
- default_config.memory = create_body.HostConfig.Memory
- default_config.blkio_weight = create_body.HostConfig.BlkioWeight
-
- if create_body.HostConfig.Devices and type(create_body.HostConfig.Devices) == "table" then
- default_config.device = {}
- for _, v in ipairs(create_body.HostConfig.Devices) do
- table.insert( default_config.device, v.PathOnHost..":"..v.PathInContainer..(v.CgroupPermissions ~= "" and (":" .. v.CgroupPermissions) or "") )
- end
- end
- if create_body.HostConfig.Tmpfs and type(create_body.HostConfig.Tmpfs) == "table" then
- default_config.tmpfs = {}
- for k, v in pairs(create_body.HostConfig.Tmpfs) do
- table.insert( default_config.tmpfs, k .. (v~="" and ":" or "")..v )
- end
- end
- end
+ local container_id = cmd_line:match("^duplicate/(.+)")
+ create_body = dk:containers_duplicate_config({id = container_id}) or {}
+
+ if not create_body.HostConfig then
+ create_body.HostConfig = {}
+ end
+
+ if next(create_body) ~= nil then
+ default_config.name = nil
+ default_config.image = create_body.Image
+ default_config.hostname = create_body.Hostname
+ default_config.tty = create_body.Tty and true or false
+ default_config.interactive = create_body.OpenStdin and true or false
+ default_config.privileged = create_body.HostConfig.Privileged and true or false
+ default_config.restart = create_body.HostConfig.RestartPolicy and create_body.HostConfig.RestartPolicy.name or nil
+ default_config.network = create_body.NetworkingConfig and create_body.NetworkingConfig.EndpointsConfig and next(create_body.NetworkingConfig.EndpointsConfig) or nil
+ default_config.ip = default_config.network and default_config.network ~= "bridge" and default_config.network ~= "host" and default_config.network ~= "null" and create_body.NetworkingConfig.EndpointsConfig[default_config.network].IPAMConfig and create_body.NetworkingConfig.EndpointsConfig[default_config.network].IPAMConfig.IPv4Address or nil
+ default_config.link = create_body.HostConfig.Links
+ default_config.env = create_body.Env
+ default_config.dns = create_body.HostConfig.Dns
+ default_config.volume = create_body.HostConfig.Binds
+ default_config.cap_add = create_body.HostConfig.CapAdd
+ default_config.publish_all = create_body.HostConfig.PublishAllPorts
+
+ if create_body.HostConfig.Sysctls and type(create_body.HostConfig.Sysctls) == "table" then
+ default_config.sysctl = {}
+ for k, v in pairs(create_body.HostConfig.Sysctls) do
+ table.insert( default_config.sysctl, k.."="..v )
+ end
+ end
+
+ if create_body.HostConfig.LogConfig and create_body.HostConfig.LogConfig.Config and type(create_body.HostConfig.LogConfig.Config) == "table" then
+ default_config.log_opt = {}
+ for k, v in pairs(create_body.HostConfig.LogConfig.Config) do
+ table.insert( default_config.log_opt, k.."="..v )
+ end
+ end
+
+ if create_body.HostConfig.PortBindings and type(create_body.HostConfig.PortBindings) == "table" then
+ default_config.publish = {}
+ for k, v in pairs(create_body.HostConfig.PortBindings) do
+ table.insert( default_config.publish, v[1].HostPort..":"..k:match("^(%d+)/.+").."/"..k:match("^%d+/(.+)") )
+ end
+ end
+
+ default_config.user = create_body.User or nil
+ default_config.command = create_body.Cmd and type(create_body.Cmd) == "table" and table.concat(create_body.Cmd, " ") or nil
+ default_config.advance = 1
+ default_config.cpus = create_body.HostConfig.NanoCPUs
+ default_config.cpu_shares = create_body.HostConfig.CpuShares
+ default_config.memory = create_body.HostConfig.Memory
+ default_config.blkio_weight = create_body.HostConfig.BlkioWeight
+
+ if create_body.HostConfig.Devices and type(create_body.HostConfig.Devices) == "table" then
+ default_config.device = {}
+ for _, v in ipairs(create_body.HostConfig.Devices) do
+ table.insert( default_config.device, v.PathOnHost..":"..v.PathInContainer..(v.CgroupPermissions ~= "" and (":" .. v.CgroupPermissions) or "") )
+ end
+ end
+
+ if create_body.HostConfig.Tmpfs and type(create_body.HostConfig.Tmpfs) == "table" then
+ default_config.tmpfs = {}
+ for k, v in pairs(create_body.HostConfig.Tmpfs) do
+ table.insert( default_config.tmpfs, k .. (v~="" and ":" or "")..v )
+ end
+ end
+ end
end
-local m = SimpleForm("docker", translate("Docker"))
+m = SimpleForm("docker", translate("Docker"))
m.redirect = luci.dispatcher.build_url("admin", "docker", "containers")
--- m.reset = false
--- m.submit = false
--- new Container
-docker_status = m:section(SimpleSection)
-docker_status.template = "dockerman/apply_widget"
-docker_status.err=docker:read_status()
-docker_status.err=docker_status.err and docker_status.err:gsub("\n","<br>"):gsub(" ","&nbsp;")
-if docker_status.err then docker:clear_status() end
+s = m:section(SimpleSection)
+s.template = "dockerman/apply_widget"
+s.err=docker:read_status()
+s.err=s.err and s.err:gsub("\n","<br>"):gsub(" ","&nbsp;")
+if s.err then
+ docker:clear_status()
+end
-local s = m:section(SimpleSection, translate("New Container"))
+s = m:section(SimpleSection, translate("New Container"))
s.addremove = true
s.anonymous = true
-local d = s:option(DummyValue,"cmd_line", translate("Resolve CLI"))
-d.rawhtml = true
-d.template = "dockerman/newcontainer_resolve"
-
-d = s:option(Value, "name", translate("Container Name"))
-d.rmempty = true
-d.default = default_config.name or nil
-
-d = s:option(Flag, "interactive", translate("Interactive (-i)"))
-d.rmempty = true
-d.disabled = 0
-d.enabled = 1
-d.default = default_config.interactive and 1 or 0
-
-d = s:option(Flag, "tty", translate("TTY (-t)"))
-d.rmempty = true
-d.disabled = 0
-d.enabled = 1
-d.default = default_config.tty and 1 or 0
-
-d = s:option(Value, "image", translate("Docker Image"))
-d.rmempty = true
-d.default = default_config.image or nil
+o = s:option(DummyValue,"cmd_line", translate("Resolve CLI"))
+o.rawhtml = true
+o.template = "dockerman/newcontainer_resolve"
+
+o = s:option(Value, "name", translate("Container Name"))
+o.rmempty = true
+o.default = default_config.name or nil
+
+o = s:option(Flag, "interactive", translate("Interactive (-i)"))
+o.rmempty = true
+o.disabled = 0
+o.enabled = 1
+o.default = default_config.interactive and 1 or 0
+
+o = s:option(Flag, "tty", translate("TTY (-t)"))
+o.rmempty = true
+o.disabled = 0
+o.enabled = 1
+o.default = default_config.tty and 1 or 0
+
+o = s:option(Value, "image", translate("Docker Image"))
+o.rmempty = true
+o.default = default_config.image or nil
for _, v in ipairs (images) do
- if v.RepoTags then
- d:value(v.RepoTags[1], v.RepoTags[1])
- end
+ if v.RepoTags then
+ o:value(v.RepoTags[1], v.RepoTags[1])
+ end
end
-d = s:option(Flag, "_force_pull", translate("Always pull image first"))
-d.rmempty = true
-d.disabled = 0
-d.enabled = 1
-d.default = 0
-
-d = s:option(Flag, "privileged", translate("Privileged"))
-d.rmempty = true
-d.disabled = 0
-d.enabled = 1
-d.default = default_config.privileged and 1 or 0
-
-d = s:option(ListValue, "restart", translate("Restart Policy"))
-d.rmempty = true
-
-d:value("no", "No")
-d:value("unless-stopped", "Unless stopped")
-d:value("always", "Always")
-d:value("on-failure", "On failure")
-d.default = default_config.restart or "unless-stopped"
+o = s:option(Flag, "_force_pull", translate("Always pull image first"))
+o.rmempty = true
+o.disabled = 0
+o.enabled = 1
+o.default = 0
+
+o = s:option(Flag, "privileged", translate("Privileged"))
+o.rmempty = true
+o.disabled = 0
+o.enabled = 1
+o.default = default_config.privileged and 1 or 0
+
+o = s:option(ListValue, "restart", translate("Restart Policy"))
+o.rmempty = true
+o:value("no", "No")
+o:value("unless-stopped", "Unless stopped")
+o:value("always", "Always")
+o:value("on-failure", "On failure")
+o.default = default_config.restart or "unless-stopped"
local d_network = s:option(ListValue, "network", translate("Networks"))
d_network.rmempty = true
@@ -305,349 +509,394 @@ d_ip.datatype="ip4addr"
d_ip:depends("network", "nil")
d_ip.default = default_config.ip or nil
-d = s:option(DynamicList, "link", translate("Links with other containers"))
-d.placeholder = "container_name:alias"
-d.rmempty = true
-d:depends("network", "bridge")
-d.default = default_config.link or nil
-
-d = s:option(DynamicList, "dns", translate("Set custom DNS servers"))
-d.placeholder = "8.8.8.8"
-d.rmempty = true
-d.default = default_config.dns or nil
-
-d = s:option(Value, "user", translate("User(-u)"), translate("The user that commands are run as inside the container.(format: name|uid[:group|gid])"))
-d.placeholder = "1000:1000"
-d.rmempty = true
-d.default = default_config.user or nil
-
-d = s:option(DynamicList, "env", translate("Environmental Variable(-e)"), translate("Set environment variables to inside the container"))
-d.placeholder = "TZ=Asia/Shanghai"
-d.rmempty = true
-d.default = default_config.env or nil
-
-d = s:option(DynamicList, "volume", translate("Bind Mount(-v)"), translate("Bind mount a volume"))
-d.placeholder = "/media:/media:slave"
-d.rmempty = true
-d.default = default_config.volume or nil
-
-local d_publish = s:option(DynamicList, "publish", translate("Exposed Ports(-p)"), translate("Publish container's port(s) to the host"))
+o = s:option(DynamicList, "link", translate("Links with other containers"))
+o.placeholder = "container_name:alias"
+o.rmempty = true
+o:depends("network", "bridge")
+o.default = default_config.link or nil
+
+o = s:option(DynamicList, "dns", translate("Set custom DNS servers"))
+o.placeholder = "8.8.8.8"
+o.rmempty = true
+o.default = default_config.dns or nil
+
+o = s:option(Value, "user",
+ translate("User(-u)"),
+ translate("The user that commands are run as inside the container.(format: name|uid[:group|gid])"))
+o.placeholder = "1000:1000"
+o.rmempty = true
+o.default = default_config.user or nil
+
+o = s:option(DynamicList, "env",
+ translate("Environmental Variable(-e)"),
+ translate("Set environment variables to inside the container"))
+o.placeholder = "TZ=Asia/Shanghai"
+o.rmempty = true
+o.default = default_config.env or nil
+
+o = s:option(DynamicList, "volume",
+ translate("Bind Mount(-v)"),
+ translate("Bind mount a volume"))
+o.placeholder = "/media:/media:slave"
+o.rmempty = true
+o.default = default_config.volume or nil
+
+local d_publish = s:option(DynamicList, "publish",
+ translate("Exposed Ports(-p)"),
+ translate("Publish container's port(s) to the host"))
d_publish.placeholder = "2200:22/tcp"
d_publish.rmempty = true
d_publish.default = default_config.publish or nil
-d = s:option(Value, "command", translate("Run command"))
-d.placeholder = "/bin/sh init.sh"
-d.rmempty = true
-d.default = default_config.command or nil
-
-d = s:option(Flag, "advance", translate("Advance"))
-d.rmempty = true
-d.disabled = 0
-d.enabled = 1
-d.default = default_config.advance or 0
-
-d = s:option(Value, "hostname", translate("Host Name"), translate("The hostname to use for the container"))
-d.rmempty = true
-d.default = default_config.hostname or nil
-d:depends("advance", 1)
-
-d = s:option(Flag, "publish_all", translate("Exposed All Ports(-P)"), translate("Allocates an ephemeral host port for all of a container's exposed ports"))
-d.rmempty = true
-d.disabled = 0
-d.enabled = 1
-d.default = default_config.publish_all and 1 or 0
-d:depends("advance", 1)
-
-d = s:option(DynamicList, "device", translate("Device(--device)"), translate("Add host device to the container"))
-d.placeholder = "/dev/sda:/dev/xvdc:rwm"
-d.rmempty = true
-d:depends("advance", 1)
-d.default = default_config.device or nil
-
-d = s:option(DynamicList, "tmpfs", translate("Tmpfs(--tmpfs)"), translate("Mount tmpfs directory"))
-d.placeholder = "/run:rw,noexec,nosuid,size=65536k"
-d.rmempty = true
-d:depends("advance", 1)
-d.default = default_config.tmpfs or nil
-
-d = s:option(DynamicList, "sysctl", translate("Sysctl(--sysctl)"), translate("Sysctls (kernel parameters) options"))
-d.placeholder = "net.ipv4.ip_forward=1"
-d.rmempty = true
-d:depends("advance", 1)
-d.default = default_config.sysctl or nil
-
-d = s:option(DynamicList, "cap_add", translate("CAP-ADD(--cap-add)"), translate("A list of kernel capabilities to add to the container"))
-d.placeholder = "NET_ADMIN"
-d.rmempty = true
-d:depends("advance", 1)
-d.default = default_config.cap_add or nil
-
-d = s:option(Value, "cpus", translate("CPUs"), translate("Number of CPUs. Number is a fractional number. 0.000 means no limit"))
-d.placeholder = "1.5"
-d.rmempty = true
-d:depends("advance", 1)
-d.datatype="ufloat"
-d.default = default_config.cpus or nil
-
-d = s:option(Value, "cpu_shares", translate("CPU Shares Weight"), translate("CPU shares relative weight, if 0 is set, the system will ignore the value and use the default of 1024"))
-d.placeholder = "1024"
-d.rmempty = true
-d:depends("advance", 1)
-d.datatype="uinteger"
-d.default = default_config.cpu_shares or nil
-
-d = s:option(Value, "memory", translate("Memory"), translate("Memory limit (format: <number>[<unit>]). Number is a positive integer. Unit can be one of b, k, m, or g. Minimum is 4M"))
-d.placeholder = "128m"
-d.rmempty = true
-d:depends("advance", 1)
-d.default = default_config.memory or nil
-
-d = s:option(Value, "blkio_weight", translate("Block IO Weight"), translate("Block IO weight (relative weight) accepts a weight value between 10 and 1000"))
-d.placeholder = "500"
-d.rmempty = true
-d:depends("advance", 1)
-d.datatype="uinteger"
-d.default = default_config.blkio_weight or nil
-
-d = s:option(DynamicList, "log_opt", translate("Log driver options"), translate("The logging configuration for this container"))
-d.placeholder = "max-size=1m"
-d.rmempty = true
-d:depends("advance", 1)
-d.default = default_config.log_opt or nil
+o = s:option(Value, "command", translate("Run command"))
+o.placeholder = "/bin/sh init.sh"
+o.rmempty = true
+o.default = default_config.command or nil
+
+o = s:option(Flag, "advance", translate("Advance"))
+o.rmempty = true
+o.disabled = 0
+o.enabled = 1
+o.default = default_config.advance or 0
+
+o = s:option(Value, "hostname",
+ translate("Host Name"),
+ translate("The hostname to use for the container"))
+o.rmempty = true
+o.default = default_config.hostname or nil
+o:depends("advance", 1)
+
+o = s:option(Flag, "publish_all",
+ translate("Exposed All Ports(-P)"),
+ translate("Allocates an ephemeral host port for all of a container's exposed ports"))
+o.rmempty = true
+o.disabled = 0
+o.enabled = 1
+o.default = default_config.publish_all and 1 or 0
+o:depends("advance", 1)
+
+o = s:option(DynamicList, "device",
+ translate("Device(--device)"),
+ translate("Add host device to the container"))
+o.placeholder = "/dev/sda:/dev/xvdc:rwm"
+o.rmempty = true
+o:depends("advance", 1)
+o.default = default_config.device or nil
+
+o = s:option(DynamicList, "tmpfs",
+ translate("Tmpfs(--tmpfs)"),
+ translate("Mount tmpfs directory"))
+o.placeholder = "/run:rw,noexec,nosuid,size=65536k"
+o.rmempty = true
+o:depends("advance", 1)
+o.default = default_config.tmpfs or nil
+
+o = s:option(DynamicList, "sysctl",
+ translate("Sysctl(--sysctl)"),
+ translate("Sysctls (kernel parameters) options"))
+o.placeholder = "net.ipv4.ip_forward=1"
+o.rmempty = true
+o:depends("advance", 1)
+o.default = default_config.sysctl or nil
+
+o = s:option(DynamicList, "cap_add",
+ translate("CAP-ADD(--cap-add)"),
+ translate("A list of kernel capabilities to add to the container"))
+o.placeholder = "NET_ADMIN"
+o.rmempty = true
+o:depends("advance", 1)
+o.default = default_config.cap_add or nil
+
+o = s:option(Value, "cpus",
+ translate("CPUs"),
+ translate("Number of CPUs. Number is a fractional number. 0.000 means no limit"))
+o.placeholder = "1.5"
+o.rmempty = true
+o:depends("advance", 1)
+o.datatype="ufloat"
+o.default = default_config.cpus or nil
+
+o = s:option(Value, "cpu_shares",
+ translate("CPU Shares Weight"),
+ translate("CPU shares relative weight, if 0 is set, the system will ignore the value and use the default of 1024"))
+o.placeholder = "1024"
+o.rmempty = true
+o:depends("advance", 1)
+o.datatype="uinteger"
+o.default = default_config.cpu_shares or nil
+
+o = s:option(Value, "memory",
+ translate("Memory"),
+ translate("Memory limit (format: <number>[<unit>]). Number is a positive integer. Unit can be one of b, k, m, or g. Minimum is 4M"))
+o.placeholder = "128m"
+o.rmempty = true
+o:depends("advance", 1)
+o.default = default_config.memory or nil
+
+o = s:option(Value, "blkio_weight",
+ translate("Block IO Weight"),
+ translate("Block IO weight (relative weight) accepts a weight value between 10 and 1000"))
+o.placeholder = "500"
+o.rmempty = true
+o:depends("advance", 1)
+o.datatype="uinteger"
+o.default = default_config.blkio_weight or nil
+
+o = s:option(DynamicList, "log_opt",
+ translate("Log driver options"),
+ translate("The logging configuration for this container"))
+o.placeholder = "max-size=1m"
+o.rmempty = true
+o:depends("advance", 1)
+o.default = default_config.log_opt or nil
for _, v in ipairs (networks) do
- if v.Name then
- local parent = v.Options and v.Options.parent or nil
- local ip = v.IPAM and v.IPAM.Config and v.IPAM.Config[1] and v.IPAM.Config[1].Subnet or nil
- ipv6 = v.IPAM and v.IPAM.Config and v.IPAM.Config[2] and v.IPAM.Config[2].Subnet or nil
- local network_name = v.Name .. " | " .. v.Driver .. (parent and (" | " .. parent) or "") .. (ip and (" | " .. ip) or "").. (ipv6 and (" | " .. ipv6) or "")
- d_network:value(v.Name, network_name)
-
- if v.Name ~= "none" and v.Name ~= "bridge" and v.Name ~= "host" then
- d_ip:depends("network", v.Name)
- end
-
- if v.Driver == "bridge" then
- d_publish:depends("network", v.Name)
- end
- end
+ if v.Name then
+ local parent = v.Options and v.Options.parent or nil
+ local ip = v.IPAM and v.IPAM.Config and v.IPAM.Config[1] and v.IPAM.Config[1].Subnet or nil
+ ipv6 = v.IPAM and v.IPAM.Config and v.IPAM.Config[2] and v.IPAM.Config[2].Subnet or nil
+ local network_name = v.Name .. " | " .. v.Driver .. (parent and (" | " .. parent) or "") .. (ip and (" | " .. ip) or "").. (ipv6 and (" | " .. ipv6) or "")
+ d_network:value(v.Name, network_name)
+
+ if v.Name ~= "none" and v.Name ~= "bridge" and v.Name ~= "host" then
+ d_ip:depends("network", v.Name)
+ end
+
+ if v.Driver == "bridge" then
+ d_publish:depends("network", v.Name)
+ end
+ end
end
m.handle = function(self, state, data)
- if state ~= FORM_VALID then return end
- local tmp
- local name = data.name or ("luci_" .. os.date("%Y%m%d%H%M%S"))
- local hostname = data.hostname
- local tty = type(data.tty) == "number" and (data.tty == 1 and true or false) or default_config.tty or false
- local publish_all = type(data.publish_all) == "number" and (data.publish_all == 1 and true or false) or default_config.publish_all or false
- local interactive = type(data.interactive) == "number" and (data.interactive == 1 and true or false) or default_config.interactive or false
- local image = data.image
- local user = data.user
- if image and not image:match(".-:.+") then
- image = image .. ":latest"
- end
- local privileged = type(data.privileged) == "number" and (data.privileged == 1 and true or false) or default_config.privileged or false
- local restart = data.restart
- local env = data.env
- local dns = data.dns
- local cap_add = data.cap_add
- local sysctl = {}
- tmp = data.sysctl
- if type(tmp) == "table" then
- for i, v in ipairs(tmp) do
- local k,v1 = v:match("(.-)=(.+)")
- if k and v1 then
- sysctl[k]=v1
- end
- end
- end
- local log_opt = {}
- tmp = data.log_opt
- if type(tmp) == "table" then
- for i, v in ipairs(tmp) do
- local k,v1 = v:match("(.-)=(.+)")
- if k and v1 then
- log_opt[k]=v1
- end
- end
- end
- local network = data.network
- local ip = (network ~= "bridge" and network ~= "host" and network ~= "none") and data.ip or nil
- local volume = data.volume
- local memory = data.memory or 0
- local cpu_shares = data.cpu_shares or 0
- local cpus = data.cpus or 0
- local blkio_weight = data.blkio_weight or 500
-
- local portbindings = {}
- local exposedports = {}
- local tmpfs = {}
- tmp = data.tmpfs
- if type(tmp) == "table" then
- for i, v in ipairs(tmp)do
- local k= v:match("([^:]+)")
- local v1 = v:match(".-:([^:]+)") or ""
- if k then
- tmpfs[k]=v1
- end
- end
- end
-
- local device = {}
- tmp = data.device
- if type(tmp) == "table" then
- for i, v in ipairs(tmp) do
- local t = {}
- local _,_, h, c, p = v:find("(.-):(.-):(.+)")
- if h and c then
- t['PathOnHost'] = h
- t['PathInContainer'] = c
- t['CgroupPermissions'] = p or "rwm"
- else
- local _,_, h, c = v:find("(.-):(.+)")
- if h and c then
- t['PathOnHost'] = h
- t['PathInContainer'] = c
- t['CgroupPermissions'] = "rwm"
- else
- t['PathOnHost'] = v
- t['PathInContainer'] = v
- t['CgroupPermissions'] = "rwm"
- end
- end
- if next(t) ~= nil then
- table.insert( device, t )
- end
- end
- end
-
- tmp = data.publish or {}
- for i, v in ipairs(tmp) do
- for v1 ,v2 in string.gmatch(v, "(%d+):([^%s]+)") do
- local _,_,p= v2:find("^%d+/(%w+)")
- if p == nil then
- v2=v2..'/tcp'
- end
- portbindings[v2] = {{HostPort=v1}}
- exposedports[v2] = {HostPort=v1}
- end
- end
-
- local link = data.link
- tmp = data.command
- local command = {}
- if tmp ~= nil then
- for v in string.gmatch(tmp, "[^%s]+") do
- command[#command+1] = v
- end
- end
- if memory ~= 0 then
- _,_,n,unit = memory:find("([%d%.]+)([%l%u]+)")
- if n then
- unit = unit and unit:sub(1,1):upper() or "B"
- if unit == "M" then
- memory = tonumber(n) * 1024 * 1024
- elseif unit == "G" then
- memory = tonumber(n) * 1024 * 1024 * 1024
- elseif unit == "K" then
- memory = tonumber(n) * 1024
- else
- memory = tonumber(n)
- end
- end
- end
-
- create_body.Hostname = network ~= "host" and (hostname or name) or nil
- create_body.Tty = tty and true or false
- create_body.OpenStdin = interactive and true or false
- create_body.User = user
- create_body.Cmd = command
- create_body.Env = env
- create_body.Image = image
- create_body.ExposedPorts = exposedports
- create_body.HostConfig = create_body.HostConfig or {}
- create_body.HostConfig.Dns = dns
- create_body.HostConfig.Binds = volume
- create_body.HostConfig.RestartPolicy = { Name = restart, MaximumRetryCount = 0 }
- create_body.HostConfig.Privileged = privileged and true or false
- create_body.HostConfig.PortBindings = portbindings
- create_body.HostConfig.Memory = tonumber(memory)
- create_body.HostConfig.CpuShares = tonumber(cpu_shares)
- create_body.HostConfig.NanoCPUs = tonumber(cpus) * 10 ^ 9
- create_body.HostConfig.BlkioWeight = tonumber(blkio_weight)
- create_body.HostConfig.PublishAllPorts = publish_all
- if create_body.HostConfig.NetworkMode ~= network then
- -- network mode changed, need to clear duplicate config
- create_body.NetworkingConfig = nil
- end
- create_body.HostConfig.NetworkMode = network
- if ip then
- if create_body.NetworkingConfig and create_body.NetworkingConfig.EndpointsConfig and type(create_body.NetworkingConfig.EndpointsConfig) == "table" then
- -- ip + duplicate config
- for k, v in pairs (create_body.NetworkingConfig.EndpointsConfig) do
- if k == network and v.IPAMConfig and v.IPAMConfig.IPv4Address then
- v.IPAMConfig.IPv4Address = ip
- else
- create_body.NetworkingConfig.EndpointsConfig = { [network] = { IPAMConfig = { IPv4Address = ip } } }
- end
- break
- end
- else
- -- ip + no duplicate config
- create_body.NetworkingConfig = { EndpointsConfig = { [network] = { IPAMConfig = { IPv4Address = ip } } } }
- end
- elseif not create_body.NetworkingConfig then
- -- no ip + no duplicate config
- create_body.NetworkingConfig = nil
- end
- create_body["HostConfig"]["Tmpfs"] = tmpfs
- create_body["HostConfig"]["Devices"] = device
- create_body["HostConfig"]["Sysctls"] = sysctl
- create_body["HostConfig"]["CapAdd"] = cap_add
- create_body["HostConfig"]["LogConfig"] = next(log_opt) ~= nil and { Config = log_opt } or nil
-
- if network == "bridge" then
- create_body["HostConfig"]["Links"] = link
- end
- local pull_image = function(image)
- local json_stringify = luci.jsonc and luci.jsonc.stringify
- docker:append_status("Images: " .. "pulling" .. " " .. image .. "...\n")
- local res = dk.images:create({query = {fromImage=image}}, docker.pull_image_show_status_cb)
- if res and res.code == 200 and (res.body[#res.body] and not res.body[#res.body].error and res.body[#res.body].status and (res.body[#res.body].status == "Status: Downloaded newer image for ".. image or res.body[#res.body].status == "Status: Image is up to date for ".. image)) then
- docker:append_status("done\n")
- else
- res.code = (res.code == 200) and 500 or res.code
- docker:append_status("code:" .. res.code.." ".. (res.body[#res.body] and res.body[#res.body].error or (res.body.message or res.message)).. "\n")
- luci.http.redirect(luci.dispatcher.build_url("admin/docker/newcontainer"))
- end
- end
- docker:clear_status()
- local exist_image = false
- if image then
- for _, v in ipairs (images) do
- if v.RepoTags and v.RepoTags[1] == image then
- exist_image = true
- break
- end
- end
- if not exist_image then
- pull_image(image)
- elseif data._force_pull == 1 then
- pull_image(image)
- end
- end
-
- create_body = docker.clear_empty_tables(create_body)
- docker:append_status("Container: " .. "create" .. " " .. name .. "...")
- local res = dk.containers:create({name = name, body = create_body})
- if res and res.code == 201 then
- docker:clear_status()
- luci.http.redirect(luci.dispatcher.build_url("admin/docker/containers"))
- else
- docker:append_status("code:" .. res.code.." ".. (res.body.message and res.body.message or res.message))
- luci.http.redirect(luci.dispatcher.build_url("admin/docker/newcontainer"))
- end
+ if state ~= FORM_VALID then
+ return
+ end
+
+ local tmp
+ local name = data.name or ("luci_" .. os.date("%Y%m%d%H%M%S"))
+ local hostname = data.hostname
+ local tty = type(data.tty) == "number" and (data.tty == 1 and true or false) or default_config.tty or false
+ local publish_all = type(data.publish_all) == "number" and (data.publish_all == 1 and true or false) or default_config.publish_all or false
+ local interactive = type(data.interactive) == "number" and (data.interactive == 1 and true or false) or default_config.interactive or false
+ local image = data.image
+ local user = data.user
+
+ if image and not image:match(".-:.+") then
+ image = image .. ":latest"
+ end
+
+ local privileged = type(data.privileged) == "number" and (data.privileged == 1 and true or false) or default_config.privileged or false
+ local restart = data.restart
+ local env = data.env
+ local dns = data.dns
+ local cap_add = data.cap_add
+ local sysctl = {}
+
+ tmp = data.sysctl
+ if type(tmp) == "table" then
+ for i, v in ipairs(tmp) do
+ local k,v1 = v:match("(.-)=(.+)")
+ if k and v1 then
+ sysctl[k]=v1
+ end
+ end
+ end
+
+ local log_opt = {}
+ tmp = data.log_opt
+ if type(tmp) == "table" then
+ for i, v in ipairs(tmp) do
+ local k,v1 = v:match("(.-)=(.+)")
+ if k and v1 then
+ log_opt[k]=v1
+ end
+ end
+ end
+
+ local network = data.network
+ local ip = (network ~= "bridge" and network ~= "host" and network ~= "none") and data.ip or nil
+ local volume = data.volume
+ local memory = data.memory or 0
+ local cpu_shares = data.cpu_shares or 0
+ local cpus = data.cpus or 0
+ local blkio_weight = data.blkio_weight or 500
+
+ local portbindings = {}
+ local exposedports = {}
+
+ local tmpfs = {}
+ tmp = data.tmpfs
+ if type(tmp) == "table" then
+ for i, v in ipairs(tmp)do
+ local k= v:match("([^:]+)")
+ local v1 = v:match(".-:([^:]+)") or ""
+ if k then
+ tmpfs[k]=v1
+ end
+ end
+ end
+
+ local device = {}
+ tmp = data.device
+ if type(tmp) == "table" then
+ for i, v in ipairs(tmp) do
+ local t = {}
+ local _,_, h, c, p = v:find("(.-):(.-):(.+)")
+ if h and c then
+ t['PathOnHost'] = h
+ t['PathInContainer'] = c
+ t['CgroupPermissions'] = p or "rwm"
+ else
+ local _,_, h, c = v:find("(.-):(.+)")
+ if h and c then
+ t['PathOnHost'] = h
+ t['PathInContainer'] = c
+ t['CgroupPermissions'] = "rwm"
+ else
+ t['PathOnHost'] = v
+ t['PathInContainer'] = v
+ t['CgroupPermissions'] = "rwm"
+ end
+ end
+
+ if next(t) ~= nil then
+ table.insert( device, t )
+ end
+ end
+ end
+
+ tmp = data.publish or {}
+ for i, v in ipairs(tmp) do
+ for v1 ,v2 in string.gmatch(v, "(%d+):([^%s]+)") do
+ local _,_,p= v2:find("^%d+/(%w+)")
+ if p == nil then
+ v2=v2..'/tcp'
+ end
+ portbindings[v2] = {{HostPort=v1}}
+ exposedports[v2] = {HostPort=v1}
+ end
+ end
+
+ local link = data.link
+ tmp = data.command
+ local command = {}
+ if tmp ~= nil then
+ for v in string.gmatch(tmp, "[^%s]+") do
+ command[#command+1] = v
+ end
+ end
+
+ if memory ~= 0 then
+ _,_,n,unit = memory:find("([%d%.]+)([%l%u]+)")
+ if n then
+ unit = unit and unit:sub(1,1):upper() or "B"
+ if unit == "M" then
+ memory = tonumber(n) * 1024 * 1024
+ elseif unit == "G" then
+ memory = tonumber(n) * 1024 * 1024 * 1024
+ elseif unit == "K" then
+ memory = tonumber(n) * 1024
+ else
+ memory = tonumber(n)
+ end
+ end
+ end
+
+ create_body.Hostname = network ~= "host" and (hostname or name) or nil
+ create_body.Tty = tty and true or false
+ create_body.OpenStdin = interactive and true or false
+ create_body.User = user
+ create_body.Cmd = command
+ create_body.Env = env
+ create_body.Image = image
+ create_body.ExposedPorts = exposedports
+ create_body.HostConfig = create_body.HostConfig or {}
+ create_body.HostConfig.Dns = dns
+ create_body.HostConfig.Binds = volume
+ create_body.HostConfig.RestartPolicy = { Name = restart, MaximumRetryCount = 0 }
+ create_body.HostConfig.Privileged = privileged and true or false
+ create_body.HostConfig.PortBindings = portbindings
+ create_body.HostConfig.Memory = tonumber(memory)
+ create_body.HostConfig.CpuShares = tonumber(cpu_shares)
+ create_body.HostConfig.NanoCPUs = tonumber(cpus) * 10 ^ 9
+ create_body.HostConfig.BlkioWeight = tonumber(blkio_weight)
+ create_body.HostConfig.PublishAllPorts = publish_all
+
+ if create_body.HostConfig.NetworkMode ~= network then
+ create_body.NetworkingConfig = nil
+ end
+
+ create_body.HostConfig.NetworkMode = network
+
+ if ip then
+ if create_body.NetworkingConfig and create_body.NetworkingConfig.EndpointsConfig and type(create_body.NetworkingConfig.EndpointsConfig) == "table" then
+ for k, v in pairs (create_body.NetworkingConfig.EndpointsConfig) do
+ if k == network and v.IPAMConfig and v.IPAMConfig.IPv4Address then
+ v.IPAMConfig.IPv4Address = ip
+ else
+ create_body.NetworkingConfig.EndpointsConfig = { [network] = { IPAMConfig = { IPv4Address = ip } } }
+ end
+ break
+ end
+ else
+ create_body.NetworkingConfig = { EndpointsConfig = { [network] = { IPAMConfig = { IPv4Address = ip } } } }
+ end
+ elseif not create_body.NetworkingConfig then
+ create_body.NetworkingConfig = nil
+ end
+
+ create_body["HostConfig"]["Tmpfs"] = tmpfs
+ create_body["HostConfig"]["Devices"] = device
+ create_body["HostConfig"]["Sysctls"] = sysctl
+ create_body["HostConfig"]["CapAdd"] = cap_add
+ create_body["HostConfig"]["LogConfig"] = next(log_opt) ~= nil and { Config = log_opt } or nil
+
+ if network == "bridge" then
+ create_body["HostConfig"]["Links"] = link
+ end
+
+ local pull_image = function(image)
+ local json_stringify = luci.jsonc and luci.jsonc.stringify
+ docker:append_status("Images: " .. "pulling" .. " " .. image .. "...\n")
+ local res = dk.images:create({query = {fromImage=image}}, docker.pull_image_show_status_cb)
+ if res and res.code == 200 and (res.body[#res.body] and not res.body[#res.body].error and res.body[#res.body].status and (res.body[#res.body].status == "Status: Downloaded newer image for ".. image or res.body[#res.body].status == "Status: Image is up to date for ".. image)) then
+ docker:append_status("done\n")
+ else
+ res.code = (res.code == 200) and 500 or res.code
+ docker:append_status("code:" .. res.code.." ".. (res.body[#res.body] and res.body[#res.body].error or (res.body.message or res.message)).. "\n")
+ luci.http.redirect(luci.dispatcher.build_url("admin/docker/newcontainer"))
+ end
+ end
+
+ docker:clear_status()
+ local exist_image = false
+
+ if image then
+ for _, v in ipairs (images) do
+ if v.RepoTags and v.RepoTags[1] == image then
+ exist_image = true
+ break
+ end
+ end
+ if not exist_image then
+ pull_image(image)
+ elseif data._force_pull == 1 then
+ pull_image(image)
+ end
+ end
+
+ create_body = docker.clear_empty_tables(create_body)
+
+ docker:append_status("Container: " .. "create" .. " " .. name .. "...")
+ local res = dk.containers:create({name = name, body = create_body})
+ if res and res.code == 201 then
+ docker:clear_status()
+ luci.http.redirect(luci.dispatcher.build_url("admin/docker/containers"))
+ else
+ docker:append_status("code:" .. res.code.." ".. (res.body.message and res.body.message or res.message))
+ luci.http.redirect(luci.dispatcher.build_url("admin/docker/newcontainer"))
+ end
end
return m
diff --git a/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/newnetwork.lua b/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/newnetwork.lua
index bdadbaf881..f4cfea5bc5 100644
--- a/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/newnetwork.lua
+++ b/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/newnetwork.lua
@@ -3,219 +3,244 @@ LuCI - Lua Configuration Interface
Copyright 2019 lisaac <https://github.com/lisaac/luci-app-dockerman>
]]--
-require "luci.util"
local docker = require "luci.model.docker"
+
+local m, s, o
+
local dk = docker.new()
m = SimpleForm("docker", translate("Docker"))
m.redirect = luci.dispatcher.build_url("admin", "docker", "networks")
-docker_status = m:section(SimpleSection)
-docker_status.template = "dockerman/apply_widget"
-docker_status.err=docker:read_status()
-docker_status.err=docker_status.err and docker_status.err:gsub("\n","<br>"):gsub(" ","&nbsp;")
-if docker_status.err then docker:clear_status() end
+s = m:section(SimpleSection)
+s.template = "dockerman/apply_widget"
+s.err=docker:read_status()
+s.err=s.err and s.err:gsub("\n","<br>"):gsub(" ","&nbsp;")
+if s.err then
+ docker:clear_status()
+end
s = m:section(SimpleSection, translate("New Network"))
s.addremove = true
s.anonymous = true
-d = s:option(Value, "name", translate("Network Name"))
-d.rmempty = true
+o = s:option(Value, "name", translate("Network Name"))
+o.rmempty = true
-d = s:option(ListValue, "dirver", translate("Driver"))
-d.rmempty = true
-d:value("bridge", "bridge")
-d:value("macvlan", "macvlan")
-d:value("ipvlan", "ipvlan")
-d:value("overlay", "overlay")
+o = s:option(ListValue, "dirver", translate("Driver"))
+o.rmempty = true
+o:value("bridge", "bridge")
+o:value("macvlan", "macvlan")
+o:value("ipvlan", "ipvlan")
+o:value("overlay", "overlay")
-d = s:option(Value, "parent", translate("Parent Interface"))
-d.rmempty = true
-d:depends("dirver", "macvlan")
+o = s:option(Value, "parent", translate("Parent Interface"))
+o.rmempty = true
+o:depends("dirver", "macvlan")
local interfaces = luci.sys and luci.sys.net and luci.sys.net.devices() or {}
for _, v in ipairs(interfaces) do
- d:value(v, v)
+ o:value(v, v)
end
-d.default="br-lan"
-d.placeholder="br-lan"
-
-d = s:option(Value, "macvlan_mode", translate("Macvlan Mode"))
-d.rmempty = true
-d:depends("dirver", "macvlan")
-d.default="bridge"
-d:value("bridge", "bridge")
-d:value("private", "private")
-d:value("vepa", "vepa")
-d:value("passthru", "passthru")
-
-d = s:option(Value, "ipvlan_mode", translate("Ipvlan Mode"))
-d.rmempty = true
-d:depends("dirver", "ipvlan")
-d.default="l3"
-d:value("l2", "l2")
-d:value("l3", "l3")
-
-d = s:option(Flag, "ingress", translate("Ingress"), translate("Ingress network is the network which provides the routing-mesh in swarm mode"))
-d.rmempty = true
-d.disabled = 0
-d.enabled = 1
-d.default = 0
-d:depends("dirver", "overlay")
-
-d = s:option(DynamicList, "options", translate("Options"))
-d.rmempty = true
-d.placeholder="com.docker.network.driver.mtu=1500"
-
-d = s:option(Flag, "internal", translate("Internal"), translate("Restrict external access to the network"))
-d.rmempty = true
-d:depends("dirver", "overlay")
-d.disabled = 0
-d.enabled = 1
-d.default = 0
-
-if nixio.fs.access("/etc/config/network") and nixio.fs.access("/etc/config/firewall")then
- d = s:option(Flag, "op_macvlan", translate("Create macvlan interface"), translate("Auto create macvlan interface in Openwrt"))
- d:depends("dirver", "macvlan")
- d.disabled = 0
- d.enabled = 1
- d.default = 1
+o.default="br-lan"
+o.placeholder="br-lan"
+
+o = s:option(Value, "macvlan_mode", translate("Macvlan Mode"))
+o.rmempty = true
+o:depends("dirver", "macvlan")
+o.default="bridge"
+o:value("bridge", "bridge")
+o:value("private", "private")
+o:value("vepa", "vepa")
+o:value("passthru", "passthru")
+
+o = s:option(Value, "ipvlan_mode", translate("Ipvlan Mode"))
+o.rmempty = true
+o:depends("dirver", "ipvlan")
+o.default="l3"
+o:value("l2", "l2")
+o:value("l3", "l3")
+
+o = s:option(Flag, "ingress",
+ translate("Ingress"),
+ translate("Ingress network is the network which provides the routing-mesh in swarm mode"))
+o.rmempty = true
+o.disabled = 0
+o.enabled = 1
+o.default = 0
+o:depends("dirver", "overlay")
+
+o = s:option(DynamicList, "options", translate("Options"))
+o.rmempty = true
+o.placeholder="com.docker.network.driver.mtu=1500"
+
+o = s:option(Flag, "internal", translate("Internal"), translate("Restrict external access to the network"))
+o.rmempty = true
+o:depends("dirver", "overlay")
+o.disabled = 0
+o.enabled = 1
+o.default = 0
+
+if nixio.fs.access("/etc/config/network") and nixio.fs.access("/etc/config/firewall")then
+ o = s:option(Flag, "op_macvlan", translate("Create macvlan interface"), translate("Auto create macvlan interface in Openwrt"))
+ o:depends("dirver", "macvlan")
+ o.disabled = 0
+ o.enabled = 1
+ o.default = 1
end
-d = s:option(Value, "subnet", translate("Subnet"))
-d.rmempty = true
-d.placeholder="10.1.0.0/16"
-d.datatype="ip4addr"
-
-d = s:option(Value, "gateway", translate("Gateway"))
-d.rmempty = true
-d.placeholder="10.1.1.1"
-d.datatype="ip4addr"
-
-d = s:option(Value, "ip_range", translate("IP range"))
-d.rmempty = true
-d.placeholder="10.1.1.0/24"
-d.datatype="ip4addr"
-
-d = s:option(DynamicList, "aux_address", translate("Exclude IPs"))
-d.rmempty = true
-d.placeholder="my-route=10.1.1.1"
-
-d = s:option(Flag, "ipv6", translate("Enable IPv6"))
-d.rmempty = true
-d.disabled = 0
-d.enabled = 1
-d.default = 0
-
-d = s:option(Value, "subnet6", translate("IPv6 Subnet"))
-d.rmempty = true
-d.placeholder="fe80::/10"
-d.datatype="ip6addr"
-d:depends("ipv6", 1)
-
-d = s:option(Value, "gateway6", translate("IPv6 Gateway"))
-d.rmempty = true
-d.placeholder="fe80::1"
-d.datatype="ip6addr"
-d:depends("ipv6", 1)
+o = s:option(Value, "subnet", translate("Subnet"))
+o.rmempty = true
+o.placeholder="10.1.0.0/16"
+o.datatype="ip4addr"
+
+o = s:option(Value, "gateway", translate("Gateway"))
+o.rmempty = true
+o.placeholder="10.1.1.1"
+o.datatype="ip4addr"
+
+o = s:option(Value, "ip_range", translate("IP range"))
+o.rmempty = true
+o.placeholder="10.1.1.0/24"
+o.datatype="ip4addr"
+
+o = s:option(DynamicList, "aux_address", translate("Exclude IPs"))
+o.rmempty = true
+o.placeholder="my-route=10.1.1.1"
+
+o = s:option(Flag, "ipv6", translate("Enable IPv6"))
+o.rmempty = true
+o.disabled = 0
+o.enabled = 1
+o.default = 0
+
+o = s:option(Value, "subnet6", translate("IPv6 Subnet"))
+o.rmempty = true
+o.placeholder="fe80::/10"
+o.datatype="ip6addr"
+o:depends("ipv6", 1)
+
+o = s:option(Value, "gateway6", translate("IPv6 Gateway"))
+o.rmempty = true
+o.placeholder="fe80::1"
+o.datatype="ip6addr"
+o:depends("ipv6", 1)
m.handle = function(self, state, data)
- if state == FORM_VALID then
- local name = data.name
- local driver = data.dirver
-
- local internal = data.internal == 1 and true or false
-
- local subnet = data.subnet
- local gateway = data.gateway
- local ip_range = data.ip_range
-
- local aux_address = {}
- local tmp = data.aux_address or {}
- for i,v in ipairs(tmp) do
- _,_,k1,v1 = v:find("(.-)=(.+)")
- aux_address[k1] = v1
- end
-
- local options = {}
- tmp = data.options or {}
- for i,v in ipairs(tmp) do
- _,_,k1,v1 = v:find("(.-)=(.+)")
- options[k1] = v1
- end
-
- local ipv6 = data.ipv6 == 1 and true or false
-
- local create_body={
- Name = name,
- Driver = driver,
- EnableIPv6 = ipv6,
- IPAM = {
- Driver= "default"
- },
- Internal = internal
- }
-
- if subnet or gateway or ip_range then
- create_body["IPAM"]["Config"] = {
- {
- Subnet = subnet,
- Gateway = gateway,
- IPRange = ip_range,
- AuxAddress = aux_address,
- AuxiliaryAddresses = aux_address
- }
- }
- end
- if driver == "macvlan" then
- create_body["Options"] = {
- macvlan_mode = data.macvlan_mode,
- parent = data.parent
- }
- elseif driver == "ipvlan" then
- create_body["Options"] = {
- ipvlan_mode = data.ipvlan_mode
- }
- elseif driver == "overlay" then
- create_body["Ingress"] = data.ingerss == 1 and true or false
- end
-
- if ipv6 and data.subnet6 and data.subnet6 then
- if type(create_body["IPAM"]["Config"]) ~= "table" then
- create_body["IPAM"]["Config"] = {}
- end
- local index = #create_body["IPAM"]["Config"]
- create_body["IPAM"]["Config"][index+1] = {
- Subnet = data.subnet6,
- Gateway = data.gateway6
- }
- end
-
- if next(options) ~= nil then
- create_body["Options"] = create_body["Options"] or {}
- for k, v in pairs(options) do
- create_body["Options"][k] = v
- end
- end
-
- create_body = docker.clear_empty_tables(create_body)
- docker:write_status("Network: " .. "create" .. " " .. create_body.Name .. "...")
- local res = dk.networks:create({body = create_body})
- if res and res.code == 201 then
- docker:write_status("Network: " .. "create macvlan interface...")
- res = dk.networks:inspect({ name = create_body.Name })
- if driver == "macvlan" and data.op_macvlan ~= 0 and res.code == 200
- and res.body and res.body.IPAM and res.body.IPAM.Config and res.body.IPAM.Config[1]
- and res.body.IPAM.Config[1].Gateway and res.body.IPAM.Config[1].Subnet then
- docker.create_macvlan_interface(data.name, data.parent, res.body.IPAM.Config[1].Gateway, res.body.IPAM.Config[1].Subnet)
- end
- docker:clear_status()
- luci.http.redirect(luci.dispatcher.build_url("admin/docker/networks"))
- else
- docker:append_status("code:" .. res.code.." ".. (res.body.message and res.body.message or res.message).. "\n")
- luci.http.redirect(luci.dispatcher.build_url("admin/docker/newnetwork"))
- end
- end
+ if state == FORM_VALID then
+ local name = data.name
+ local driver = data.dirver
+
+ local internal = data.internal == 1 and true or false
+
+ local subnet = data.subnet
+ local gateway = data.gateway
+ local ip_range = data.ip_range
+
+ local aux_address = {}
+ local tmp = data.aux_address or {}
+ for i,v in ipairs(tmp) do
+ _,_,k1,v1 = v:find("(.-)=(.+)")
+ aux_address[k1] = v1
+ end
+
+ local options = {}
+ tmp = data.options or {}
+ for i,v in ipairs(tmp) do
+ _,_,k1,v1 = v:find("(.-)=(.+)")
+ options[k1] = v1
+ end
+
+ local ipv6 = data.ipv6 == 1 and true or false
+
+ local create_body = {
+ Name = name,
+ Driver = driver,
+ EnableIPv6 = ipv6,
+ IPAM = {
+ Driver= "default"
+ },
+ Internal = internal
+ }
+
+ if subnet or gateway or ip_range then
+ create_body["IPAM"]["Config"] = {
+ {
+ Subnet = subnet,
+ Gateway = gateway,
+ IPRange = ip_range,
+ AuxAddress = aux_address,
+ AuxiliaryAddresses = aux_address
+ }
+ }
+ end
+
+ if driver == "macvlan" then
+ create_body["Options"] = {
+ macvlan_mode = data.macvlan_mode,
+ parent = data.parent
+ }
+ elseif driver == "ipvlan" then
+ create_body["Options"] = {
+ ipvlan_mode = data.ipvlan_mode
+ }
+ elseif driver == "overlay" then
+ create_body["Ingress"] = data.ingerss == 1 and true or false
+ end
+
+ if ipv6 and data.subnet6 and data.subnet6 then
+ if type(create_body["IPAM"]["Config"]) ~= "table" then
+ create_body["IPAM"]["Config"] = {}
+ end
+ local index = #create_body["IPAM"]["Config"]
+ create_body["IPAM"]["Config"][index+1] = {
+ Subnet = data.subnet6,
+ Gateway = data.gateway6
+ }
+ end
+
+ if next(options) ~= nil then
+ create_body["Options"] = create_body["Options"] or {}
+ for k, v in pairs(options) do
+ create_body["Options"][k] = v
+ end
+ end
+
+ create_body = docker.clear_empty_tables(create_body)
+ docker:write_status("Network: " .. "create" .. " " .. create_body.Name .. "...")
+
+ local res = dk.networks:create({
+ body = create_body
+ })
+
+ if res and res.code == 201 then
+ docker:write_status("Network: " .. "create macvlan interface...")
+ res = dk.networks:inspect({
+ name = create_body.Name
+ })
+
+ if driver == "macvlan" and
+ data.op_macvlan ~= 0 and
+ res.code == 200 and
+ res.body and
+ res.body.IPAM and
+ res.body.IPAM.Config and
+ res.body.IPAM.Config[1] and
+ res.body.IPAM.Config[1].Gateway and
+ res.body.IPAM.Config[1].Subnet then
+
+ docker.create_macvlan_interface(data.name,
+ data.parent,
+ res.body.IPAM.Config[1].Gateway,
+ res.body.IPAM.Config[1].Subnet)
+ end
+
+ docker:clear_status()
+ luci.http.redirect(luci.dispatcher.build_url("admin/docker/networks"))
+ else
+ docker:append_status("code:" .. res.code.." ".. (res.body.message and res.body.message or res.message).. "\n")
+ luci.http.redirect(luci.dispatcher.build_url("admin/docker/newnetwork"))
+ end
+ end
end
return m
diff --git a/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/overview.lua b/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/overview.lua
index 5515aacc72..dc20125e29 100644
--- a/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/overview.lua
+++ b/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/overview.lua
@@ -3,26 +3,29 @@ LuCI - Lua Configuration Interface
Copyright 2019 lisaac <https://github.com/lisaac/luci-app-dockerman>
]]--
-require "luci.util"
local docker = require "luci.model.docker"
-local uci = require "luci.model.uci"
+
+local m, s, o
function byte_format(byte)
- local suff = {"B", "KB", "MB", "GB", "TB"}
- for i=1, 5 do
- if byte > 1024 and i < 5 then
- byte = byte / 1024
- else
- return string.format("%.2f %s", byte, suff[i])
- end
- end
+ local suff = {"B", "KB", "MB", "GB", "TB"}
+ for i=1, 5 do
+ if byte > 1024 and i < 5 then
+ byte = byte / 1024
+ else
+ return string.format("%.2f %s", byte, suff[i])
+ end
+ end
end
-local map_dockerman = Map("dockerman", translate("Docker"), translate("DockerMan is a Simple Docker manager client for LuCI, If you have any issue please visit:") .. " ".. [[<a href="https://github.com/lisaac/luci-app-dockerman" target="_blank">]] ..translate("Github") .. [[</a>]])
+m = Map("dockerd", translate("Docker"),
+ translate("DockerMan is a Simple Docker manager client for LuCI, If you have any issue please visit:") ..
+ " " ..
+ [[<a href="https://github.com/lisaac/luci-app-dockerman" target="_blank">]] ..
+ translate("Github") ..
+ [[</a>]])
+
local docker_info_table = {}
--- docker_info_table['0OperatingSystem'] = {_key=translate("Operating System"),_value='-'}
--- docker_info_table['1Architecture'] = {_key=translate("Architecture"),_value='-'}
--- docker_info_table['2KernelVersion'] = {_key=translate("Kernel Version"),_value='-'}
docker_info_table['3ServerVersion'] = {_key=translate("Docker Version"),_value='-'}
docker_info_table['4ApiVersion'] = {_key=translate("Api Version"),_value='-'}
docker_info_table['5NCPU'] = {_key=translate("CPUs"),_value='-'}
@@ -31,124 +34,122 @@ docker_info_table['7DockerRootDir'] = {_key=translate("Docker Root Dir"),_value=
docker_info_table['8IndexServerAddress'] = {_key=translate("Index Server Address"),_value='-'}
docker_info_table['9RegistryMirrors'] = {_key=translate("Registry Mirrors"),_value='-'}
-local s = map_dockerman:section(Table, docker_info_table)
+s = m:section(Table, docker_info_table)
s:option(DummyValue, "_key", translate("Info"))
s:option(DummyValue, "_value")
-s = map_dockerman:section(SimpleSection)
+
+s = m:section(SimpleSection)
+s.template = "dockerman/overview"
+
s.containers_running = '-'
s.images_used = '-'
s.containers_total = '-'
s.images_total = '-'
s.networks_total = '-'
s.volumes_total = '-'
-local containers_list
--- local socket = luci.model.uci.cursor():get("dockerman", "local", "socket_path")
-if (require "luci.model.docker").new():_ping().code == 200 then
- local dk = docker.new()
- containers_list = dk.containers:list({query = {all=true}}).body
- local images_list = dk.images:list().body
- local vol = dk.volumes:list()
- local volumes_list = vol and vol.body and vol.body.Volumes or {}
- local networks_list = dk.networks:list().body or {}
- local docker_info = dk:info()
- -- docker_info_table['0OperatingSystem']._value = docker_info.body.OperatingSystem
- -- docker_info_table['1Architecture']._value = docker_info.body.Architecture
- -- docker_info_table['2KernelVersion']._value = docker_info.body.KernelVersion
- docker_info_table['3ServerVersion']._value = docker_info.body.ServerVersion
- docker_info_table['4ApiVersion']._value = docker_info.headers["Api-Version"]
- docker_info_table['5NCPU']._value = tostring(docker_info.body.NCPU)
- docker_info_table['6MemTotal']._value = byte_format(docker_info.body.MemTotal)
- if docker_info.body.DockerRootDir then
- local statvfs = nixio.fs.statvfs(docker_info.body.DockerRootDir)
- local size = statvfs and (statvfs.bavail * statvfs.bsize) or 0
- docker_info_table['7DockerRootDir']._value = docker_info.body.DockerRootDir .. " (" .. tostring(byte_format(size)) .. " " .. translate("Available") .. ")"
- end
- docker_info_table['8IndexServerAddress']._value = docker_info.body.IndexServerAddress
- for i, v in ipairs(docker_info.body.RegistryConfig.Mirrors) do
- docker_info_table['9RegistryMirrors']._value = docker_info_table['9RegistryMirrors']._value == "-" and v or (docker_info_table['9RegistryMirrors']._value .. ", " .. v)
- end
-
- s.images_used = 0
- for i, v in ipairs(images_list) do
- for ci,cv in ipairs(containers_list) do
- if v.Id == cv.ImageID then
- s.images_used = s.images_used + 1
- break
- end
- end
- end
- s.containers_running = tostring(docker_info.body.ContainersRunning)
- s.images_used = tostring(s.images_used)
- s.containers_total = tostring(docker_info.body.Containers)
- s.images_total = tostring(#images_list)
- s.networks_total = tostring(#networks_list)
- s.volumes_total = tostring(#volumes_list)
-end
-s.template = "dockerman/overview"
-local section_dockerman = map_dockerman:section(NamedSection, "local", "section", translate("Setting"))
-section_dockerman:tab("daemon", translate("Docker Daemon"))
-section_dockerman:tab("ac", translate("Access Control"))
-section_dockerman:tab("dockerman", translate("DockerMan"))
+if docker.new():_ping().code == 200 then
+ local dk = docker.new()
+ local containers_list = dk.containers:list({query = {all=true}}).body
+ local images_list = dk.images:list().body
+ local vol = dk.volumes:list()
+ local volumes_list = vol and vol.body and vol.body.Volumes or {}
+ local networks_list = dk.networks:list().body or {}
+ local docker_info = dk:info()
-local socket_path = section_dockerman:taboption("dockerman", Value, "socket_path", translate("Docker Socket Path"))
-socket_path.default = "/var/run/docker.sock"
-socket_path.placeholder = "/var/run/docker.sock"
-socket_path.rmempty = false
+ docker_info_table['3ServerVersion']._value = docker_info.body.ServerVersion
+ docker_info_table['4ApiVersion']._value = docker_info.headers["Api-Version"]
+ docker_info_table['5NCPU']._value = tostring(docker_info.body.NCPU)
+ docker_info_table['6MemTotal']._value = byte_format(docker_info.body.MemTotal)
+ if docker_info.body.DockerRootDir then
+ local statvfs = nixio.fs.statvfs(docker_info.body.DockerRootDir)
+ local size = statvfs and (statvfs.bavail * statvfs.bsize) or 0
+ docker_info_table['7DockerRootDir']._value = docker_info.body.DockerRootDir .. " (" .. tostring(byte_format(size)) .. " " .. translate("Available") .. ")"
+ end
-local remote_endpoint = section_dockerman:taboption("dockerman", Flag, "remote_endpoint", translate("Remote Endpoint"), translate("Dockerman connect to remote endpoint"))
-remote_endpoint.rmempty = false
-remote_endpoint.enabled = "true"
-remote_endpoint.disabled = "false"
+ docker_info_table['8IndexServerAddress']._value = docker_info.body.IndexServerAddress
+ for i, v in ipairs(docker_info.body.RegistryConfig.Mirrors) do
+ docker_info_table['9RegistryMirrors']._value = docker_info_table['9RegistryMirrors']._value == "-" and v or (docker_info_table['9RegistryMirrors']._value .. ", " .. v)
+ end
-local remote_host = section_dockerman:taboption("dockerman", Value, "remote_host", translate("Remote Host"))
-remote_host.placeholder = "10.1.1.2"
--- remote_host:depends("remote_endpoint", "true")
+ s.images_used = 0
+ for i, v in ipairs(images_list) do
+ for ci,cv in ipairs(containers_list) do
+ if v.Id == cv.ImageID then
+ s.images_used = s.images_used + 1
+ break
+ end
+ end
+ end
+
+ s.containers_running = tostring(docker_info.body.ContainersRunning)
+ s.images_used = tostring(s.images_used)
+ s.containers_total = tostring(docker_info.body.Containers)
+ s.images_total = tostring(#images_list)
+ s.networks_total = tostring(#networks_list)
+ s.volumes_total = tostring(#volumes_list)
+end
-local remote_port = section_dockerman:taboption("dockerman", Value, "remote_port", translate("Remote Port"))
-remote_port.placeholder = "2375"
-remote_port.default = "2375"
--- remote_port:depends("remote_endpoint", "true")
+s = m:section(NamedSection, "globals", "section", translate("Setting"))
--- local status_path = section_dockerman:taboption("dockerman", Value, "status_path", translate("Action Status Tempfile Path"), translate("Where you want to save the docker status file"))
--- local debug = section_dockerman:taboption("dockerman", Flag, "debug", translate("Enable Debug"), translate("For debug, It shows all docker API actions of luci-app-dockerman in Debug Tempfile Path"))
--- debug.enabled="true"
--- debug.disabled="false"
--- local debug_path = section_dockerman:taboption("dockerman", Value, "debug_path", translate("Debug Tempfile Path"), translate("Where you want to save the debug tempfile"))
+o = s:option(Flag, "remote_endpoint",
+ translate("Remote Endpoint"),
+ translate("Connect to remote endpoint"))
+o.rmempty = false
+
+o = s:option(Value, "socket_path",
+ translate("Docker Socket Path"))
+o.default = "unix://var/run/docker.sock"
+o.placeholder = "unix://var/run/docker.sock"
+o:depends("remote_endpoint", 1)
+
+o = s:option(Value, "remote_host",
+ translate("Remote Host"))
+o.placeholder = "10.1.1.2"
+o:depends("remote_endpoint", 1)
+
+o = s:option(Value, "remote_port",
+ translate("Remote Port"))
+o.placeholder = "2375"
+o.default = "2375"
+o:depends("remote_endpoint", 1)
if nixio.fs.access("/usr/bin/dockerd") then
- local allowed_interface = section_dockerman:taboption("ac", DynamicList, "ac_allowed_interface", translate("Allowed access interfaces"), translate("Which interface(s) can access containers under the bridge network, fill-in Interface Name"))
- local interfaces = luci.sys and luci.sys.net and luci.sys.net.devices() or {}
- for i, v in ipairs(interfaces) do
- allowed_interface:value(v, v)
- end
- local allowed_container = section_dockerman:taboption("ac", DynamicList, "ac_allowed_container", translate("Containers allowed to be accessed"), translate("Which container(s) under bridge network can be accessed, even from interfaces that are not allowed, fill-in Container Id or Name"))
- -- allowed_container.placeholder = "container name_or_id"
- if containers_list then
- for i, v in ipairs(containers_list) do
- if v.State == "running" and v.NetworkSettings and v.NetworkSettings.Networks and v.NetworkSettings.Networks.bridge and v.NetworkSettings.Networks.bridge.IPAddress then
- allowed_container:value(v.Id:sub(1,12), v.Names[1]:sub(2) .. " | " .. v.NetworkSettings.Networks.bridge.IPAddress)
- end
- end
- end
-
- local dockerd_enable = section_dockerman:taboption("daemon", Flag, "daemon_ea", translate("Enable"))
- dockerd_enable.enabled = "true"
- dockerd_enable.rmempty = true
- local data_root = section_dockerman:taboption("daemon", Value, "daemon_data_root", translate("Docker Root Dir"))
- data_root.placeholder = "/opt/docker/"
- local registry_mirrors = section_dockerman:taboption("daemon", DynamicList, "daemon_registry_mirrors", translate("Registry Mirrors"))
- registry_mirrors:value("https://hub-mirror.c.163.com", "https://hub-mirror.c.163.com")
-
- local log_level = section_dockerman:taboption("daemon", ListValue, "daemon_log_level", translate("Log Level"), translate('Set the logging level'))
- log_level:value("debug", "debug")
- log_level:value("info", "info")
- log_level:value("warn", "warn")
- log_level:value("error", "error")
- log_level:value("fatal", "fatal")
- local hosts = section_dockerman:taboption("daemon", DynamicList, "daemon_hosts", translate("Server Host"), translate('Daemon unix socket (unix:///var/run/docker.sock) or TCP Remote Hosts (tcp://0.0.0.0:2375), default: unix:///var/run/docker.sock'))
- hosts:value("unix:///var/run/docker.sock", "unix:///var/run/docker.sock")
- hosts:value("tcp://0.0.0.0:2375", "tcp://0.0.0.0:2375")
- hosts.rmempty = true
+ o = s:option(Value, "data_root",
+ translate("Docker Root Dir"))
+ o.placeholder = "/opt/docker/"
+ o:depends("remote_endpoint", 0)
+
+ o = s:option(Value, "bip",
+ translate("Default bridge"),
+ translate("Configure the default bridge network"))
+ o.placeholder = "172.17.0.1/16"
+ o.default = "172.17.0.1/16"
+ o.datatype = "ipaddr"
+ o:depends("remote_endpoint", 0)
+
+ o = s:option(DynamicList, "registry_mirrors",
+ translate("Registry Mirrors"))
+ o:value("https://hub-mirror.c.163.com", "https://hub-mirror.c.163.com")
+ o:depends("remote_endpoint", 0)
+
+ o = s:option(ListValue, "log_level",
+ translate("Log Level"),
+ translate('Set the logging level'))
+ o:value("debug", "debug")
+ o:value("info", "info")
+ o:value("warn", "warn")
+ o:value("error", "error")
+ o:value("fatal", "fatal")
+ o:depends("remote_endpoint", 0)
+
+ o = s:option(DynamicList, "hosts",
+ translate("Client connection"),
+ translate('Specifies where the Docker daemon will listen for client connections'))
+ o:value("unix://var/run/docker.sock", "unix://var/run/docker.sock")
+ o:value("tcp://0.0.0.0:2375", "tcp://0.0.0.0:2375")
+ o.rmempty = true
+ o:depends("remote_endpoint", 0)
end
-return map_dockerman
+
+return m
diff --git a/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/volumes.lua b/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/volumes.lua
index 1685027203..8dae4a020d 100644
--- a/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/volumes.lua
+++ b/applications/luci-app-dockerman/luasrc/model/cbi/dockerman/volumes.lua
@@ -3,114 +3,140 @@ LuCI - Lua Configuration Interface
Copyright 2019 lisaac <https://github.com/lisaac/luci-app-dockerman>
]]--
-require "luci.util"
-local uci = luci.model.uci.cursor()
local docker = require "luci.model.docker"
local dk = docker.new()
-local containers, volumes
-local res = dk.volumes:list()
-if res.code <300 then volumes = res.body.Volumes else return end
-res = dk.containers:list({query = {all=true}})
-if res.code <300 then containers = res.body else return end
+local m, s, o
+
+local res, containers, volumes
function get_volumes()
- local data = {}
- for i, v in ipairs(volumes) do
- -- local index = v.CreatedAt .. v.Name
- local index = v.Name
- data[index]={}
- data[index]["_selected"] = 0
- data[index]["_nameraw"] = v.Name
- data[index]["_name"] = v.Name:sub(1,12)
- for ci,cv in ipairs(containers) do
- if cv.Mounts and type(cv.Mounts) ~= "table" then break end
- for vi, vv in ipairs(cv.Mounts) do
- if v.Name == vv.Name then
- data[index]["_containers"] = (data[index]["_containers"] and (data[index]["_containers"] .. " | ") or "")..
- '<a href='..luci.dispatcher.build_url("admin/docker/container/"..cv.Id)..' class="dockerman_link" title="'..translate("Container detail")..'">'.. cv.Names[1]:sub(2)..'</a>'
- end
- end
- end
- data[index]["_driver"] = v.Driver
- data[index]["_mountpoint"] = nil
- for v1 in v.Mountpoint:gmatch('[^/]+') do
- if v1 == index then
- data[index]["_mountpoint"] = data[index]["_mountpoint"] .."/" .. v1:sub(1,12) .. "..."
- else
- data[index]["_mountpoint"] = (data[index]["_mountpoint"] and data[index]["_mountpoint"] or "").."/".. v1
- end
- end
- data[index]["_created"] = v.CreatedAt
- end
- return data
+ local data = {}
+ for i, v in ipairs(volumes) do
+ local index = v.Name
+ data[index]={}
+ data[index]["_selected"] = 0
+ data[index]["_nameraw"] = v.Name
+ data[index]["_name"] = v.Name:sub(1,12)
+
+ for ci,cv in ipairs(containers) do
+ if cv.Mounts and type(cv.Mounts) ~= "table" then
+ break
+ end
+ for vi, vv in ipairs(cv.Mounts) do
+ if v.Name == vv.Name then
+ data[index]["_containers"] = (data[index]["_containers"] and (data[index]["_containers"] .. " | ") or "")..
+ '<a href='..luci.dispatcher.build_url("admin/docker/container/"..cv.Id)..' class="dockerman_link" title="'..translate("Container detail")..'">'.. cv.Names[1]:sub(2)..'</a>'
+ end
+ end
+ end
+ data[index]["_driver"] = v.Driver
+ data[index]["_mountpoint"] = nil
+
+ for v1 in v.Mountpoint:gmatch('[^/]+') do
+ if v1 == index then
+ data[index]["_mountpoint"] = data[index]["_mountpoint"] .."/" .. v1:sub(1,12) .. "..."
+ else
+ data[index]["_mountpoint"] = (data[index]["_mountpoint"] and data[index]["_mountpoint"] or "").."/".. v1
+ end
+ end
+ data[index]["_created"] = v.CreatedAt
+ end
+
+ return data
+end
+
+res = dk.volumes:list()
+if res.code <300 then
+ volumes = res.body.Volumes
+else
+ return
+end
+
+res = dk.containers:list({
+ query = {
+ all=true
+ }
+})
+if res.code <300 then
+ containers = res.body
+else
+ return
end
local volume_list = get_volumes()
--- m = Map("docker", translate("Docker"))
m = SimpleForm("docker", translate("Docker"))
m.submit=false
m.reset=false
+s = m:section(Table, volume_list, translate("Volumes"))
+
+o = s:option(Flag, "_selected","")
+o.disabled = 0
+o.enabled = 1
+o.default = 0
+o.write = function(self, section, value)
+ volume_list[section]._selected = value
+end
+
+o = s:option(DummyValue, "_name", translate("Name"))
+
+o = s:option(DummyValue, "_driver", translate("Driver"))
-volume_table = m:section(Table, volume_list, translate("Volumes"))
+o = s:option(DummyValue, "_containers", translate("Containers"))
+o.rawhtml = true
-volume_selecter = volume_table:option(Flag, "_selected","")
-volume_selecter.disabled = 0
-volume_selecter.enabled = 1
-volume_selecter.default = 0
+o = s:option(DummyValue, "_mountpoint", translate("Mount Point"))
-volume_id = volume_table:option(DummyValue, "_name", translate("Name"))
-volume_table:option(DummyValue, "_driver", translate("Driver"))
-volume_table:option(DummyValue, "_containers", translate("Containers")).rawhtml = true
-volume_table:option(DummyValue, "_mountpoint", translate("Mount Point"))
-volume_table:option(DummyValue, "_created", translate("Created"))
-volume_selecter.write = function(self, section, value)
- volume_list[section]._selected = value
+o = s:option(DummyValue, "_created", translate("Created"))
+
+s = m:section(SimpleSection)
+s.template = "dockerman/apply_widget"
+s.err=docker:read_status()
+s.err=s.err and s.err:gsub("\n","<br>"):gsub(" ","&nbsp;")
+if s.err then
+ docker:clear_status()
end
-docker_status = m:section(SimpleSection)
-docker_status.template = "dockerman/apply_widget"
-docker_status.err=docker:read_status()
-docker_status.err=docker_status.err and docker_status.err:gsub("\n","<br>"):gsub(" ","&nbsp;")
-if docker_status.err then docker:clear_status() end
-
-action = m:section(Table,{{}})
-action.notitle=true
-action.rowcolors=false
-action.template="cbi/nullsection"
-btnremove = action:option(Button, "remove")
-btnremove.inputtitle= translate("Remove")
-btnremove.template = "dockerman/cbi/inlinebutton"
-btnremove.inputstyle = "remove"
-btnremove.forcewrite = true
-btnremove.write = function(self, section)
- local volume_selected = {}
- -- 遍历table中sectionid
- local volume_table_sids = volume_table:cfgsections()
- for _, volume_table_sid in ipairs(volume_table_sids) do
- -- 得到选中项的名字
- if volume_list[volume_table_sid]._selected == 1 then
- -- volume_selected[#volume_selected+1] = volume_id:cfgvalue(volume_table_sid)
- volume_selected[#volume_selected+1] = volume_table_sid
- end
- end
- if next(volume_selected) ~= nil then
- local success = true
- docker:clear_status()
- for _,vol in ipairs(volume_selected) do
- docker:append_status("Volumes: " .. "remove" .. " " .. vol .. "...")
- local msg = dk.volumes["remove"](dk, {id = vol})
- if msg.code ~= 204 then
- docker:append_status("code:" .. msg.code.." ".. (msg.body.message and msg.body.message or msg.message).. "\n")
- success = false
- else
- docker:append_status("done\n")
- end
- end
- if success then docker:clear_status() end
- luci.http.redirect(luci.dispatcher.build_url("admin/docker/volumes"))
- end
+s = m:section(Table,{{}})
+s.notitle=true
+s.rowcolors=false
+s.template="cbi/nullsection"
+
+o = s:option(Button, "remove")
+o.inputtitle= translate("Remove")
+o.template = "dockerman/cbi/inlinebutton"
+o.inputstyle = "remove"
+o.forcewrite = true
+o.write = function(self, section)
+ local volume_selected = {}
+
+ for k in pairs(volume_list) do
+ if volume_list[k]._selected == 1 then
+ volume_selected[#volume_selected+1] = k
+ end
+ end
+
+ if next(volume_selected) ~= nil then
+ local success = true
+ docker:clear_status()
+ for _,vol in ipairs(volume_selected) do
+ docker:append_status("Volumes: " .. "remove" .. " " .. vol .. "...")
+ local msg = dk.volumes["remove"](dk, {id = vol})
+ if msg.code ~= 204 then
+ docker:append_status("code:" .. msg.code.." ".. (msg.body.message and msg.body.message or msg.message).. "\n")
+ success = false
+ else
+ docker:append_status("done\n")
+ end
+ end
+
+ if success then
+ docker:clear_status()
+ end
+ luci.http.redirect(luci.dispatcher.build_url("admin/docker/volumes"))
+ end
end
+
return m
diff --git a/applications/luci-app-dockerman/luasrc/model/docker.lua b/applications/luci-app-dockerman/luasrc/model/docker.lua
index e62454a8fc..a0c74c0e41 100644
--- a/applications/luci-app-dockerman/luasrc/model/docker.lua
+++ b/applications/luci-app-dockerman/luasrc/model/docker.lua
@@ -3,276 +3,347 @@ LuCI - Lua Configuration Interface
Copyright 2019 lisaac <https://github.com/lisaac/luci-app-dockerman>
]]--
-require "luci.util"
local docker = require "luci.docker"
+local fs = require "nixio.fs"
local uci = (require "luci.model.uci").cursor()
local _docker = {}
+_docker.options = {}
--pull image and return iamge id
local update_image = function(self, image_name)
- local json_stringify = luci.jsonc and luci.jsonc.stringify
- _docker:append_status("Images: " .. "pulling" .. " " .. image_name .. "...\n")
- local res = self.images:create({query = {fromImage=image_name}}, _docker.pull_image_show_status_cb)
- if res and res.code == 200 and (#res.body > 0 and not res.body[#res.body].error and res.body[#res.body].status and (res.body[#res.body].status == "Status: Downloaded newer image for ".. image_name)) then
- _docker:append_status("done\n")
- else
- res.body.message = res.body[#res.body] and res.body[#res.body].error or (res.body.message or res.message)
- end
- new_image_id = self.images:inspect({name = image_name}).body.Id
- return new_image_id, res
+ local json_stringify = luci.jsonc and luci.jsonc.stringify
+ _docker:append_status("Images: " .. "pulling" .. " " .. image_name .. "...\n")
+ local res = self.images:create({query = {fromImage=image_name}}, _docker.pull_image_show_status_cb)
+
+ if res and res.code == 200 and (#res.body > 0 and not res.body[#res.body].error and res.body[#res.body].status and (res.body[#res.body].status == "Status: Downloaded newer image for ".. image_name)) then
+ _docker:append_status("done\n")
+ else
+ res.body.message = res.body[#res.body] and res.body[#res.body].error or (res.body.message or res.message)
+ end
+
+ new_image_id = self.images:inspect({name = image_name}).body.Id
+ return new_image_id, res
end
local table_equal = function(t1, t2)
- if not t1 then return true end
- if not t2 then return false end
- if #t1 ~= #t2 then return false end
- for i, v in ipairs(t1) do
- if t1[i] ~= t2[i] then return false end
- end
- return true
+ if not t1 then
+ return true
+ end
+
+ if not t2 then
+ return false
+ end
+
+ if #t1 ~= #t2 then
+ return false
+ end
+
+ for i, v in ipairs(t1) do
+ if t1[i] ~= t2[i] then
+ return false
+ end
+ end
+
+ return true
end
local table_subtract = function(t1, t2)
- if not t1 or next(t1) == nil then return nil end
- if not t2 or next(t2) == nil then return t1 end
- local res = {}
- for _, v1 in ipairs(t1) do
- local found = false
- for _, v2 in ipairs(t2) do
- if v1 == v2 then
- found= true
- break
- end
- end
- if not found then
- table.insert(res, v1)
- end
- end
- return next(res) == nil and nil or res
+ if not t1 or next(t1) == nil then
+ return nil
+ end
+
+ if not t2 or next(t2) == nil then
+ return t1
+ end
+
+ local res = {}
+ for _, v1 in ipairs(t1) do
+ local found = false
+ for _, v2 in ipairs(t2) do
+ if v1 == v2 then
+ found= true
+ break
+ end
+ end
+ if not found then
+ table.insert(res, v1)
+ end
+ end
+
+ return next(res) == nil and nil or res
end
local map_subtract = function(t1, t2)
- if not t1 or next(t1) == nil then return nil end
- if not t2 or next(t2) == nil then return t1 end
- local res = {}
- for k1, v1 in pairs(t1) do
- local found = false
- for k2, v2 in ipairs(t2) do
- if k1 == k2 and luci.util.serialize_data(v1) == luci.util.serialize_data(v2) then
- found= true
- break
- end
- end
- if not found then
- res[k1] = v1
- -- if v1 and type(v1) == "table" then
- -- if next(v1) == nil then
- -- res[k1] = { k = 'v' }
- -- else
- -- res[k1] = v1
- -- end
- -- end
- end
- end
-
- return next(res) ~= nil and res or nil
+ if not t1 or next(t1) == nil then
+ return nil
+ end
+
+ if not t2 or next(t2) == nil then
+ return t1
+ end
+
+ local res = {}
+ for k1, v1 in pairs(t1) do
+ local found = false
+ for k2, v2 in ipairs(t2) do
+ if k1 == k2 and luci.util.serialize_data(v1) == luci.util.serialize_data(v2) then
+ found= true
+ break
+ end
+ end
+
+ if not found then
+ res[k1] = v1
+ end
+ end
+
+ return next(res) ~= nil and res or nil
end
_docker.clear_empty_tables = function ( t )
- local k, v
- if next(t) == nil then
- t = nil
- else
- for k, v in pairs(t) do
- if type(v) == 'table' then
- t[k] = _docker.clear_empty_tables(v)
- end
- end
- end
- return t
+ local k, v
+
+ if next(t) == nil then
+ t = nil
+ else
+ for k, v in pairs(t) do
+ if type(v) == 'table' then
+ t[k] = _docker.clear_empty_tables(v)
+ end
+ end
+ end
+
+ return t
end
--- return create_body, extra_network
local get_config = function(container_config, image_config)
- local config = container_config.Config
- local old_host_config = container_config.HostConfig
- local old_network_setting = container_config.NetworkSettings.Networks or {}
- if config.WorkingDir == image_config.WorkingDir then config.WorkingDir = "" end
- if config.User == image_config.User then config.User = "" end
- if table_equal(config.Cmd, image_config.Cmd) then config.Cmd = nil end
- if table_equal(config.Entrypoint, image_config.Entrypoint) then config.Entrypoint = nil end
- if table_equal(config.ExposedPorts, image_config.ExposedPorts) then config.ExposedPorts = nil end
- config.Env = table_subtract(config.Env, image_config.Env)
- config.Labels = table_subtract(config.Labels, image_config.Labels)
- config.Volumes = map_subtract(config.Volumes, image_config.Volumes)
- -- subtract ports exposed in image from container
- if old_host_config.PortBindings and next(old_host_config.PortBindings) ~= nil then
- config.ExposedPorts = {}
- for p, v in pairs(old_host_config.PortBindings) do
- config.ExposedPorts[p] = { HostPort=v[1] and v[1].HostPort }
- end
- end
-
- -- handle network config, we need only one network, extras need to network connect action
- local network_setting = {}
- local multi_network = false
- local extra_network = {}
- for k, v in pairs(old_network_setting) do
- if multi_network then
- extra_network[k] = v
- else
- network_setting[k] = v
- end
- multi_network = true
- end
-
- -- handle hostconfig
- local host_config = old_host_config
- -- if host_config.PortBindings and next(host_config.PortBindings) == nil then host_config.PortBindings = nil end
- -- host_config.LogConfig = nil
- host_config.Mounts = {}
- -- for volumes
- for i, v in ipairs(container_config.Mounts) do
- if v.Type == "volume" then
- table.insert(host_config.Mounts, {
- Type = v.Type,
- Target = v.Destination,
- Source = v.Source:match("([^/]+)\/_data"),
- BindOptions = (v.Type == "bind") and {Propagation = v.Propagation} or nil,
- ReadOnly = not v.RW
- })
- end
- end
-
-
- -- merge configs
- local create_body = config
- create_body["HostConfig"] = host_config
- create_body["NetworkingConfig"] = {EndpointsConfig = network_setting}
- create_body = _docker.clear_empty_tables(create_body) or {}
- extra_network = _docker.clear_empty_tables(extra_network) or {}
- return create_body, extra_network
+ local config = container_config.Config
+ local old_host_config = container_config.HostConfig
+ local old_network_setting = container_config.NetworkSettings.Networks or {}
+
+ if config.WorkingDir == image_config.WorkingDir then
+ config.WorkingDir = ""
+ end
+
+ if config.User == image_config.User then
+ config.User = ""
+ end
+
+ if table_equal(config.Cmd, image_config.Cmd) then
+ config.Cmd = nil
+ end
+
+ if table_equal(config.Entrypoint, image_config.Entrypoint) then
+ config.Entrypoint = nil
+ end
+
+ if table_equal(config.ExposedPorts, image_config.ExposedPorts) then
+ config.ExposedPorts = nil
+ end
+
+ config.Env = table_subtract(config.Env, image_config.Env)
+ config.Labels = table_subtract(config.Labels, image_config.Labels)
+ config.Volumes = map_subtract(config.Volumes, image_config.Volumes)
+
+ if old_host_config.PortBindings and next(old_host_config.PortBindings) ~= nil then
+ config.ExposedPorts = {}
+ for p, v in pairs(old_host_config.PortBindings) do
+ config.ExposedPorts[p] = { HostPort=v[1] and v[1].HostPort }
+ end
+ end
+
+ local network_setting = {}
+ local multi_network = false
+ local extra_network = {}
+
+ for k, v in pairs(old_network_setting) do
+ if multi_network then
+ extra_network[k] = v
+ else
+ network_setting[k] = v
+ end
+ multi_network = true
+ end
+
+ local host_config = old_host_config
+ host_config.Mounts = {}
+ for i, v in ipairs(container_config.Mounts) do
+ if v.Type == "volume" then
+ table.insert(host_config.Mounts, {
+ Type = v.Type,
+ Target = v.Destination,
+ Source = v.Source:match("([^/]+)\/_data"),
+ BindOptions = (v.Type == "bind") and {Propagation = v.Propagation} or nil,
+ ReadOnly = not v.RW
+ })
+ end
+ end
+
+ local create_body = config
+ create_body["HostConfig"] = host_config
+ create_body["NetworkingConfig"] = {EndpointsConfig = network_setting}
+ create_body = _docker.clear_empty_tables(create_body) or {}
+ extra_network = _docker.clear_empty_tables(extra_network) or {}
+
+ return create_body, extra_network
end
local upgrade = function(self, request)
- _docker:clear_status()
- -- get image name, image id, container name, configuration information
- local container_info = self.containers:inspect({id = request.id})
- if container_info.code > 300 and type(container_info.body) == "table" then
- return container_info
- end
- local image_name = container_info.body.Config.Image
- if not image_name:match(".-:.+") then image_name = image_name .. ":latest" end
- local old_image_id = container_info.body.Image
- local container_name = container_info.body.Name:sub(2)
-
- local image_id, res = update_image(self, image_name)
- if res and res.code ~= 200 then return res end
- if image_id == old_image_id then
- return {code = 305, body = {message = "Already up to date"}}
- end
-
- _docker:append_status("Container: " .. "Stop" .. " " .. container_name .. "...")
- res = self.containers:stop({name = container_name})
- if res and res.code < 305 then
- _docker:append_status("done\n")
- else
- return res
- end
-
- _docker:append_status("Container: rename" .. " " .. container_name .. " to ".. container_name .. "_old ...")
- res = self.containers:rename({name = container_name, query = { name = container_name .. "_old" }})
- if res and res.code < 300 then
- _docker:append_status("done\n")
- else
- return res
- end
-
- -- handle config
- local image_config = self.images:inspect({id = old_image_id}).body.Config
- local create_body, extra_network = get_config(container_info.body, image_config)
-
- -- create new container
- _docker:append_status("Container: Create" .. " " .. container_name .. "...")
- create_body = _docker.clear_empty_tables(create_body)
- res = self.containers:create({name = container_name, body = create_body})
- if res and res.code > 300 then return res end
- _docker:append_status("done\n")
-
- -- extra networks need to network connect action
- for k, v in pairs(extra_network) do
- _docker:append_status("Networks: Connect" .. " " .. container_name .. "...")
- res = self.networks:connect({id = k, body = {Container = container_name, EndpointConfig = v}})
- if res.code > 300 then return res end
-
- _docker:append_status("done\n")
- end
- _docker:clear_status()
- return res
+ _docker:clear_status()
+
+ local container_info = self.containers:inspect({id = request.id})
+
+ if container_info.code > 300 and type(container_info.body) == "table" then
+ return container_info
+ end
+
+ local image_name = container_info.body.Config.Image
+ if not image_name:match(".-:.+") then
+ image_name = image_name .. ":latest"
+ end
+
+ local old_image_id = container_info.body.Image
+ local container_name = container_info.body.Name:sub(2)
+
+ local image_id, res = update_image(self, image_name)
+ if res and res.code ~= 200 then
+ return res
+ end
+
+ if image_id == old_image_id then
+ return {code = 305, body = {message = "Already up to date"}}
+ end
+
+ _docker:append_status("Container: " .. "Stop" .. " " .. container_name .. "...")
+ res = self.containers:stop({name = container_name})
+ if res and res.code < 305 then
+ _docker:append_status("done\n")
+ else
+ return res
+ end
+
+ _docker:append_status("Container: rename" .. " " .. container_name .. " to ".. container_name .. "_old ...")
+ res = self.containers:rename({name = container_name, query = { name = container_name .. "_old" }})
+ if res and res.code < 300 then
+ _docker:append_status("done\n")
+ else
+ return res
+ end
+
+ local image_config = self.images:inspect({id = old_image_id}).body.Config
+ local create_body, extra_network = get_config(container_info.body, image_config)
+
+ -- create new container
+ _docker:append_status("Container: Create" .. " " .. container_name .. "...")
+ create_body = _docker.clear_empty_tables(create_body)
+ res = self.containers:create({name = container_name, body = create_body})
+ if res and res.code > 300 then
+ return res
+ end
+ _docker:append_status("done\n")
+
+ -- extra networks need to network connect action
+ for k, v in pairs(extra_network) do
+ _docker:append_status("Networks: Connect" .. " " .. container_name .. "...")
+ res = self.networks:connect({id = k, body = {Container = container_name, EndpointConfig = v}})
+ if res.code > 300 then
+ return res
+ end
+ _docker:append_status("done\n")
+ end
+
+ _docker:clear_status()
+ return res
end
local duplicate_config = function (self, request)
- local container_info = self.containers:inspect({id = request.id})
- if container_info.code > 300 and type(container_info.body) == "table" then return nil end
- local old_image_id = container_info.body.Image
- local image_config = self.images:inspect({id = old_image_id}).body.Config
- return get_config(container_info.body, image_config)
+ local container_info = self.containers:inspect({id = request.id})
+ if container_info.code > 300 and type(container_info.body) == "table" then
+ return nil
+ end
+
+ local old_image_id = container_info.body.Image
+ local image_config = self.images:inspect({id = old_image_id}).body.Config
+
+ return get_config(container_info.body, image_config)
end
-_docker.new = function(option)
- local option = option or {}
- local remote = uci:get("dockerman", "local", "remote_endpoint")
- options = {
- host = (remote == "true") and (option.host or uci:get("dockerman", "local", "remote_host")) or nil,
- port = (remote == "true") and (option.port or uci:get("dockerman", "local", "remote_port")) or nil,
- debug = option.debug or uci:get("dockerman", "local", "debug") == 'true' and true or false,
- debug_path = option.debug_path or uci:get("dockerman", "local", "debug_path")
- }
- options.socket_path = (remote ~= "true" or not options.host or not options.port) and (option.socket_path or uci:get("dockerman", "local", "socket_path") or "/var/run/docker.sock") or nil
- local _new = docker.new(options)
- _new.options.status_path = uci:get("dockerman", "local", "status_path")
- _new.containers_upgrade = upgrade
- _new.containers_duplicate_config = duplicate_config
- return _new
+_docker.new = function()
+ local host = nil
+ local port = nil
+ local socket_path = nil
+ local debug_path = nil
+
+ local remote = uci:get_bool("dockerd", "globals", "remote_endpoint")
+ if remote then
+ host = uci:get("dockerd", "globals", "remote_host") or nil
+ port = uci:get("dockerd", "globals", "remote_port") or nil
+ else
+ socket_path = uci:get("dockerd", "globals", "socket_path") or "/var/run/docker.sock"
+ end
+
+ local debug = uci:get_bool("dockerd", "globals", "debug")
+ if debug then
+ debug_path = uci:get("dockerd", "globals", "debug_path") or "/tmp/.docker_debug"
+ end
+
+ local status_path = uci:get("dockerd", "globals", "status_path") or "/tmp/.docker_status"
+
+ _docker.options = {
+ host = host,
+ port = port,
+ socket_path = socket_path,
+ debug = debug,
+ debug_path = debug_path,
+ status_path = status_path
+ }
+
+ local _new = docker.new(_docker.options)
+ _new.containers_upgrade = upgrade
+ _new.containers_duplicate_config = duplicate_config
+
+ return _new
end
-_docker.options={}
-_docker.options.status_path = uci:get("dockerman", "local", "status_path")
_docker.append_status=function(self,val)
- if not val then return end
- local file_docker_action_status=io.open(self.options.status_path, "a+")
- file_docker_action_status:write(val)
- file_docker_action_status:close()
+ if not val then
+ return
+ end
+ local file_docker_action_status=io.open(self.options.status_path, "a+")
+ file_docker_action_status:write(val)
+ file_docker_action_status:close()
end
_docker.write_status=function(self,val)
- if not val then return end
- local file_docker_action_status=io.open(self.options.status_path, "w+")
- file_docker_action_status:write(val)
- file_docker_action_status:close()
+ if not val then
+ return
+ end
+ local file_docker_action_status=io.open(self.options.status_path, "w+")
+ file_docker_action_status:write(val)
+ file_docker_action_status:close()
end
_docker.read_status=function(self)
- return nixio.fs.readfile(self.options.status_path)
+ return fs.readfile(self.options.status_path)
end
_docker.clear_status=function(self)
- nixio.fs.remove(self.options.status_path)
+ fs.remove(self.options.status_path)
end
local status_cb = function(res, source, handler)
- res.body = res.body or {}
- while true do
- local chunk = source()
- if chunk then
- --standard output to res.body
- table.insert(res.body, chunk)
- handler(chunk)
- else
- return
- end
- end
+ res.body = res.body or {}
+ while true do
+ local chunk = source()
+ if chunk then
+ --standard output to res.body
+ table.insert(res.body, chunk)
+ handler(chunk)
+ else
+ return
+ end
+ end
end
--{"status":"Pulling from library\/debian","id":"latest"}
@@ -284,114 +355,128 @@ end
--{"status":"Digest: sha256:a63d0b2ecbd723da612abf0a8bdb594ee78f18f691d7dc652ac305a490c9b71a"}
--{"status":"Status: Downloaded newer image for debian:latest"}
_docker.pull_image_show_status_cb = function(res, source)
- return status_cb(res, source, function(chunk)
- local json_parse = luci.jsonc.parse
- local step = json_parse(chunk)
- if type(step) == "table" then
- local buf = _docker:read_status()
- local num = 0
- local str = '\t' .. (step.id and (step.id .. ": ") or "") .. (step.status and step.status or "") .. (step.progress and (" " .. step.progress) or "").."\n"
- if step.id then buf, num = buf:gsub("\t"..step.id .. ": .-\n", str) end
- if num == 0 then
- buf = buf .. str
- end
- _docker:write_status(buf)
- end
- end)
+ return status_cb(res, source, function(chunk)
+ local json_parse = luci.jsonc.parse
+ local step = json_parse(chunk)
+ if type(step) == "table" then
+ local buf = _docker:read_status()
+ local num = 0
+ local str = '\t' .. (step.id and (step.id .. ": ") or "") .. (step.status and step.status or "") .. (step.progress and (" " .. step.progress) or "").."\n"
+ if step.id then
+ buf, num = buf:gsub("\t"..step.id .. ": .-\n", str)
+ end
+ if num == 0 then
+ buf = buf .. str
+ end
+ _docker:write_status(buf)
+ end
+ end)
end
--{"status":"Downloading from https://downloads.openwrt.org/releases/19.07.0/targets/x86/64/openwrt-19.07.0-x86-64-generic-rootfs.tar.gz"}
--{"status":"Importing","progressDetail":{"current":1572391,"total":3821714},"progress":"[====================\u003e ] 1.572MB/3.822MB"}
--{"status":"sha256:d5304b58e2d8cc0a2fd640c05cec1bd4d1229a604ac0dd2909f13b2b47a29285"}
_docker.import_image_show_status_cb = function(res, source)
- return status_cb(res, source, function(chunk)
- local json_parse = luci.jsonc.parse
- local step = json_parse(chunk)
- if type(step) == "table" then
- local buf = _docker:read_status()
- local num = 0
- local str = '\t' .. (step.status and step.status or "") .. (step.progress and (" " .. step.progress) or "").."\n"
- if step.status then buf, num = buf:gsub("\t"..step.status .. " .-\n", str) end
- if num == 0 then
- buf = buf .. str
- end
- _docker:write_status(buf)
- end
- end
- )
+ return status_cb(res, source, function(chunk)
+ local json_parse = luci.jsonc.parse
+ local step = json_parse(chunk)
+ if type(step) == "table" then
+ local buf = _docker:read_status()
+ local num = 0
+ local str = '\t' .. (step.status and step.status or "") .. (step.progress and (" " .. step.progress) or "").."\n"
+ if step.status then
+ buf, num = buf:gsub("\t"..step.status .. " .-\n", str)
+ end
+ if num == 0 then
+ buf = buf .. str
+ end
+ _docker:write_status(buf)
+ end
+ end)
end
--- _docker.print_status_cb = function(res, source)
--- return status_cb(res, source, function(step)
--- luci.util.perror(step)
--- end
--- )
--- end
-
_docker.create_macvlan_interface = function(name, device, gateway, subnet)
- if not nixio.fs.access("/etc/config/network") or not nixio.fs.access("/etc/config/firewall") then return end
- if uci:get("dockerman", "local", "remote_endpoint") == "true" then return end
- local ip = require "luci.ip"
- local if_name = "docker_"..name
- local dev_name = "macvlan_"..name
- local net_mask = tostring(ip.new(subnet):mask())
- local lan_interfaces
- -- add macvlan device
- uci:delete("network", dev_name)
- uci:set("network", dev_name, "device")
- uci:set("network", dev_name, "name", dev_name)
- uci:set("network", dev_name, "ifname", device)
- uci:set("network", dev_name, "type", "macvlan")
- uci:set("network", dev_name, "mode", "bridge")
- -- add macvlan interface
- uci:delete("network", if_name)
- uci:set("network", if_name, "interface")
- uci:set("network", if_name, "proto", "static")
- uci:set("network", if_name, "ifname", dev_name)
- uci:set("network", if_name, "ipaddr", gateway)
- uci:set("network", if_name, "netmask", net_mask)
- uci:foreach("firewall", "zone", function(s)
- if s.name == "lan" then
- local interfaces
- if type(s.network) == "table" then
- interfaces = table.concat(s.network, " ")
- uci:delete("firewall", s[".name"], "network")
- else
- interfaces = s.network and s.network or ""
- end
- interfaces = interfaces .. " " .. if_name
- interfaces = interfaces:gsub("%s+", " ")
- uci:set("firewall", s[".name"], "network", interfaces)
- end
- end)
- uci:commit("firewall")
- uci:commit("network")
- os.execute("ifup " .. if_name)
+ if not fs.access("/etc/config/network") or not fs.access("/etc/config/firewall") then
+ return
+ end
+
+ if uci:get("dockerd", "globals", "remote_endpoint") == "true" then
+ return
+ end
+
+ local ip = require "luci.ip"
+ local if_name = "docker_"..name
+ local dev_name = "macvlan_"..name
+ local net_mask = tostring(ip.new(subnet):mask())
+ local lan_interfaces
+
+ -- add macvlan device
+ uci:delete("network", dev_name)
+ uci:set("network", dev_name, "device")
+ uci:set("network", dev_name, "name", dev_name)
+ uci:set("network", dev_name, "ifname", device)
+ uci:set("network", dev_name, "type", "macvlan")
+ uci:set("network", dev_name, "mode", "bridge")
+
+ -- add macvlan interface
+ uci:delete("network", if_name)
+ uci:set("network", if_name, "interface")
+ uci:set("network", if_name, "proto", "static")
+ uci:set("network", if_name, "ifname", dev_name)
+ uci:set("network", if_name, "ipaddr", gateway)
+ uci:set("network", if_name, "netmask", net_mask)
+ uci:foreach("firewall", "zone", function(s)
+ if s.name == "lan" then
+ local interfaces
+ if type(s.network) == "table" then
+ interfaces = table.concat(s.network, " ")
+ uci:delete("firewall", s[".name"], "network")
+ else
+ interfaces = s.network and s.network or ""
+ end
+ interfaces = interfaces .. " " .. if_name
+ interfaces = interfaces:gsub("%s+", " ")
+ uci:set("firewall", s[".name"], "network", interfaces)
+ end
+ end)
+
+ uci:commit("firewall")
+ uci:commit("network")
+
+ os.execute("ifup " .. if_name)
end
_docker.remove_macvlan_interface = function(name)
- if not nixio.fs.access("/etc/config/network") or not nixio.fs.access("/etc/config/firewall") then return end
- if uci:get("dockerman", "local", "remote_endpoint") == "true" then return end
- local if_name = "docker_"..name
- local dev_name = "macvlan_"..name
- uci:foreach("firewall", "zone", function(s)
- if s.name == "lan" then
- local interfaces
- if type(s.network) == "table" then
- interfaces = table.concat(s.network, " ")
- else
- interfaces = s.network and s.network or ""
- end
- interfaces = interfaces and interfaces:gsub(if_name, "")
- interfaces = interfaces and interfaces:gsub("%s+", " ")
- uci:set("firewall", s[".name"], "network", interfaces)
- end
- end)
- uci:commit("firewall")
- uci:delete("network", dev_name)
- uci:delete("network", if_name)
- uci:commit("network")
- os.execute("ip link del " .. if_name)
+ if not fs.access("/etc/config/network") or not fs.access("/etc/config/firewall") then
+ return
+ end
+
+ if uci:get("dockerd", "globals", "remote_endpoint") == "true" then
+ return
+ end
+
+ local if_name = "docker_"..name
+ local dev_name = "macvlan_"..name
+ uci:foreach("firewall", "zone", function(s)
+ if s.name == "lan" then
+ local interfaces
+ if type(s.network) == "table" then
+ interfaces = table.concat(s.network, " ")
+ else
+ interfaces = s.network and s.network or ""
+ end
+ interfaces = interfaces and interfaces:gsub(if_name, "")
+ interfaces = interfaces and interfaces:gsub("%s+", " ")
+ uci:set("firewall", s[".name"], "network", interfaces)
+ end
+ end)
+
+ uci:delete("network", dev_name)
+ uci:delete("network", if_name)
+ uci:commit("network")
+ uci:commit("firewall")
+
+ os.execute("ip link del " .. if_name)
end
return _docker