#!/usr/bin/env perl
use strict;
use warnings;
use FindBin qw($Bin);
use lib "$Bin";
use Net::Proxmox::VE;
use Data::Dumper;
use Getopt::Long;
use HTTP::Headers;
use HTTP::Request::Common qw(POST);
my $host = undef;
my $username = undef;
my $password = undef;
my $all = 0;
my $debug = undef;
my $help = undef;
GetOptions (
'h|host=s' => \$host,
'u|username=s' => \$username,
'p|password=s' => \$password,
'a|all' => \$all,
'debug' => \$debug,
'help|?' => \$help,
) or die $!;
sub usage {
my $exit = shift;
print <<_HELP;
Usage spice-cli --host|-h host --username|-u username
--password|-p password
[--debug] [--help|-?] [--all|-a] [action]
action: list | term id
list List nodes in cluster
id vmid
term Open spice terminal on 'id'
--all: List all VM's. Default is to list running VM's.
_HELP
exit $exit;
}
usage(0) if $help;
do {
print "Missing host, password, and username\n";
usage(1);
} unless $host && $password && $username;
my $pve = Net::Proxmox::VE->new(
host => $host,
username => $username,
password => $password,
debug => $debug,
);
die "login failed\n" unless $pve->login;
die "invalid login ticket\n" unless $pve->check_login_ticket;
die "unsupport api version\n" unless $pve->api_version_check;
sub get_resources {
my $resources = $pve->get("/cluster/resources");
my %items = ();
foreach my $item (@$resources) {
next unless $item->{type} eq 'openvz' or $item->{type} eq 'qemu';
my $key = $item->{vmid};
$items{$key} = $item;
}
return \%items;
}
sub cmd_list {
my $resources = shift;
printf "%4s %6s %7s %-20s %s\n", 'vmid', 'type', 'status', 'node', 'name';
print "\n";
while ((my $id, my $vm) = each %$resources) {
next unless $vm->{type} eq 'openvz' or $vm->{type} eq 'qemu';
next unless $vm->{status} eq 'running' or $all;
printf "%4s %6s %7s %-20s %s\n", $vm->{vmid}, $vm->{type}, $vm->{status}, $vm->{node}, $vm->{name};
#print Dumper($vm);
}
}
sub cmd_term {
my ($resources, $id) = @_;
my $vm = $resources->{$id};
my $ua = $pve->{ua};
my $uri = $pve->url_prefix . '/api2/spiceconfig';
$uri .= "/nodes/$vm->{node}/$vm->{id}/spiceproxy";
my $request = HTTP::Request->new();
$request->uri($uri);
$request->header(
'cookie' => 'PVEAuthCookie=' . $pve->{ticket}->{ticket});
$request->header(
'CSRFPreventionToken' => $pve->{ticket}->{CSRFPreventionToken});
$request->method('POST');
$request->content("proxy=$vm->{node}");
my $response = $ua->request($request);
if ($debug) {
print $request->as_string, "\n";
print $response->as_string, "\n";
}
if ( $response->is_success ) {
my $content = $response->decoded_content;
open (PROXY, '>/tmp/spiceproxy') or die "$!\n";
print PROXY $content;
close PROXY;
my $cmd = ('remote-viewer /tmp/spiceproxy > /dev/null 2>&1');
if (exec($cmd)) {
if ($? == -1) {
print "failed to execute: $!\n";
} elsif ($? & 127) {
printf "child died with signal %d, %s coredump\n",
($? & 127), ($? & 128) ? 'with' : 'without';
} else {
printf "child exited with value %d\n", $? >> 8;
}
}
} else {
print "WARNING: request failed: " . $request->as_string . "\n";
print "WARNING: response status: " . $response->status_line . "\n";
}
}
my $action = $ARGV[0];
my $id = undef;
usage(1) unless $action;
my $resources = get_resources;
if ($action =~ /list/) {
cmd_list($resources);
} elsif ($action =~ /term/) {
($id = $ARGV[1]) or die 'missing id';
cmd_term($resources, $id);
} else {
print "$action: Unknown action\n";
usage(1);
}