2021-04-19 20:35:39 +02:00
require 'pmap'
class PVE :: Cli
2021-06-07 13:54:17 +02:00
using IPAddress :: ToSWithNetmaskForNetworks
2021-04-19 20:35:39 +02:00
def cli_base
cli . cmd :list , " List CT/VM-IDs " , aliases : [ 'ls' ] , & lambda { | target = nil |
connect
nodes = Proxmox :: Node . all
nodes .
flat_map { | n | [ n . method ( :lxc ) , n . method ( :qemu ) ] } .
flat_pmap { | m | m . call . map { | c | c . vmid . to_i } } .
sort .
each { | c | puts c }
}
2022-01-12 20:07:52 +01:00
cli . cmd ( :status , " Lists Nodes/VMs/CTs with status " , & lambda { | target = nil , sort : , node : nil , status : nil |
hosting_table target : target , status : status , sort : sort do | push |
node_opt ( node ) .
each ( & push ) . lazy .
flat_map { | n | [ Thread . new ( n , & :lxc ) , Thread . new ( n , & :qemu ) ] } .
each { | n | n . value . each & push }
end
2021-04-19 20:35:39 +02:00
} ) .
2022-01-12 20:07:52 +01:00
opt ( :sort , '-s' , '--sort=COLUMNS' , " Sort by COLUMNs eg hn for host and name ([s]tatus, h[a], [i]d, [n]ame (default), [h]ost, [u]ptime, [c]pu, [m]em, [d]isk) " , default : 'n' ) .
opt ( :node , '-n' , '--node=NODE' , " List only hosted by this NODE " ) .
opt ( :status , '-S' , '--status=STATUS' , " Filter for status (running, stopped, ...) (default: no filter) " )
2021-04-19 20:35:39 +02:00
2022-10-05 12:55:03 +02:00
def val2str v
case v
when true then 1
when false then 0
else v
end
end
2021-04-19 20:35:39 +02:00
def prepare_show_config cnf
r = { }
cnf . each do | k , v |
case k
when :network
v . each do | net |
s =
net .
reject { | k , v | :card == k } .
sort_by { | k , v | :name == k ? :AAAAAAAAA : k } .
2022-10-05 12:55:03 +02:00
map { | k , v | " #{ k } = #{ val2str v } " }
r [ net [ :card ] . to_sym ] = s . join ','
2021-04-19 20:35:39 +02:00
end
when :sshkeys
2022-10-05 12:55:03 +02:00
r [ k ] = CGI . unescape ( v ) . gsub ( / ^ / , " " * 14 ) . gsub / \ A {14}| \ n \ z / , ''
when :features
r [ k ] = v . map { | k , v | " #{ k } = #{ val2str v } " } . join ','
2021-04-19 20:35:39 +02:00
else
2022-10-05 12:55:03 +02:00
r [ k ] = val2str ( v ) . to_s . gsub ( / $^ / , " " * 14 ) . gsub / \ n \ z / , ''
2021-04-19 20:35:39 +02:00
end
end
r
end
def show_config cnf , old = nil
cnf = prepare_show_config cnf
2022-10-05 12:55:03 +02:00
l = cnf . keys . map ( & :length ) . max
2021-04-19 20:35:39 +02:00
if old
old = prepare_show_config old
( cnf . keys + old . keys ) . uniq . sort . each do | k |
v , o = cnf [ k ] , old [ k ]
if v == o
2022-10-05 12:55:03 +02:00
puts " #{ k } : #{ ' ' * ( l - k . length ) } #{ v } "
2021-04-19 20:35:39 +02:00
else
2022-10-05 12:55:03 +02:00
puts " \e [31m #{ k } : #{ ' ' * ( l - k . length ) } #{ o } \e [0m " unless o . nil?
puts " \e [32m #{ k } : #{ ' ' * ( l - k . length ) } #{ v } \e [0m " unless v . nil?
2021-04-19 20:35:39 +02:00
end
end
else
2022-10-05 12:55:03 +02:00
cnf . sort_by { | k , _ | k } . each do | k , v |
puts " #{ k } : #{ ' ' * ( l - k . length ) } #{ v } "
2021-04-19 20:35:39 +02:00
end
end
end
cli . sub :config , " CT/VM Configuration " , min : 2 , aliases : %w[ cnf ] do | ccli |
2021-12-08 22:35:21 +01:00
ccli . cmd :help , '' , aliases : [ nil , '-h' , '--help' ] , & lambda { | * args | help ccli , * args }
2021-04-19 20:35:39 +02:00
2021-09-27 16:07:08 +02:00
ccli . cmd :set , " Set Configs for CT/VM " , min : 3 , & lambda { | name_or_id , * args |
2021-04-19 20:35:39 +02:00
if %w[ -h --help ] . include? name_or_id
STDERR . puts " Usage: set -h|--help # Show help "
STDERR . puts " set ct|vm --CNF1=VAL1 --CNF2=VAL2 ... # Set config-value. Empty value clears field. "
exit 1
end
opts = { }
until args . empty?
case arg = args . shift
2022-10-05 12:55:03 +02:00
when / \ A--no-( \ w+) \ z /
opts [ $1 . to_sym ] = false
2021-04-19 20:35:39 +02:00
when / \ A--( \ w+)=(.*) \ z /
opts [ $1 . to_sym ] = $2
2022-10-05 12:55:03 +02:00
when / \ A--( \ w+)! \ z / , / \ A--del-( \ w+) \ z /
opts [ $1 . to_sym ] = nil
2021-04-19 20:35:39 +02:00
when / \ A--( \ w+) \ z /
2022-10-05 12:55:03 +02:00
n = $1 . to_sym
opts [ n ] =
if args . empty? or / \ A-- / == args . first
true
else opts [ n ] = args . shift
end
2021-04-19 20:35:39 +02:00
else
raise UsageError , " Expection option to set. What do you mean with: #{ arg } "
end
end
opts . each do | k , v |
opts [ k ] =
2022-10-05 12:55:03 +02:00
case v
2021-04-19 20:35:39 +02:00
when '' then nil
else v
end
end
% i [ migrate_downtime ] . each do | k |
next unless opts . has_key? k
opts [ k ] =
case v = opts [ k ]
when nil , '' , 'nil' then nil
else v . to_f
end
end
% i [ memory background_delay balloon cores cpulimit cpuunits migrate_speed shares smp sockets vcpus swap tty ] . each do | k |
next unless opts . has_key? k
opts [ k ] =
case v = opts [ k ]
when nil , '' , 'nil' then nil
else v . to_i
end
end
% i [ unprivileged debug onboot protection template ] . each do | k |
next unless opts . has_key? k
opts [ k ] =
case v = opts [ k ]
2022-10-05 12:55:03 +02:00
when true , * %w[ 1 T TRUE t true True Y YES y yes Yes ] then true
when false , * %w[ 0 F FALSE f false False N NO n no No ] then false
2021-04-19 20:35:39 +02:00
when '' , 'nil' , nil then nil
else raise UsageError , " Boolean expected, given: #{ v . inspect } "
end
end
connect
th = Proxmox :: LXC . find ( name_or_id ) || Proxmox :: Qemu . find_by_name ( name_or_id )
2022-10-05 12:55:03 +02:00
raise UsageError , " Container or Node not found: #{ name_or_id } " unless th
2021-04-19 20:35:39 +02:00
old = th . config
opts [ :digest ] || = old [ :digest ]
th . cnfset opts
show_config th . config , old
}
2021-09-27 16:07:08 +02:00
2022-01-12 20:07:52 +01:00
ccli . cmd :show , " Show Config of CT/VM " , & lambda { | name_or_id |
2021-09-27 16:07:08 +02:00
connect
th = Proxmox :: LXC . find ( name_or_id ) || Proxmox :: Qemu . find_by_name ( name_or_id )
2022-10-05 12:55:03 +02:00
raise UsageError , " Container or Node not found: #{ name_or_id } " unless th
2021-09-27 16:07:08 +02:00
show_config th . config
}
2021-04-19 20:35:39 +02:00
end
2021-11-25 15:55:07 +01:00
cli . cmd ( :enter , " Enter Console of CT/Node " , & lambda { | name_or_id |
2021-04-19 20:35:39 +02:00
connect
th = Proxmox :: LXC . find ( name_or_id ) || Proxmox :: Node . find_by_name ( name_or_id )
raise UsageError , " Container or Node not found: #{ name_or_id } " unless th
STDERR . puts " ! #{ $? . exitstatus } " unless th . enter
2021-11-25 15:55:07 +01:00
} ) .
completion do | * pre , arg |
completion_helper * pre , arg do | f |
complete_lxc ( f ) + complete_node ( f )
end
end
2021-04-19 20:35:39 +02:00
2022-10-05 12:55:03 +02:00
cli . cmd ( :run , " Starts CT/VM " , aliases : %w[ start star ] , & lambda { | * names_or_ids |
2021-04-19 20:35:39 +02:00
connect
2022-10-05 12:55:03 +02:00
raise UsageError , " Nothing to start? " if names_or_ids . empty?
per_argument names_or_ids , print : " \e [34;1mRunning CT/VM \e [0m %s: \n " do | name_or_id |
th = Proxmox :: LXC . find ( name_or_id ) || Proxmox :: Qemu . find_by_name ( name_or_id )
raise UsageError , " Container or Node not found: #{ name_or_id } " unless th
start th
end
2021-11-25 15:55:07 +01:00
} ) .
completion do | * pre , arg |
completion_helper * pre , arg do | f |
complete_lxc ( f ) + complete_qemu ( f )
end
end
2021-04-19 20:35:39 +02:00
2022-10-05 12:55:03 +02:00
cli . cmd ( :migrate , " Migrates (halted) CTs/VMs to an other host " , min : 2 , & lambda { | target , * names_or_ids , fire : , timeout : , secs : |
connect
node = Proxmox :: Node . find_by_name! target
per_argument names_or_ids , print : " \e [1;34mCT/VM(s) \e [0m %s: \n " do | name_or_id |
th = Proxmox :: LXC . find ( name_or_id ) || Proxmox :: Qemu . find_by_name ( name_or_id )
raise UsageError , " CT/VM not found: #{ name_or_id } " unless th
unless th . stopped?
raise UsageError , " VM #{ name_or_id } is running. Shutdown or for VM-online-migration see `help qm migrate` " if Proxmox :: Qemu === th
raise UsageError , " CT #{ name_or_id } is running. You have to shutdown it. "
end
task = th . migrate node
wait task , text : " Migrating " , timeout : timeout unless fire
end
} ) . tap { | c | opts_wait c }
2021-04-19 20:35:39 +02:00
#cli.cmd :reboot, "Reboot CT/VM (not implemented, yet)", min: 6, &lambda {|name_or_id|
# connect
# th = Proxmox::LXC.find( name_or_id) || Proxmox::Qemu.find_by_name( name_or_id)
# raise UsageError, "Container or Node not found: #{name_or_id}" unless th
# reboot th
#}
2022-10-05 12:55:03 +02:00
cli . cmd ( :stop , " Stops CT/VM " , min : 4 , & lambda { | * names_or_ids |
2021-04-19 20:35:39 +02:00
connect
2022-10-05 12:55:03 +02:00
per_argument names_or_ids , print : " \e [34;1mStopping CT/VM \e [0m %s: \n " do | name_or_id |
th = Proxmox :: LXC . find ( name_or_id ) || Proxmox :: Qemu . find_by_name ( name_or_id )
raise UsageError , " Container or Node not found: #{ name_or_id } " unless th
stop th
end
2021-11-25 15:55:07 +01:00
} ) .
completion do | * pre , arg |
completion_helper * pre , arg do | f |
complete_lxc ( f ) + complete_qemu ( f )
end
end
2021-04-19 20:35:39 +02:00
2021-12-08 22:35:21 +01:00
cli . cmd ( :help , '' , aliases : [ '-h' , '--help' ] , & lambda { | * args , full : |
if full
cli . help_full * args , output : STDERR
else
cli . help * args , output : STDERR
end
} ) .
opt ( :full , '-f' , '--[no-]full' , 'Includes all commands of all subcommands.' , default : false )
2021-04-19 20:35:39 +02:00
2021-12-08 22:35:21 +01:00
cli . cmd :cli , 'Opens interactive console' , min : 3 , aliases : [ nil ] , & lambda {
2021-04-19 20:35:39 +02:00
@interactive = true
cli . interactive ( File . basename ( $0 , '.rb' ) ) . run
}
end
end