UPID-Parser, Task status & monitoring

This commit is contained in:
Denis Knauf 2023-09-21 14:55:24 +02:00
parent 83211a817f
commit 6530c40749
3 changed files with 131 additions and 30 deletions

View file

@ -380,9 +380,9 @@ class PVE::Cli
end end
status = status =
case status case status
when /\Asta(r(t(ed?)?)?)?\z/i, /\Aon(l(i(ne?)?)?)?\z/i, /\Ar(u(n(n(i(ng?)?)?)?)?)?\z/i, '1' when /\Asta(?:r(?:t(?:ed?)?)?)?\z/i, /\Aon(?:l(?:i(?:ne?)?)?)?\z/i, /\Ar(?:u(?:n(?:n(?:i(?:ng?)?)?)?)?)?\z/i, '1'
%i[started online running] %i[started online running]
when /\Asto(p(p(ed?)?)?)?\z/i, /\Aof(f(l(i(ne?)?)?)?)?\z/i, '0' when /\Asto(?:p(?:p(?:ed?)?)?)?\z/i, /\Aof(?:f(?:l(?:i(?:ne?)?)?)?)?\z/i, '0'
%i[stopped offline] %i[stopped offline]
when nil, '', /\Aa(ll?)?\z/i then nil when nil, '', /\Aa(ll?)?\z/i then nil
else else
@ -533,7 +533,7 @@ class PVE::Cli
end end
[ [
case v.status case v.status
when :running, :online then ColoredString.new v.status, "32" when :running, :online then ColoredString.new v.status, "32"
when :stopped, :offline then ColoredString.new v.status, "31" when :stopped, :offline then ColoredString.new v.status, "31"
else v.status else v.status
end, end,
@ -546,14 +546,14 @@ class PVE::Cli
end, end,
v.name, node, v.name, node,
v.respond_to?(:uptime) ? TablizedOutput::V.new( v.uptime, Measured.seconds( v.uptime)) : unknown, v.respond_to?(:uptime) ? TablizedOutput::V.new( v.uptime, Measured.seconds( v.uptime)) : unknown,
v.respond_to?(:cpu) ? TablizedOutput::Percentage.new( v.cpu) : unknown, v.respond_to?(:cpu) ? TablizedOutput::Percentage.new( v.cpu) : unknown,
v.respond_to?(:mem) ? TablizedOutput::V.new( v.mem, Measured.bytes( v.mem)) : unknown, v.respond_to?(:mem) ? TablizedOutput::V.new( v.mem, Measured.bytes( v.mem)) : unknown,
v.respond_to?(:maxmem) ? TablizedOutput::Percentage.new( v.mem/v.maxmem.to_f) : unknown, v.respond_to?(:maxmem) ? TablizedOutput::Percentage.new( v.mem/v.maxmem.to_f) : unknown,
v.respond_to?(:disk) ? TablizedOutput::V.new( v.disk.to_i, Measured.bytes( v.disk.to_i)) : unknown, v.respond_to?(:disk) ? TablizedOutput::V.new( v.disk.to_i, Measured.bytes( v.disk.to_i)) : unknown,
if v.respond_to?(:maxdisk) and 0 < v.maxdisk.to_i if v.respond_to?(:maxdisk) and 0 < v.maxdisk.to_i
TablizedOutput::Percentage.new( v.disk.to_f/v.maxdisk.to_f) TablizedOutput::Percentage.new( v.disk.to_f/v.maxdisk.to_f)
else unknown end, else unknown end,
v.respond_to?(:tags) ? v.tags.join(', ') : '', v.respond_to?(:tags) ? v.tags.join(', ') : '',
] ]
end end
end end

View file

@ -1,4 +1,33 @@
class PVE::Cli class PVE::Cli
def task_table order: nil, &exe
to = TablizedOutput.new %w[S Starttime Node SID Type UPID], format: '<<<<<<'
hosted = {}
Proxmox::Hosted.all.each {|h| hosted[h.vmid.to_i] = h }
hosted.delete nil
exe.call lambda {|t|
u = t.upid
v = u.id ? hosted[u.id.to_i] : t.node
to.push [
case t.status.state
when :running then ColoredString.new '...', '30'
when :success then ColoredString.new 'OK', '32'
when :failed then ColoredString.new 'failed', '31'
end,
u.starttime.strftime( '%Y-%m-%d %H:%M:%S'),
u.node,
case v&.t
when 'nd' then ColoredString.new v.sid, '33'
when 'qm' then ColoredString.new v.sid, '35'
when 'ct' then ColoredString.new v.sid, '36'
when nil then u.id.inspect
else v.sid
end,
u.dtype, u.upid
]
}
to.print order: order
end
def cli_task def cli_task
cli.sub :task, "Inspect tasks" do |tcli| cli.sub :task, "Inspect tasks" do |tcli|
tcli.cmd :list, "List done tasks", &lambda {|node=nil| tcli.cmd :list, "List done tasks", &lambda {|node=nil|
@ -7,7 +36,7 @@ def cli_task
nodes = nodes.select {|n| node == n.name } if node nodes = nodes.select {|n| node == n.name } if node
nodes.flat_map do |n| nodes.flat_map do |n|
n.tasks.map &:upid n.tasks.map &:upid
end.sort.each {|upid| puts upid } end.sort_by(&:upid).each {|upid| puts upid }
} }
tcli.cmd :get, "Inspect a task", &lambda {|upid| tcli.cmd :get, "Inspect a task", &lambda {|upid|
@ -22,21 +51,39 @@ def cli_task
end end
} }
tcli.cmd :status, "Shows tasks states", &lambda {|upid| tcli.cmd( :status, "Lists tasks with status", aliases: [nil], &lambda {|target=nil, sort: nil, node: nil, status: nil|
} connect
task_table order: [2] do |push|
Proxmox::Node.all.each {|n| n.tasks.each {|t| p t; push.call t } }
end
})
tcli.cmd :monitor, "Monitors running tasks", &lambda {|node: nil| tcli.cmd( :monitor, "Monitors running tasks", &lambda {|node: nil|
connect connect
nodes = Proxmox::Node.all nodes = Proxmox::Node.all
nodes = nodes.select {|n| node == n.name } if node nodes = nodes.select {|n| node == n.name } if node
tasks = {} tasks = {}
loop do nodes.each {|n| n.tasks.each {|t| tasks[t.upid.upid] = true } }
nodes.flat_map do |n| begin
n.tasks.map &:upid loop do
end.sort.each do |upid| task_table order: [2] do |push|
begin
nodes.
flat_map {|n| n.tasks }.
select {|t| tasks[t.upid.upid] != true or t.running? }.
each( &push)
STDERR.print "\e[2J\e[1;1H"
rescue RestClient::InternalServerError
end
end
sleep 1
end end
rescue Interrupt
STDERR.print "\e[2J\e[1;1H"
end end
} })
tcli.cmd( :help, '', aliases: ['-h', '--help']) {|*args| help ct_cli, *args }
end end
end end
end end

View file

@ -21,6 +21,8 @@ module Proxmox
end end
class AlreadyLocked < Exception class AlreadyLocked < Exception
end end
class UnparsableUPID < Exception
end
def self.cnfstr2hash str def self.cnfstr2hash str
str. str.
@ -84,7 +86,7 @@ module Proxmox
end end
def self.find_by_vmid vmid def self.find_by_vmid vmid
Proxmox::LXC.find_by_vmid( vmid) || Proxmox::Qemu.find_by_vmid( vmid) || Proxmox::Node.find_by_vmid( vmid) Proxmox::LXC.find_by_vmid( vmid) || Proxmox::Qemu.find_by_vmid( vmid)
end end
def self.find name_or_id def self.find name_or_id
@ -299,12 +301,22 @@ module Proxmox
def refresh! def refresh!
d = rest_get @rest_prefix d = rest_get @rest_prefix
d[:starttime] &&= Time.at d[:starttime] d[:starttime] &&= Time.at d[:starttime]
d = {exitstatus: nil}.merge d
__update__ d.merge( node: @node, upid: @upid, task: @task) __update__ d.merge( node: @node, upid: @upid, task: @task)
end end
def __update__ **data def __update__ **data
data = data.merge t: 'status' data = data.merge t: 'status'
data[:status] = data[:status]&.to_sym
data[:exitstatus] = data[:exitstatus]&.to_sym
data[:state] =
case data[:status]
when :running then :running
when :stopped
case data[:exitstatus]
when :OK then :success
else :failed
end
end
super **data super **data
end end
@ -319,11 +331,51 @@ module Proxmox
"#<#{self.class.name}|#{@upid} node=#{@node.node} #{h.join ' '}>" "#<#{self.class.name}|#{@upid} node=#{@node.node} #{h.join ' '}>"
end end
def running?() 'running' == @status end def running?() :running == @status end
def finished?() 'stopped' == @status end def finished?() :stopped == @status end
alias stopped? finished? alias stopped? finished?
def successfull?() stopped? ? 'OK' == @exitstatus : nil end def successfull?() stopped? ? :OK == @exitstatus : nil end
def failed?() stopped? ? 'OK' != @exitstatus : nil end def failed?() stopped? ? :OK != @exitstatus : nil end
end
def running?() status.running? end
def finished?() status.finished? end
alias stopped? finished?
def successfull?() status.successfull? end
def failed?() status.failed? end
class UPID
RE =
/^
UPID :
(?<node>[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?) :
(?<pid>[0-9A-Fa-f]{8}) :
(?<pstart>[0-9A-Fa-f]{8,9}) :
(?<starttime>[0-9A-Fa-f]{8}) :
(?<dtype>[^:\s]+) :
(?<id>[^:\s]*) :
(?<user>[^:\s]+) :
$/x
attr_reader :upid, :node, :pid, :pstart, :starttime, :dtype, :id, :user
alias to_s upid
def initialize upid
m = RE.match upid
raise UnparsableUPID, "UPID cannot be parsed: #{upid.inspect} (#{upid.class})" unless m
@upid = upid
@node = m[:node]
@pid = m[:pid].to_i 16
@pstart = m[:pstart].to_i 16
@starttime = Time.at m[:starttime].to_i( 16)
@dtype = m[:dtype]
@id = case m[:id] when '' then nil else m[:id].to_i end
@user = m[:user]
end
def inspect
"#<#{self.class.name} #{@upid}>"
end
end end
def rest_prefix def rest_prefix
@ -332,7 +384,7 @@ module Proxmox
def initialize def initialize
rest_prefix rest_prefix
@sid = upid @sid, @upid = upid, UPID.new( upid)
end end
def inspect def inspect
@ -349,6 +401,10 @@ module Proxmox
end end
class Hosted < Base class Hosted < Base
def self.all
Node.all.flat_map {|n| n.qemu + n.lxc }
end
def refresh! def refresh!
__updata__ rest_get( "#{@rest_prefix}/status/current").merge( node: @node, t: @t) __updata__ rest_get( "#{@rest_prefix}/status/current").merge( node: @node, t: @t)
end end
@ -450,25 +506,23 @@ module Proxmox
rest_put "#{@rest_prefix}/config", **r rest_put "#{@rest_prefix}/config", **r
end end
# it is only allowed to lock local guests, because it is only possible to unlock local guests.
def lock reason, &exe def lock reason, &exe
cnf = config cnf = config
raise Proxmox::AlreadyLocked, "Machine #{self} Already locked for: #{cnf[:lock].inspect}" unless cnf[:lock].nil? raise Proxmox::AlreadyLocked, "Machine #{self} Already locked for: #{cnf[:lock].inspect}" unless cnf[:lock].nil?
r = rest_put "#{@rest_prefix}/config", {lock: reason} r = rest_put "#{@rest_prefix}/config", {lock: reason}
if block_given? if block_given?
begin begin return yield
return yield ensure unlock
ensure
unlock
end end
else else r
r
end end
end end
def unlock def unlock
system 'pct', 'unlock', @vmid.to_s system 'pct', 'unlock', @vmid.to_s
raise Proxmox::UnlockFailed unless $?.success? raise Proxmox::UnlockFailed unless $?.success?
#rest_put "#{@rest_prefix}/config", {delete: %w[lock]} #rest_put "#{@rest_prefix}/config", {delete: %w[lock], skiplock: 1}
end end
def resize disk, size def resize disk, size