]]--
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(" "," ")
-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(" "," ")
+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 "-"
+ },
+ }
+
+ 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
+
+ _, _, 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
+ local 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
+ local filesection= m:section(SimpleSection)
+ m.submit = false
+ m.reset = false
+ filesection.template = "dockerman/container_file"
+ filesection.container = container_id
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
+ 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
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 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
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("dockerd", "globals", "remote_endpoint")
- local socket_path = (remote == "false" or not remote) and uci:get("dockerd", "globals", "socket_path") or nil
- local host = (remote == "true") and uci:get("dockerd", "globals", "remote_host") or nil
- local port = (remote == "true") and uci:get("dockerd", "globals", "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 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("dockerd", "globals", "remote_endpoint")
+ local socket_path = (remote == "false" or not remote) and uci:get("dockerd", "globals", "socket_path") or nil
+ local host = (remote == "true") and uci:get("dockerd", "globals", "remote_host") or nil
+ local port = (remote == "true") and uci:get("dockerd", "globals", "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
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
+ 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
end
return m