mirror of
https://github.com/Ysurac/openmptcprouter-feeds.git
synced 2025-03-09 15:40:03 +00:00
fix
This commit is contained in:
parent
6b7f0b4dba
commit
fd8b7384d5
104 changed files with 13356 additions and 31 deletions
210
luci-app-diskman/luasrc/model/cbi/diskman/btrfs.lua
Normal file
210
luci-app-diskman/luasrc/model/cbi/diskman/btrfs.lua
Normal file
|
@ -0,0 +1,210 @@
|
|||
--[[
|
||||
LuCI - Lua Configuration Interface
|
||||
Copyright 2019 lisaac <https://github.com/lisaac/luci-app-diskman>
|
||||
]]--
|
||||
|
||||
require "luci.util"
|
||||
require("luci.tools.webadmin")
|
||||
local dm = require "luci.model.diskman"
|
||||
local uuid = arg[1]
|
||||
|
||||
if not uuid then luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman")) end
|
||||
|
||||
-- mount subv=/ to tempfs
|
||||
mount_point = "/tmp/.btrfs_tmp"
|
||||
nixio.fs.mkdirr(mount_point)
|
||||
luci.util.exec(dm.command.umount .. " "..mount_point .. " >/dev/null 2>&1")
|
||||
luci.util.exec(dm.command.mount .. " -t btrfs -o subvol=/ UUID="..uuid.." "..mount_point)
|
||||
|
||||
m = SimpleForm("btrfs", translate("Btrfs"), translate("Manage Btrfs"))
|
||||
m.template = "diskman/cbi/xsimpleform"
|
||||
m.redirect = luci.dispatcher.build_url("admin/system/diskman")
|
||||
m.submit = false
|
||||
m.reset = false
|
||||
|
||||
-- info
|
||||
local btrfs_info = dm.get_btrfs_info(mount_point)
|
||||
local table_btrfs_info = m:section(Table, {btrfs_info}, translate("Btrfs Info"))
|
||||
table_btrfs_info:option(DummyValue, "uuid", translate("UUID"))
|
||||
table_btrfs_info:option(DummyValue, "members", translate("Members"))
|
||||
table_btrfs_info:option(DummyValue, "data_raid_level", translate("Data"))
|
||||
table_btrfs_info:option(DummyValue, "metadata_raid_lavel", translate("Metadata"))
|
||||
table_btrfs_info:option(DummyValue, "size_formated", translate("Size"))
|
||||
table_btrfs_info:option(DummyValue, "used_formated", translate("Used"))
|
||||
table_btrfs_info:option(DummyValue, "free_formated", translate("Free Space"))
|
||||
table_btrfs_info:option(DummyValue, "usage", translate("Usage"))
|
||||
local v_btrfs_label = table_btrfs_info:option(Value, "label", translate("Label"))
|
||||
local value_btrfs_label = ""
|
||||
v_btrfs_label.write = function(self, section, value)
|
||||
value_btrfs_label = value or ""
|
||||
end
|
||||
local btn_update_label = table_btrfs_info:option(Button, "_update_label")
|
||||
btn_update_label.inputtitle = translate("Update")
|
||||
btn_update_label.inputstyle = "edit"
|
||||
btn_update_label.write = function(self, section, value)
|
||||
local cmd = dm.command.btrfs .. " filesystem label " .. mount_point .. " " .. value_btrfs_label
|
||||
local res = luci.util.exec(cmd)
|
||||
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman/btrfs/" .. uuid))
|
||||
end
|
||||
-- subvolume
|
||||
local subvolume_list = dm.get_btrfs_subv(mount_point)
|
||||
subvolume_list["_"] = { ID = 0 }
|
||||
table_subvolume = m:section(Table, subvolume_list, translate("SubVolumes"))
|
||||
table_subvolume:option(DummyValue, "id", translate("ID"))
|
||||
table_subvolume:option(DummyValue, "top_level", translate("Top Level"))
|
||||
table_subvolume:option(DummyValue, "uuid", translate("UUID"))
|
||||
table_subvolume:option(DummyValue, "otime", translate("Otime"))
|
||||
table_subvolume:option(DummyValue, "snapshots", translate("Snapshots"))
|
||||
local v_path = table_subvolume:option(Value, "path", translate("Path"))
|
||||
v_path.forcewrite = true
|
||||
v_path.render = function(self, section, scope)
|
||||
if subvolume_list[section].ID == 0 then
|
||||
self.template = "cbi/value"
|
||||
self.placeholder = "/my_subvolume"
|
||||
self.forcewrite = true
|
||||
Value.render(self, section, scope)
|
||||
else
|
||||
self.template = "cbi/dvalue"
|
||||
DummyValue.render(self, section, scope)
|
||||
end
|
||||
end
|
||||
local value_path
|
||||
v_path.write = function(self, section, value)
|
||||
value_path = value
|
||||
end
|
||||
local btn_set_default = table_subvolume:option(Button, "_subv_set_default", translate("Set Default"))
|
||||
btn_set_default.forcewrite = true
|
||||
btn_set_default.inputstyle = "edit"
|
||||
btn_set_default.template = "diskman/cbi/disabled_button"
|
||||
btn_set_default.render = function(self, section, scope)
|
||||
if subvolume_list[section].default_subvolume then
|
||||
self.view_disabled = true
|
||||
self.inputtitle = translate("Set Default")
|
||||
elseif subvolume_list[section].ID == 0 then
|
||||
self.template = "cbi/dvalue"
|
||||
else
|
||||
self.inputtitle = translate("Set Default")
|
||||
self.view_disabled = false
|
||||
end
|
||||
Button.render(self, section, scope)
|
||||
end
|
||||
btn_set_default.write = function(self, section, value)
|
||||
local cmd
|
||||
if value == translate("Set Default") then
|
||||
cmd = dm.command.btrfs .. " subvolume set-default " .. mount_point..subvolume_list[section].path
|
||||
else
|
||||
cmd = dm.command.btrfs .. " subvolume set-default " .. mount_point.."/"
|
||||
end
|
||||
local res = luci.util.exec(cmd.. " 2>&1")
|
||||
if res and (res:match("ERR") or res:match("not enough arguments")) then
|
||||
m.errmessage = res
|
||||
else
|
||||
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman/btrfs/" .. uuid))
|
||||
end
|
||||
end
|
||||
local btn_remove = table_subvolume:option(Button, "_subv_remove")
|
||||
btn_remove.template = "diskman/cbi/disabled_button"
|
||||
btn_remove.forcewrite = true
|
||||
btn_remove.render = function(self, section, scope)
|
||||
if subvolume_list[section].ID == 0 then
|
||||
btn_remove.inputtitle = translate("Create")
|
||||
btn_remove.inputstyle = "add"
|
||||
self.view_disabled = false
|
||||
elseif subvolume_list[section].path == "/" or subvolume_list[section].default_subvolume then
|
||||
btn_remove.inputtitle = translate("Delete")
|
||||
btn_remove.inputstyle = "remove"
|
||||
self.view_disabled = true
|
||||
else
|
||||
btn_remove.inputtitle = translate("Delete")
|
||||
btn_remove.inputstyle = "remove"
|
||||
self.view_disabled = false
|
||||
end
|
||||
Button.render(self, section, scope)
|
||||
end
|
||||
|
||||
btn_remove.write = function(self, section, value)
|
||||
local cmd
|
||||
if value == translate("Delete") then
|
||||
cmd = dm.command.btrfs .. " subvolume delete " .. mount_point .. subvolume_list[section].path
|
||||
elseif value == translate("Create") then
|
||||
if value_path and value_path:match("^/") then
|
||||
cmd = dm.command.btrfs .. " subvolume create " .. mount_point .. value_path
|
||||
else
|
||||
m.errmessage = translate("Please input Subvolume Path, Subvolume must start with '/'")
|
||||
return
|
||||
end
|
||||
end
|
||||
local res = luci.util.exec(cmd.. " 2>&1")
|
||||
if res and (res:match("ERR") or res:match("not enough arguments")) then
|
||||
m.errmessage = luci.util.pcdata(res)
|
||||
else
|
||||
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman/btrfs/" .. uuid))
|
||||
end
|
||||
end
|
||||
-- snapshot
|
||||
-- local snapshot_list = dm.get_btrfs_subv(mount_point, 1)
|
||||
-- table_snapshot = m:section(Table, snapshot_list, translate("Snapshots"))
|
||||
-- table_snapshot:option(DummyValue, "id", translate("ID"))
|
||||
-- table_snapshot:option(DummyValue, "top_level", translate("Top Level"))
|
||||
-- table_snapshot:option(DummyValue, "uuid", translate("UUID"))
|
||||
-- table_snapshot:option(DummyValue, "otime", translate("Otime"))
|
||||
-- table_snapshot:option(DummyValue, "path", translate("Path"))
|
||||
-- local snp_remove = table_snapshot:option(Button, "_snp_remove")
|
||||
-- snp_remove.inputtitle = translate("Delete")
|
||||
-- snp_remove.inputstyle = "remove"
|
||||
-- snp_remove.write = function(self, section, value)
|
||||
-- local cmd = dm.command.btrfs .. " subvolume delete " .. mount_point .. snapshot_list[section].path
|
||||
-- local res = luci.util.exec(cmd.. " 2>&1")
|
||||
-- if res and (res:match("ERR") or res:match("not enough arguments")) then
|
||||
-- m.errmessage = luci.util.pcdata(res)
|
||||
-- else
|
||||
-- luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman/btrfs/" .. uuid))
|
||||
-- end
|
||||
-- end
|
||||
|
||||
-- new snapshots
|
||||
local s_snapshot = m:section(SimpleSection, translate("New Snapshot"))
|
||||
local value_sorce, value_dest, value_readonly
|
||||
local v_sorce = s_snapshot:option(Value, "_source", translate("Source Path"), translate("The source path for create the snapshot"))
|
||||
v_sorce.placeholder = "/data"
|
||||
v_sorce.forcewrite = true
|
||||
v_sorce.write = function(self, section, value)
|
||||
value_sorce = value
|
||||
end
|
||||
|
||||
local v_readonly = s_snapshot:option(Flag, "_readonly", translate("Readonly"), translate("The path where you want to store the snapshot"))
|
||||
v_readonly.forcewrite = true
|
||||
v_readonly.rmempty = false
|
||||
v_readonly.disabled = 0
|
||||
v_readonly.enabled = 1
|
||||
v_readonly.default = 1
|
||||
v_readonly.write = function(self, section, value)
|
||||
value_readonly = value
|
||||
end
|
||||
local v_dest = s_snapshot:option(Value, "_dest", translate("Destination Path (optional)"))
|
||||
v_dest.forcewrite = true
|
||||
v_dest.placeholder = "/.snapshot/202002051538"
|
||||
v_dest.write = function(self, section, value)
|
||||
value_dest = value
|
||||
end
|
||||
local btn_snp_create = s_snapshot:option(Button, "_snp_create")
|
||||
btn_snp_create.title = " "
|
||||
btn_snp_create.inputtitle = translate("New Snapshot")
|
||||
btn_snp_create.inputstyle = "add"
|
||||
btn_snp_create.write = function(self, section, value)
|
||||
if value_sorce and value_sorce:match("^/") then
|
||||
if not value_dest then value_dest = "/.snapshot"..value_sorce.."/"..os.date("%Y%m%d%H%M%S") end
|
||||
nixio.fs.mkdirr(mount_point..value_dest:match("(.-)[^/]+$"))
|
||||
local cmd = dm.command.btrfs .. " subvolume snapshot" .. (value_readonly == 1 and " -r " or " ") .. mount_point..value_sorce .. " " .. mount_point..value_dest
|
||||
local res = luci.util.exec(cmd .. " 2>&1")
|
||||
if res and (res:match("ERR") or res:match("not enough arguments")) then
|
||||
m.errmessage = luci.util.pcdata(res)
|
||||
else
|
||||
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman/btrfs/" .. uuid))
|
||||
end
|
||||
else
|
||||
m.errmessage = translate("Please input Source Path of snapshot, Source Path must start with '/'")
|
||||
end
|
||||
end
|
||||
|
||||
return m
|
327
luci-app-diskman/luasrc/model/cbi/diskman/disks.lua
Normal file
327
luci-app-diskman/luasrc/model/cbi/diskman/disks.lua
Normal file
|
@ -0,0 +1,327 @@
|
|||
--[[
|
||||
LuCI - Lua Configuration Interface
|
||||
Copyright 2019 lisaac <https://github.com/lisaac/luci-app-diskman>
|
||||
]]--
|
||||
|
||||
require "luci.util"
|
||||
require("luci.tools.webadmin")
|
||||
local dm = require "luci.model.diskman"
|
||||
|
||||
-- Use (non-UCI) SimpleForm since we have no related config file
|
||||
m = SimpleForm("diskman", translate("DiskMan"), translate("Manage Disks over LuCI."))
|
||||
m.template = "diskman/cbi/xsimpleform"
|
||||
m:append(Template("diskman/disk_info"))
|
||||
-- disable submit and reset button
|
||||
m.submit = false
|
||||
m.reset = false
|
||||
-- rescan disks
|
||||
rescan = m:section(SimpleSection)
|
||||
rescan_button = rescan:option(Button, "_rescan")
|
||||
rescan_button.inputtitle= translate("Rescan Disks")
|
||||
rescan_button.template = "diskman/cbi/inlinebutton"
|
||||
rescan_button.inputstyle = "add"
|
||||
rescan_button.forcewrite = true
|
||||
rescan_button.write = function(self, section, value)
|
||||
luci.util.exec("echo '- - -' | tee /sys/class/scsi_host/host*/scan > /dev/null")
|
||||
if dm.command.mdadm then
|
||||
luci.util.exec(dm.command.mdadm .. " --assemble --scan")
|
||||
end
|
||||
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman"))
|
||||
end
|
||||
|
||||
-- disks
|
||||
local disks = dm.list_devices()
|
||||
d = m:section(Table, disks, translate("Disks"))
|
||||
d.config = "disk"
|
||||
-- option(type, id(key of table), text)
|
||||
d:option(DummyValue, "path", translate("Path"))
|
||||
d:option(DummyValue, "model", translate("Model"))
|
||||
d:option(DummyValue, "sn", translate("Serial Number"))
|
||||
d:option(DummyValue, "size_formated", translate("Size"))
|
||||
d:option(DummyValue, "temp", translate("Temp"))
|
||||
-- d:option(DummyValue, "sec_size", translate("Sector Size "))
|
||||
d:option(DummyValue, "p_table", translate("Partition Table"))
|
||||
d:option(DummyValue, "sata_ver", translate("SATA Version"))
|
||||
-- d:option(DummyValue, "rota_rate", translate("Rotation Rate"))
|
||||
d:option(DummyValue, "health", translate("Health"))
|
||||
d:option(DummyValue, "status", translate("Status"))
|
||||
|
||||
d.extedit = luci.dispatcher.build_url("admin/system/diskman/partition/%s")
|
||||
|
||||
-- raid devices
|
||||
if dm.command.mdadm then
|
||||
local raid_devices = dm.list_raid_devices()
|
||||
-- raid_devices = diskmanager.getRAIDdevices()
|
||||
if next(raid_devices) ~= nil then
|
||||
local r = m:section(Table, raid_devices, translate("RAID Devices"))
|
||||
r.config = "_raid"
|
||||
r:option(DummyValue, "path", translate("Path"))
|
||||
r:option(DummyValue, "level", translate("RAID mode"))
|
||||
r:option(DummyValue, "size_formated", translate("Size"))
|
||||
r:option(DummyValue, "p_table", translate("Partition Table"))
|
||||
r:option(DummyValue, "status", translate("Status"))
|
||||
r:option(DummyValue, "members_str", translate("Members"))
|
||||
r:option(DummyValue, "active", translate("Active"))
|
||||
r.extedit = luci.dispatcher.build_url("admin/system/diskman/partition/%s")
|
||||
end
|
||||
end
|
||||
|
||||
-- btrfs devices
|
||||
if dm.command.btrfs then
|
||||
btrfs_devices = dm.list_btrfs_devices()
|
||||
if next(btrfs_devices) ~= nil then
|
||||
local table_btrfs = m:section(Table, btrfs_devices, translate("Btrfs"))
|
||||
table_btrfs:option(DummyValue, "uuid", translate("UUID"))
|
||||
table_btrfs:option(DummyValue, "label", translate("Label"))
|
||||
table_btrfs:option(DummyValue, "members", translate("Members"))
|
||||
-- sieze is error, since there is RAID
|
||||
-- table_btrfs:option(DummyValue, "size_formated", translate("Size"))
|
||||
table_btrfs:option(DummyValue, "used_formated", translate("Usage"))
|
||||
table_btrfs.extedit = luci.dispatcher.build_url("admin/system/diskman/btrfs/%s")
|
||||
end
|
||||
end
|
||||
|
||||
-- mount point
|
||||
local mount_point = dm.get_mount_points()
|
||||
local _mount_point = {}
|
||||
table.insert( mount_point, { device = 0 } )
|
||||
local table_mp = m:section(Table, mount_point, translate("Mount Point"))
|
||||
local v_device = table_mp:option(Value, "device", translate("Device"))
|
||||
v_device.render = function(self, section, scope)
|
||||
if mount_point[section].device == 0 then
|
||||
self.template = "cbi/value"
|
||||
self.forcewrite = true
|
||||
for dev, info in pairs(disks) do
|
||||
for i, v in ipairs(info.partitions) do
|
||||
self:value("/dev/".. v.name, "/dev/".. v.name .. " ".. v.size_formated)
|
||||
end
|
||||
end
|
||||
Value.render(self, section, scope)
|
||||
else
|
||||
self.template = "cbi/dvalue"
|
||||
DummyValue.render(self, section, scope)
|
||||
end
|
||||
end
|
||||
v_device.write = function(self, section, value)
|
||||
_mount_point.device = value and value:gsub("%s+", "") or ""
|
||||
end
|
||||
local v_fs = table_mp:option(Value, "fs", translate("File System"))
|
||||
v_fs.render = function(self, section, scope)
|
||||
if mount_point[section].device == 0 then
|
||||
self.template = "cbi/value"
|
||||
self:value("auto", "auto")
|
||||
self.default = "auto"
|
||||
self.forcewrite = true
|
||||
Value.render(self, section, scope)
|
||||
else
|
||||
self.template = "cbi/dvalue"
|
||||
DummyValue.render(self, section, scope)
|
||||
end
|
||||
end
|
||||
v_fs.write = function(self, section, value)
|
||||
_mount_point.fs = value and value:gsub("%s+", "") or ""
|
||||
end
|
||||
local v_mount_option = table_mp:option(Value, "mount_options", translate("Mount Options"))
|
||||
v_mount_option.render = function(self, section, scope)
|
||||
if mount_point[section].device == 0 then
|
||||
self.template = "cbi/value"
|
||||
self.placeholder = "rw,noauto"
|
||||
self.forcewrite = true
|
||||
Value.render(self, section, scope)
|
||||
else
|
||||
self.template = "cbi/dvalue"
|
||||
local mp = mount_point[section].mount_options
|
||||
mount_point[section].mount_options = nil
|
||||
local length = 0
|
||||
for k in mp:gmatch("([^,]+)") do
|
||||
mount_point[section].mount_options = mount_point[section].mount_options and (mount_point[section].mount_options .. ",") or ""
|
||||
if length > 20 then
|
||||
mount_point[section].mount_options = mount_point[section].mount_options.. " <br>"
|
||||
length = 0
|
||||
end
|
||||
mount_point[section].mount_options = mount_point[section].mount_options .. k
|
||||
length = length + #k
|
||||
end
|
||||
self.rawhtml = true
|
||||
-- mount_point[section].mount_options = #mount_point[section].mount_options > 50 and mount_point[section].mount_options:sub(1,50) .. "..." or mount_point[section].mount_options
|
||||
DummyValue.render(self, section, scope)
|
||||
end
|
||||
end
|
||||
v_mount_option.write = function(self, section, value)
|
||||
_mount_point.mount_options = value and value:gsub("%s+", "") or ""
|
||||
end
|
||||
local v_mount_point = table_mp:option(Value, "mount_point", translate("Mount Point"))
|
||||
v_mount_point.render = function(self, section, scope)
|
||||
if mount_point[section].device == 0 then
|
||||
self.template = "cbi/value"
|
||||
self.placeholder = "/media/diskX"
|
||||
self.forcewrite = true
|
||||
Value.render(self, section, scope)
|
||||
else
|
||||
self.template = "cbi/dvalue"
|
||||
local new_mp = ""
|
||||
local v_mp_d
|
||||
for v_mp_d in self["section"]["data"][section]["mount_point"]:gmatch('[^/]+') do
|
||||
if #v_mp_d > 12 then
|
||||
new_mp = new_mp .. "/" .. v_mp_d:sub(1,7) .. ".." .. v_mp_d:sub(-4)
|
||||
else
|
||||
new_mp = new_mp .."/".. v_mp_d
|
||||
end
|
||||
end
|
||||
self["section"]["data"][section]["mount_point"] = '<span title="'..self["section"]["data"][section]["mount_point"] .. '" >'..new_mp..'</span>'
|
||||
self.rawhtml = true
|
||||
DummyValue.render(self, section, scope)
|
||||
end
|
||||
end
|
||||
v_mount_point.write = function(self, section, value)
|
||||
_mount_point.mount_point = value
|
||||
end
|
||||
local btn_umount = table_mp:option(Button, "_mount", translate("Mount"))
|
||||
btn_umount.forcewrite = true
|
||||
btn_umount.render = function(self, section, scope)
|
||||
if mount_point[section].device == 0 then
|
||||
self.inputtitle = translate("Mount")
|
||||
btn_umount.inputstyle = "add"
|
||||
else
|
||||
self.inputtitle = translate("Umount")
|
||||
btn_umount.inputstyle = "remove"
|
||||
end
|
||||
Button.render(self, section, scope)
|
||||
end
|
||||
btn_umount.write = function(self, section, value)
|
||||
local res
|
||||
if value == translate("Mount") then
|
||||
if not _mount_point.mount_point or not _mount_point.device then return end
|
||||
luci.util.exec("mkdir -p ".. _mount_point.mount_point)
|
||||
res = luci.util.exec(dm.command.mount .. " ".. _mount_point.device .. (_mount_point.fs and (" -t ".. _mount_point.fs )or "") .. (_mount_point.mount_options and (" -o " .. _mount_point.mount_options.. " ") or " ").._mount_point.mount_point .. " 2>&1")
|
||||
elseif value == translate("Umount") then
|
||||
res = luci.util.exec(dm.command.umount .. " "..mount_point[section].mount_point .. " 2>&1")
|
||||
end
|
||||
if res:match("^mount:") or res:match("^umount:") then
|
||||
m.errmessage = luci.util.pcdata(res)
|
||||
else
|
||||
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman"))
|
||||
end
|
||||
end
|
||||
|
||||
if dm.command.mdadm or dm.command.btrfs then
|
||||
local creation_section = m:section(TypedSection, "_creation")
|
||||
creation_section.cfgsections=function()
|
||||
return {translate("Creation")}
|
||||
end
|
||||
creation_section:tab("raid", translate("RAID"), translate("RAID Creation"))
|
||||
creation_section:tab("btrfs", translate("Btrfs"), translate("Multiple Devices Btrfs Creation"))
|
||||
|
||||
-- raid functions
|
||||
if dm.command.mdadm then
|
||||
|
||||
local rname, rmembers, rlevel
|
||||
local r_name = creation_section:taboption("raid", Value, "_rname", translate("Raid Name"))
|
||||
r_name.placeholder = "/dev/md0"
|
||||
r_name.write = function(self, section, value)
|
||||
rname = value
|
||||
end
|
||||
local r_level = creation_section:taboption("raid", ListValue, "_rlevel", translate("Raid Level"))
|
||||
local valid_raid = luci.util.exec("lsmod | grep md_mod")
|
||||
if valid_raid:match("linear") then
|
||||
r_level:value("linear", "Linear")
|
||||
end
|
||||
if valid_raid:match("raid456") then
|
||||
r_level:value("5", "Raid 5")
|
||||
r_level:value("6", "Raid 6")
|
||||
end
|
||||
if valid_raid:match("raid1") then
|
||||
r_level:value("1", "Raid 1")
|
||||
end
|
||||
if valid_raid:match("raid0") then
|
||||
r_level:value("0", "Raid 0")
|
||||
end
|
||||
if valid_raid:match("raid10") then
|
||||
r_level:value("10", "Raid 10")
|
||||
end
|
||||
r_level.write = function(self, section, value)
|
||||
rlevel = value
|
||||
end
|
||||
local r_member = creation_section:taboption("raid", DynamicList, "_rmember", translate("Raid Member"))
|
||||
for dev, info in pairs(disks) do
|
||||
if not info.inuse and #info.partitions == 0 then
|
||||
r_member:value(info.path, info.path.. " ".. info.size_formated)
|
||||
end
|
||||
for i, v in ipairs(info.partitions) do
|
||||
if not v.inuse then
|
||||
r_member:value("/dev/".. v.name, "/dev/".. v.name .. " ".. v.size_formated)
|
||||
end
|
||||
end
|
||||
end
|
||||
r_member.write = function(self, section, value)
|
||||
rmembers = value
|
||||
end
|
||||
local r_create = creation_section:taboption("raid", Button, "_rcreate")
|
||||
r_create.render = function(self, section, scope)
|
||||
self.title = " "
|
||||
self.inputtitle = translate("Create Raid")
|
||||
self.inputstyle = "add"
|
||||
Button.render(self, section, scope)
|
||||
end
|
||||
r_create.write = function(self, section, value)
|
||||
-- mdadm --create --verbose /dev/md0 --level=stripe --raid-devices=2 /dev/sdb6 /dev/sdc5
|
||||
local res = dm.create_raid(rname, rlevel, rmembers)
|
||||
if res and res:match("^ERR") then
|
||||
m.errmessage = luci.util.pcdata(res)
|
||||
return
|
||||
end
|
||||
dm.gen_mdadm_config()
|
||||
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman"))
|
||||
end
|
||||
end
|
||||
|
||||
-- btrfs
|
||||
if dm.command.btrfs then
|
||||
local blabel, bmembers, blevel
|
||||
local btrfs_label = creation_section:taboption("btrfs", Value, "_blabel", translate("Btrfs Label"))
|
||||
btrfs_label.write = function(self, section, value)
|
||||
blabel = value
|
||||
end
|
||||
local btrfs_level = creation_section:taboption("btrfs", ListValue, "_blevel", translate("Btrfs Raid Level"))
|
||||
btrfs_level:value("single", "Single")
|
||||
btrfs_level:value("raid0", "Raid 0")
|
||||
btrfs_level:value("raid1", "Raid 1")
|
||||
btrfs_level:value("raid10", "Raid 10")
|
||||
btrfs_level.write = function(self, section, value)
|
||||
blevel = value
|
||||
end
|
||||
|
||||
local btrfs_member = creation_section:taboption("btrfs", DynamicList, "_bmember", translate("Btrfs Member"))
|
||||
for dev, info in pairs(disks) do
|
||||
if not info.inuse and #info.partitions == 0 then
|
||||
btrfs_member:value(info.path, info.path.. " ".. info.size_formated)
|
||||
end
|
||||
for i, v in ipairs(info.partitions) do
|
||||
if not v.inuse then
|
||||
btrfs_member:value("/dev/".. v.name, "/dev/".. v.name .. " ".. v.size_formated)
|
||||
end
|
||||
end
|
||||
end
|
||||
btrfs_member.write = function(self, section, value)
|
||||
bmembers = value
|
||||
end
|
||||
local btrfs_create = creation_section:taboption("btrfs", Button, "_bcreate")
|
||||
btrfs_create.render = function(self, section, scope)
|
||||
self.title = " "
|
||||
self.inputtitle = translate("Create Btrfs")
|
||||
self.inputstyle = "add"
|
||||
Button.render(self, section, scope)
|
||||
end
|
||||
btrfs_create.write = function(self, section, value)
|
||||
-- mkfs.btrfs -L label -d blevel /dev/sda /dev/sdb
|
||||
local res = dm.create_btrfs(blabel, blevel, bmembers)
|
||||
if res and res:match("^ERR") then
|
||||
m.errmessage = luci.util.pcdata(res)
|
||||
return
|
||||
end
|
||||
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return m
|
366
luci-app-diskman/luasrc/model/cbi/diskman/partition.lua
Normal file
366
luci-app-diskman/luasrc/model/cbi/diskman/partition.lua
Normal file
|
@ -0,0 +1,366 @@
|
|||
--[[
|
||||
LuCI - Lua Configuration Interface
|
||||
Copyright 2019 lisaac <https://github.com/lisaac/luci-app-diskman>
|
||||
]]--
|
||||
|
||||
require "luci.util"
|
||||
require("luci.tools.webadmin")
|
||||
local dm = require "luci.model.diskman"
|
||||
local dev = arg[1]
|
||||
|
||||
if not dev then
|
||||
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman"))
|
||||
elseif not nixio.fs.access("/dev/"..dev) then
|
||||
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman"))
|
||||
end
|
||||
|
||||
m = SimpleForm("partition", translate("Partition Management"), translate("Partition Disk over LuCI."))
|
||||
m.template = "diskman/cbi/xsimpleform"
|
||||
m.redirect = luci.dispatcher.build_url("admin/system/diskman")
|
||||
m:append(Template("diskman/partition_info"))
|
||||
-- disable submit and reset button
|
||||
m.submit = false
|
||||
m.reset = false
|
||||
|
||||
local disk_info = dm.get_disk_info(dev, true)
|
||||
local format_cmd = dm.get_format_cmd()
|
||||
|
||||
s = m:section(Table, {disk_info}, translate("Device Info"))
|
||||
-- s:option(DummyValue, "key")
|
||||
-- s:option(DummyValue, "value")
|
||||
s:option(DummyValue, "path", translate("Path"))
|
||||
s:option(DummyValue, "model", translate("Model"))
|
||||
s:option(DummyValue, "sn", translate("Serial Number"))
|
||||
s:option(DummyValue, "size_formated", translate("Size"))
|
||||
s:option(DummyValue, "sec_size", translate("Sector Size"))
|
||||
local dv_p_table = s:option(ListValue, "p_table", translate("Partition Table"))
|
||||
dv_p_table.render = function(self, section, scope)
|
||||
-- create table only if not used by raid and no partitions on disk
|
||||
if not disk_info.p_table:match("Raid") and (#disk_info.partitions == 0 or (#disk_info.partitions == 1 and disk_info.partitions[1].number == -1) or (disk_info.p_table:match("LOOP") and not disk_info.partitions[1].inuse)) then
|
||||
self:value(disk_info.p_table, disk_info.p_table)
|
||||
self:value("GPT", "GPT")
|
||||
self:value("MBR", "MBR")
|
||||
self.default = disk_info.p_table
|
||||
ListValue.render(self, section, scope)
|
||||
else
|
||||
self.template = "cbi/dvalue"
|
||||
DummyValue.render(self, section, scope)
|
||||
end
|
||||
end
|
||||
if disk_info.type:match("md") then
|
||||
s:option(DummyValue, "level", translate("Level"))
|
||||
s:option(DummyValue, "members_str", translate("Members"))
|
||||
else
|
||||
s:option(DummyValue, "temp", translate("Temp"))
|
||||
s:option(DummyValue, "sata_ver", translate("SATA Version"))
|
||||
s:option(DummyValue, "rota_rate", translate("Rotation Rate"))
|
||||
end
|
||||
s:option(DummyValue, "status", translate("Status"))
|
||||
local btn_health = s:option(Button, "health", translate("Health"))
|
||||
btn_health.render = function(self, section, scope)
|
||||
if disk_info.health then
|
||||
self.inputtitle = disk_info.health
|
||||
if disk_info.health == "PASSED" then
|
||||
self.inputstyle = "add"
|
||||
else
|
||||
self.inputstyle = "remove"
|
||||
end
|
||||
Button.render(self, section, scope)
|
||||
else
|
||||
self.template = "cbi/dvalue"
|
||||
DummyValue.render(self, section, scope)
|
||||
end
|
||||
end
|
||||
|
||||
local btn_eject = s:option(Button, "_eject")
|
||||
btn_eject.template = "diskman/cbi/disabled_button"
|
||||
btn_eject.inputstyle = "remove"
|
||||
btn_eject.render = function(self, section, scope)
|
||||
for i, p in ipairs(disk_info.partitions) do
|
||||
if p.mount_point ~= "-" then
|
||||
self.view_disabled = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if disk_info.p_table:match("Raid") then
|
||||
self.view_disabled = true
|
||||
end
|
||||
if disk_info.type:match("md") then
|
||||
btn_eject.inputtitle = translate("Remove")
|
||||
else
|
||||
btn_eject.inputtitle = translate("Eject")
|
||||
end
|
||||
Button.render(self, section, scope)
|
||||
end
|
||||
btn_eject.forcewrite = true
|
||||
btn_eject.write = function(self, section, value)
|
||||
for i, p in ipairs(disk_info.partitions) do
|
||||
if p.mount_point ~= "-" then
|
||||
m.errmessage = p.name .. translate("is in use! please unmount it first!")
|
||||
return
|
||||
end
|
||||
end
|
||||
if disk_info.type:match("md") then
|
||||
luci.util.exec(dm.command.mdadm .. " --stop /dev/" .. dev)
|
||||
luci.util.exec(dm.command.mdadm .. " --remove /dev/" .. dev)
|
||||
for _, disk in ipairs(disk_info.members) do
|
||||
luci.util.exec(dm.command.mdadm .. " --zero-superblock " .. disk)
|
||||
end
|
||||
dm.gen_mdadm_config()
|
||||
else
|
||||
luci.util.exec("echo 1 > /sys/block/" .. dev .. "/device/delete")
|
||||
end
|
||||
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman"))
|
||||
end
|
||||
-- eject: echo 1 > /sys/block/(device)/device/delete
|
||||
-- rescan: echo '- - -' | tee /sys/class/scsi_host/host*/scan > /dev/null
|
||||
|
||||
|
||||
-- partitions info
|
||||
if not disk_info.p_table:match("Raid") then
|
||||
s_partition_table = m:section(Table, disk_info.partitions, translate("Partitions Info"), translate("Default 2048 sector alignment, support +size{b,k,m,g,t} in End Sector"))
|
||||
|
||||
-- s_partition_table:option(DummyValue, "number", translate("Number"))
|
||||
s_partition_table:option(DummyValue, "name", translate("Name"))
|
||||
local val_sec_start = s_partition_table:option(Value, "sec_start", translate("Start Sector"))
|
||||
val_sec_start.render = function(self, section, scope)
|
||||
-- could create new partition
|
||||
if disk_info.partitions[section].number == -1 and disk_info.partitions[section].size > 1 * 1024 * 1024 then
|
||||
self.template = "cbi/value"
|
||||
Value.render(self, section, scope)
|
||||
else
|
||||
self.template = "cbi/dvalue"
|
||||
DummyValue.render(self, section, scope)
|
||||
end
|
||||
end
|
||||
local val_sec_end = s_partition_table:option(Value, "sec_end", translate("End Sector"))
|
||||
val_sec_end.render = function(self, section, scope)
|
||||
-- could create new partition
|
||||
if disk_info.partitions[section].number == -1 and disk_info.partitions[section].size > 1 * 1024 * 1024 then
|
||||
self.template = "cbi/value"
|
||||
Value.render(self, section, scope)
|
||||
else
|
||||
self.template = "cbi/dvalue"
|
||||
DummyValue.render(self, section, scope)
|
||||
end
|
||||
end
|
||||
val_sec_start.forcewrite = true
|
||||
val_sec_start.write = function(self, section, value)
|
||||
disk_info.partitions[section]._sec_start = value
|
||||
end
|
||||
val_sec_end.forcewrite = true
|
||||
val_sec_end.write = function(self, section, value)
|
||||
disk_info.partitions[section]._sec_end = value
|
||||
end
|
||||
s_partition_table:option(DummyValue, "size_formated", translate("Size"))
|
||||
if disk_info.p_table == "MBR" then
|
||||
s_partition_table:option(DummyValue, "type", translate("Type"))
|
||||
end
|
||||
s_partition_table:option(DummyValue, "used_formated", translate("Used"))
|
||||
s_partition_table:option(DummyValue, "free_formated", translate("Free Space"))
|
||||
s_partition_table:option(DummyValue, "usage", translate("Usage"))
|
||||
local dv_mount_point = s_partition_table:option(DummyValue, "mount_point", translate("Mount Point"))
|
||||
dv_mount_point.rawhtml = true
|
||||
dv_mount_point.render = function(self, section, scope)
|
||||
local new_mp = ""
|
||||
local v_mp_d
|
||||
for line in self["section"]["data"][section]["mount_point"]:gmatch("[^%s]+") do
|
||||
if line == '-' then
|
||||
new_mp = line
|
||||
break
|
||||
end
|
||||
for v_mp_d in line:gmatch('[^/]+') do
|
||||
if #v_mp_d > 12 then
|
||||
new_mp = new_mp .. "/" .. v_mp_d:sub(1,7) .. ".." .. v_mp_d:sub(-4)
|
||||
else
|
||||
new_mp = new_mp .."/".. v_mp_d
|
||||
end
|
||||
end
|
||||
new_mp = '<span title="'.. line .. '" >' ..new_mp ..'</span>' .. "<br/>"
|
||||
end
|
||||
self["section"]["data"][section]["mount_point"] = new_mp
|
||||
DummyValue.render(self, section, scope)
|
||||
end
|
||||
local val_fs = s_partition_table:option(Value, "fs", translate("File System"))
|
||||
val_fs.forcewrite = true
|
||||
val_fs.partitions = disk_info.partitions
|
||||
for k, v in pairs(format_cmd) do
|
||||
val_fs.format_cmd = val_fs.format_cmd and (val_fs.format_cmd .. "," .. k) or k
|
||||
end
|
||||
|
||||
val_fs.write = function(self, section, value)
|
||||
disk_info.partitions[section]._fs = value
|
||||
end
|
||||
val_fs.render = function(self, section, scope)
|
||||
-- use listvalue when partition not mounted
|
||||
if disk_info.partitions[section].mount_point == "-" and disk_info.partitions[section].number ~= -1 and disk_info.partitions[section].type ~= "extended" then
|
||||
self.template = "diskman/cbi/format_button"
|
||||
self.inputstyle = "reset"
|
||||
self.inputtitle = disk_info.partitions[section].fs == "raw" and translate("Format") or disk_info.partitions[section].fs
|
||||
Button.render(self, section, scope)
|
||||
-- self:reset_values()
|
||||
-- self.keylist = {}
|
||||
-- self.vallist = {}
|
||||
-- for k, v in pairs(format_cmd) do
|
||||
-- self:value(k,k)
|
||||
-- end
|
||||
-- self.default = disk_info.partitions[section].fs
|
||||
else
|
||||
-- self:reset_values()
|
||||
-- self.keylist = {}
|
||||
-- self.vallist = {}
|
||||
self.template = "cbi/dvalue"
|
||||
DummyValue.render(self, section, scope)
|
||||
end
|
||||
end
|
||||
-- btn_format = s_partition_table:option(Button, "_format")
|
||||
-- btn_format.template = "diskman/cbi/format_button"
|
||||
-- btn_format.partitions = disk_info.partitions
|
||||
-- btn_format.render = function(self, section, scope)
|
||||
-- if disk_info.partitions[section].mount_point == "-" and disk_info.partitions[section].number ~= -1 and disk_info.partitions[section].type ~= "extended" then
|
||||
-- self.inputtitle = translate("Format")
|
||||
-- self.template = "diskman/cbi/disabled_button"
|
||||
-- self.view_disabled = false
|
||||
-- self.inputstyle = "reset"
|
||||
-- for k, v in pairs(format_cmd) do
|
||||
-- self:depends("val_fs", "k")
|
||||
-- end
|
||||
-- -- elseif disk_info.partitions[section].mount_point ~= "-" and disk_info.partitions[section].number ~= -1 then
|
||||
-- -- self.inputtitle = "Format"
|
||||
-- -- self.template = "diskman/cbi/disabled_button"
|
||||
-- -- self.view_disabled = true
|
||||
-- -- self.inputstyle = "reset"
|
||||
-- else
|
||||
-- self.inputtitle = ""
|
||||
-- self.template = "cbi/dvalue"
|
||||
-- end
|
||||
-- Button.render(self, section, scope)
|
||||
-- end
|
||||
-- btn_format.forcewrite = true
|
||||
-- btn_format.write = function(self, section, value)
|
||||
-- local partition_name = "/dev/".. disk_info.partitions[section].name
|
||||
-- if not nixio.fs.access(partition_name) then
|
||||
-- m.errmessage = translate("Partition NOT found!")
|
||||
-- return
|
||||
-- end
|
||||
-- local fs = disk_info.partitions[section]._fs
|
||||
-- if not format_cmd[fs] then
|
||||
-- m.errmessage = translate("Filesystem NOT support!")
|
||||
-- return
|
||||
-- end
|
||||
-- local cmd = format_cmd[fs].cmd .. " " .. format_cmd[fs].option .. " " .. partition_name
|
||||
-- local res = luci.util.exec(cmd .. " 2>&1")
|
||||
-- if res and res:lower():match("error+") then
|
||||
-- m.errmessage = luci.util.pcdata(res)
|
||||
-- else
|
||||
-- luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman/partition/" .. dev))
|
||||
-- end
|
||||
-- end
|
||||
|
||||
local btn_action = s_partition_table:option(Button, "_action")
|
||||
btn_action.forcewrite = true
|
||||
btn_action.template = "diskman/cbi/disabled_button"
|
||||
btn_action.render = function(self, section, scope)
|
||||
-- if partition is mounted or the size < 1mb, then disable the add action
|
||||
if disk_info.partitions[section].mount_point ~= "-" or (disk_info.partitions[section].type ~= "extended" and disk_info.partitions[section].number == -1 and disk_info.partitions[section].size <= 1 * 1024 * 1024) then
|
||||
self.view_disabled = true
|
||||
-- self.inputtitle = ""
|
||||
-- self.template = "cbi/dvalue"
|
||||
elseif disk_info.partitions[section].type == "extended" and next(disk_info.partitions[section]["logicals"]) ~= nil then
|
||||
self.view_disabled = true
|
||||
else
|
||||
-- self.template = "diskman/cbi/disabled_button"
|
||||
self.view_disabled = false
|
||||
end
|
||||
if disk_info.partitions[section].number ~= -1 then
|
||||
self.inputtitle = translate("Remove")
|
||||
self.inputstyle = "remove"
|
||||
else
|
||||
self.inputtitle = translate("New")
|
||||
self.inputstyle = "add"
|
||||
end
|
||||
Button.render(self, section, scope)
|
||||
end
|
||||
btn_action.write = function(self, section, value)
|
||||
if value == translate("New") then
|
||||
local start_sec = disk_info.partitions[section]._sec_start and tonumber(disk_info.partitions[section]._sec_start) or tonumber(disk_info.partitions[section].sec_start)
|
||||
local end_sec = disk_info.partitions[section]._sec_end
|
||||
|
||||
if start_sec then
|
||||
-- for sector alignment
|
||||
local align = tonumber(disk_info.phy_sec) / tonumber(disk_info.logic_sec)
|
||||
align = (align < 2048) and 2048
|
||||
if start_sec < 2048 then
|
||||
start_sec = "2048" .. "s"
|
||||
elseif math.fmod( start_sec, align ) ~= 0 then
|
||||
start_sec = tostring(start_sec + align - math.fmod( start_sec, align )) .. "s"
|
||||
else
|
||||
start_sec = start_sec .. "s"
|
||||
end
|
||||
else
|
||||
m.errmessage = translate("Invalid Start Sector!")
|
||||
return
|
||||
end
|
||||
-- support +size format for End sector
|
||||
local end_size, end_unit = end_sec:match("^+(%d-)([bkmgtsBKMGTS])$")
|
||||
if tonumber(end_size) and end_unit then
|
||||
local unit ={
|
||||
B=1,
|
||||
S=512,
|
||||
K=1024,
|
||||
M=1048576,
|
||||
G=1073741824,
|
||||
T=1099511627776
|
||||
}
|
||||
end_unit = end_unit:upper()
|
||||
end_sec = tostring(tonumber(end_size) * unit[end_unit] / unit["S"] + tonumber(start_sec:sub(1,-2)) - 1 ) .. "s"
|
||||
elseif tonumber(end_sec) then
|
||||
end_sec = end_sec .. "s"
|
||||
else
|
||||
m.errmessage = translate("Invalid End Sector!")
|
||||
return
|
||||
end
|
||||
local part_type = "primary"
|
||||
|
||||
if disk_info.p_table == "MBR" and disk_info["extended_partition_index"] then
|
||||
if tonumber(disk_info.partitions[disk_info["extended_partition_index"]].sec_start) <= tonumber(start_sec:sub(1,-2)) and tonumber(disk_info.partitions[disk_info["extended_partition_index"]].sec_end) >= tonumber(end_sec:sub(1,-2)) then
|
||||
part_type = "logical"
|
||||
if tonumber(start_sec:sub(1,-2)) - tonumber(disk_info.partitions[section].sec_start) < 2048 then
|
||||
start_sec = tonumber(start_sec:sub(1,-2)) + 2048
|
||||
start_sec = start_sec .."s"
|
||||
end
|
||||
end
|
||||
elseif disk_info.p_table == "GPT" then
|
||||
-- AUTOMATIC FIX GPT PARTITION TABLE
|
||||
-- Not all of the space available to /dev/sdb appears to be used, you can fix the GPT to use all of the space (an extra 16123870 blocks) or continue with the current setting?
|
||||
local cmd = ' printf "ok\nfix\n" | parted ---pretend-input-tty /dev/'.. dev ..' print'
|
||||
luci.util.exec(cmd .. " 2>&1")
|
||||
end
|
||||
|
||||
-- partiton
|
||||
local cmd = dm.command.parted .. " -s -a optimal /dev/" .. dev .. " mkpart " .. part_type .." " .. start_sec .. " " .. end_sec
|
||||
local res = luci.util.exec(cmd .. " 2>&1")
|
||||
if res and res:lower():match("error+") then
|
||||
m.errmessage = luci.util.pcdata(res)
|
||||
else
|
||||
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman/partition/" .. dev))
|
||||
end
|
||||
elseif value == translate("Remove") then
|
||||
-- remove partition
|
||||
local number = tostring(disk_info.partitions[section].number)
|
||||
if (not number) or (number == "") then
|
||||
m.errmessage = translate("Partition not exists!")
|
||||
return
|
||||
end
|
||||
local cmd = dm.command.parted .. " -s /dev/" .. dev .. " rm " .. number
|
||||
local res = luci.util.exec(cmd .. " 2>&1")
|
||||
if res and res:lower():match("error+") then
|
||||
m.errmessage = luci.util.pcdata(res)
|
||||
else
|
||||
luci.http.redirect(luci.dispatcher.build_url("admin/system/diskman/partition/" .. dev))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return m
|
Loading…
Add table
Add a link
Reference in a new issue