luci-app-dockerman: cbi/container update coding style
authorFlorian Eckert <fe@dev.tdt.de>
Thu, 23 Jul 2020 06:52:03 +0000 (08:52 +0200)
committerFlorian Eckert <fe@dev.tdt.de>
Tue, 28 Jul 2020 12:16:14 +0000 (14:16 +0200)
Signed-off-by: Florian Eckert <fe@dev.tdt.de>
applications/luci-app-dockerman/luasrc/model/cbi/dockerman/container.lua

index 1c39ebf4f0fa655347758cd4c02459f1827edc77..0a8cbdcd195ef91afafb2e3267e16162d578b8f5 100644 (file)
@@ -4,585 +4,747 @@ 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 "-"
+               },
+       }
+
+       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