From 6530c40749ccafc20d8e4767850458fe546841dc Mon Sep 17 00:00:00 2001 From: Denis Knauf Date: Thu, 21 Sep 2023 14:55:24 +0200 Subject: [PATCH] UPID-Parser, Task status & monitoring --- lib/pve/cli.rb | 14 ++++---- lib/pve/cli/task.rb | 65 ++++++++++++++++++++++++++++++----- lib/pve/proxmox.rb | 82 +++++++++++++++++++++++++++++++++++++-------- 3 files changed, 131 insertions(+), 30 deletions(-) diff --git a/lib/pve/cli.rb b/lib/pve/cli.rb index 172fbbb..836f302 100644 --- a/lib/pve/cli.rb +++ b/lib/pve/cli.rb @@ -380,9 +380,9 @@ class PVE::Cli end 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] - 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] when nil, '', /\Aa(ll?)?\z/i then nil else @@ -533,7 +533,7 @@ class PVE::Cli end [ 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" else v.status end, @@ -546,14 +546,14 @@ class PVE::Cli end, v.name, node, 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?(:mem) ? TablizedOutput::V.new( v.mem, Measured.bytes( v.mem)) : 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?(: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 TablizedOutput::Percentage.new( v.disk.to_f/v.maxdisk.to_f) else unknown end, - v.respond_to?(:tags) ? v.tags.join(', ') : '', + v.respond_to?(:tags) ? v.tags.join(', ') : '', ] end end diff --git a/lib/pve/cli/task.rb b/lib/pve/cli/task.rb index 3b1d201..6ec4492 100644 --- a/lib/pve/cli/task.rb +++ b/lib/pve/cli/task.rb @@ -1,4 +1,33 @@ 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 cli.sub :task, "Inspect tasks" do |tcli| 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.flat_map do |n| 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| @@ -22,21 +51,39 @@ def cli_task 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 nodes = Proxmox::Node.all nodes = nodes.select {|n| node == n.name } if node tasks = {} - loop do - nodes.flat_map do |n| - n.tasks.map &:upid - end.sort.each do |upid| + nodes.each {|n| n.tasks.each {|t| tasks[t.upid.upid] = true } } + begin + loop do + 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 + rescue Interrupt + STDERR.print "\e[2J\e[1;1H" end - } + }) + + tcli.cmd( :help, '', aliases: ['-h', '--help']) {|*args| help ct_cli, *args } end end end diff --git a/lib/pve/proxmox.rb b/lib/pve/proxmox.rb index e7dda5a..08d840f 100644 --- a/lib/pve/proxmox.rb +++ b/lib/pve/proxmox.rb @@ -21,6 +21,8 @@ module Proxmox end class AlreadyLocked < Exception end + class UnparsableUPID < Exception + end def self.cnfstr2hash str str. @@ -84,7 +86,7 @@ module Proxmox end 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 def self.find name_or_id @@ -299,12 +301,22 @@ module Proxmox def refresh! d = rest_get @rest_prefix d[:starttime] &&= Time.at d[:starttime] - d = {exitstatus: nil}.merge d __update__ d.merge( node: @node, upid: @upid, task: @task) end def __update__ **data 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 end @@ -319,11 +331,51 @@ module Proxmox "#<#{self.class.name}|#{@upid} node=#{@node.node} #{h.join ' '}>" end - def running?() 'running' == @status end - def finished?() 'stopped' == @status end + def running?() :running == @status end + def finished?() :stopped == @status end alias stopped? finished? - def successfull?() stopped? ? 'OK' == @exitstatus : nil end - def failed?() stopped? ? 'OK' != @exitstatus : nil end + def successfull?() 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 : + (?[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?) : + (?[0-9A-Fa-f]{8}) : + (?[0-9A-Fa-f]{8,9}) : + (?[0-9A-Fa-f]{8}) : + (?[^:\s]+) : + (?[^:\s]*) : + (?[^:\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 def rest_prefix @@ -332,7 +384,7 @@ module Proxmox def initialize rest_prefix - @sid = upid + @sid, @upid = upid, UPID.new( upid) end def inspect @@ -349,6 +401,10 @@ module Proxmox end class Hosted < Base + def self.all + Node.all.flat_map {|n| n.qemu + n.lxc } + end + def refresh! __updata__ rest_get( "#{@rest_prefix}/status/current").merge( node: @node, t: @t) end @@ -450,25 +506,23 @@ module Proxmox rest_put "#{@rest_prefix}/config", **r end + # it is only allowed to lock local guests, because it is only possible to unlock local guests. def lock reason, &exe cnf = config raise Proxmox::AlreadyLocked, "Machine #{self} Already locked for: #{cnf[:lock].inspect}" unless cnf[:lock].nil? r = rest_put "#{@rest_prefix}/config", {lock: reason} if block_given? - begin - return yield - ensure - unlock + begin return yield + ensure unlock end - else - r + else r end end def unlock system 'pct', 'unlock', @vmid.to_s raise Proxmox::UnlockFailed unless $?.success? - #rest_put "#{@rest_prefix}/config", {delete: %w[lock]} + #rest_put "#{@rest_prefix}/config", {delete: %w[lock], skiplock: 1} end def resize disk, size