Commit 3d0bbb36 authored by Francesc Guasch's avatar Francesc Guasch
Browse files

wip(request): pass ISO name and manage requests uid

issue #1136
parent e743b04e
#!/usr/bin/env perl
use warnings;
use strict;
use Data::Dumper;
use Getopt::Long;
use JSON::XS qw(decode_json);
use lib './lib';
use Ravada;
use Ravada::Request;
use Ravada::Utils;
no warnings "experimental::signatures";
use feature qw(signatures);
my $ME = $0;
$ME =~ s{.*/}{};
my $USAGE = $ME." command [--help] [--wait] [options]\n"
." - help: Help and usage message\n"
." - wait: Wait for request to complete"
;
my ($command) = shift @ARGV if $ARGV[0] && $ARGV[0] !~ /^-/;
my $WAIT;
my $HELP;
my %option;
$command =~ s/-/_/g if $command;
GetOptions(\%option, valid_options($command)) or exit;
$HELP= delete $option{help} if exists $option{help};
$command = shift @ARGV if !$command && $HELP && $ARGV[0];
$command =~ s/-/_/g if $command;
help($command, $option{doc}) if $HELP;
die "$USAGE\n" if !defined $command;
$WAIT= delete $option{wait} if exists $option{wait};
#################################################################
sub valid_options($command) {
my @options = ('wait','help','doc=s');
return @options if !$command;
my $definition = Ravada::Request::valid_args($command)
or die "Error: Unknown command $command\n";
for my $field ( keys %$definition ) {
$field =~ s/_/-/g;
if ($field eq 'uid' || $field =~ /^id_/ || $field =~ /^(at|timeout)$/) {
$field .= "=i";
} else {
$field .= "=s";
}
push @options,$field;
}
return @options;
}
sub help($command, $format=undef) {
if ($format && $format eq 'rst') {
print $ME;
print " $command" if $command;
print "\n".("=" x 4)."\n";
}
print "$USAGE\n" if !$format || !$command;
if (!$command) {
print "\nCommands:\n".('-' x 4)."\n";
my %valid = Ravada::Request::valid_args();
for my $field ( sort keys %valid ) {
print "- $field\n";
}
exit;
}
my $definition = Ravada::Request::valid_args_cli($command)
or die "Error: Unknown command $command\n";
delete $definition->{uid};
print "\n$command\n".("=" x length($command))."\n";
my @mandatory = grep { $definition->{$_} == 1 } keys %$definition;
if (@mandatory) {
print "Mandatory arguments:\n".('-' x 4)."\n";
for my $option( sort @mandatory ) {
next if $definition->{$option} != 1;
print "- $option".info($option)."\n";
delete $definition->{$option};
}
print "\n";
}
if (keys %$definition) {
print "Optional arguments:\n".('-' x 4)."\n";
for my $option( sort keys %$definition ) {
print "- $option".info($option)."\n";
}
}
exit;
}
sub info($option) {
my %info = (
uid => "User id that executes the request"
, at => 'Run at a given time. Format time is seconds since epoch'
,after_request => 'Run after request specified by id is done'
, id_domain => "Id of the domain or virtual machine"
);
my $text = $info{$option};
return '' if !$text;
return ": $text";
}
sub fix_options_slash($option) {
for my $field ( keys %$option ) {
if ($field =~ /-/) {
my $field2 = $field;
$field2 =~ s/-/_/g;
$option->{$field2} = $option->{$field};
delete $option->{$field};
}
}
}
sub extract_data($option) {
my $json = $option->{data};
my $data = decode_json($json);
$option->{data} = $data;
}
#################################################################
my $RVD_BACK = Ravada->new();
$option{uid} = Ravada::Utils::user_daemon->id if !exists $option{uid};
fix_options_slash(\%option);
extract_data(\%option) if $option{data};
my $request = Ravada::Request->new_request(
$command
,%option
);
print "Requested $command id=".$request->id."\n";
exit if !$WAIT;
my $msg = '';
my $t0 = time;
for (;;) {
my $msg_curr = $request->status;
if($request->error) {
$msg_curr .=" ".$request->error;
}
if ($msg_curr ne $msg || time - $t0 > 2) {
print localtime." ".$msg_curr."\n";
$msg = $msg_curr;
$t0 = time;
next;
}
last if $request->status eq 'done';
sleep 1;
}
print $request->output."\n" if defined $request->output;
......@@ -1294,7 +1294,7 @@ sub _connect_dbh {
sleep 1;
warn "Try $try $@\n";
}
die ($@ or "Can't connect to $driver $db at $host");
confess ($@ or "Can't connect to $driver $db at $host");
}
=head2 display_ip
......@@ -1547,11 +1547,12 @@ sub create_domain {
}
}
my $vm_name = delete $args{vm};
delete $args{uid};
my $start = $args{start};
my $id_base = $args{id_base};
my $id_owner = $args{id_owner} or confess "Error: missing id_owner ".Dumper(\%args);
_check_args(\%args,qw(iso_file id_base id_iso id_owner name active swap memory disk id_template start remote_ip request vm));
_check_args(\%args,qw(iso_file id_base id_iso id_owner name active swap memory disk id_template start remote_ip request vm iso_name));
confess "ERROR: Argument vm required" if !$id_base && !$vm_name;
......@@ -2944,14 +2945,14 @@ sub _cmd_change_hardware {
);
}
sub _cmd_shutdown {
sub _cmd_shutdown_machine {
my $self = shift;
my $request = shift;
my $uid = $request->args('uid');
my $name = $request->defined_arg('name');
my $id_domain = $request->defined_arg('id_domain');
my $timeout = ($request->args('timeout') or 60);
my $timeout = ($request->defined_arg('timeout') or 60);
my $id_vm = $request->defined_arg('id_vm');
confess "ERROR: Missing id_domain or name" if !$id_domain && !$name;
......@@ -3199,9 +3200,10 @@ sub _cmd_list_network_interfaces($self, $request) {
}
sub _cmd_list_isos($self, $request){
my $vm_type = $request->args('vm_type');
my $vm_type = $request->defined_arg('vm_type');
my $vm = $self->vm->[0];
$vm = Ravada::VM->open( type => $vm_type ) if $vm_type;
my $vm = Ravada::VM->open( type => $vm_type );
my @isos = sort { "\L$a" cmp "\L$b" } $vm->search_volume_path_re(qr(.*\.iso$));
$request->output(encode_json(\@isos));
......@@ -3422,15 +3424,21 @@ sub _req_method {
clone => \&_cmd_clone
,start => \&_cmd_start
,start_domain => \&_cmd_start
,start_clones => \&_cmd_start_clones
,pause => \&_cmd_pause
,create => \&_cmd_create
,create_domain => \&_cmd_create
,remove => \&_cmd_remove
,remove_domain => \&_cmd_remove
,resume => \&_cmd_resume
,dettach => \&_cmd_dettach
,cleanup => \&_cmd_cleanup
,download => \&_cmd_download
,shutdown => \&_cmd_shutdown
,shutdown => \&_cmd_shutdown_machine
,shutdown_domain => \&_cmd_shutdown_machine
,hybernate => \&_cmd_hybernate
,set_driver => \&_cmd_set_driver
,screenshot => \&_cmd_screenshot
......
......@@ -33,7 +33,7 @@ my $COUNT = 0;
our %FIELD = map { $_ => 1 } qw(error output);
our %FIELD_RO = map { $_ => 1 } qw(id name);
our $args_manage = { name => 1 , uid => 1 };
our $args_manage = { name => 1 , uid => 1, id_domain => 1 };
our $args_prepare = { id_domain => 1 , uid => 1 };
our $args_remove_base = { id_domain => 1 , uid => 1 };
our $args_manage_iptables = {uid => 1, id_domain => 1, remote_ip => 1};
......@@ -45,6 +45,7 @@ our %VALID_ARG = (
,swap => 2
,id_iso => 2
,iso_file => 2
,iso_name => 2
,id_base => 2
,id_owner => 1
,id_template => 2
......@@ -60,8 +61,11 @@ our %VALID_ARG = (
,pause_domain => $args_manage
,resume_domain => {%$args_manage, remote_ip => 1 }
,remove_domain => $args_manage
,shutdown_domain => { name => 2, id_domain => 2, uid => 1, timeout => 2, at => 2
,shutdown => {
alias => 'shutdown_domain'
,args => { name => 2, id_domain => 1, uid => 1, timeout => 2, at => 2
, id_vm => 2 }
},
,force_shutdown_domain => { id_domain => 1, uid => 1, at => 2, id_vm => 2 }
,screenshot_domain => { id_domain => 1, filename => 2 }
,domain_autostart => { id_domain => 1 , uid => 1, value => 2 }
......@@ -107,10 +111,10 @@ our %VALID_ARG = (
,list_network_interfaces => { uid => 1, vm_type => 1, type => 2 }
#isos
,list_isos => { vm_type => 1 }
,list_isos => { vm_type => 2 }
,manage_pools => { uid => 2, id_domain => 2 }
,ping_backend => {}
,ping_backend => { uid => 2 }
);
our %CMD_SEND_MESSAGE = map { $_ => 1 }
......@@ -405,8 +409,8 @@ sub _check_args {
confess "Odd number of elements ".Dumper(\@_) if scalar(@_) % 2;
my $args = { @_ };
my $valid_args = $VALID_ARG{$sub};
for (qw(at after_request)) {
my $valid_args = valid_args($sub);
for (qw(at after_request uid)) {
$valid_args->{$_}=2 if !exists $valid_args->{$_};
}
......@@ -416,14 +420,71 @@ sub _check_args {
if !$valid_args->{$_};
}
for (keys %{$VALID_ARG{$sub}}) {
next if $VALID_ARG{$sub}->{$_} == 2; # optional arg
if (exists $args->{machine} && $VALID_ARG{$sub}->{id_domain}) {
my $id_domain = _search_id_domain($args->{machine});
die "Error: provided id_domain=$args->{id_domain} and machine=$args->{machine}. "
." But $args->{machine} has a different id=$id_domain\n"
if exists $args->{id_domain} && $args->{id_domain}
&& $args->{id_domain} ne $id_domain;
die "Error: unknown machine '$args->{machine}'\n" if !defined $id_domain;
$args->{id_domain} = $id_domain;
}
my $valid = valid_args($sub);
for (keys %$valid) {
next if $valid->{$_} == 2; # optional arg
confess "Missing argument $_" if !exists $args->{$_};
}
delete $args->{machine} if exists $args->{machine} && exists $args->{id_domain};
return $args;
}
sub _valid_args_common($command_req) {
my %valid;
my $found;
if ( exists $VALID_ARG{$command_req}) {
%valid = %{$VALID_ARG{$command_req}};
%valid = %{$valid{args}} if exists $valid{args};
$found++;
} else {
for my $command (keys %VALID_ARG) {
if ( exists $VALID_ARG{$command}->{alias}
&& $VALID_ARG{$command}->{alias} eq $command_req ) {
%valid = %{$VALID_ARG{$command}->{args}};
$found++;
last;
}
}
}
confess "Error: Unknown command '$command_req'\n" if !$found;
$valid{at} = 2;
$valid{after_request} = 2;
return \%valid;
}
sub valid_args_cli($command) {
my $valid = _valid_args_common($command);
if (exists $valid->{id_domain}) {
$valid->{machine} = $valid->{id_domain};
delete $valid->{id_domain};
}
return $valid;
}
sub valid_args($command=undef) {
return %VALID_ARG if !defined $command;
my $valid = _valid_args_common($command);
if (exists $valid->{id_domain}) {
$valid->{machine} = 2;
$valid->{id_domain} = 2;
}
return $valid;
}
=head2 force_shutdown_domain
Requests to stop a domain now !
......@@ -468,7 +529,7 @@ sub shutdown_domain {
my $self = {};
bless($self,$class);
return $self->_new_request(command => 'shutdown' , args => $args);
return $self->_new_request(command => 'shutdown_domain' , args => $args);
}
sub new_request($self, $command, @args) {
......@@ -644,6 +705,17 @@ sub _search_domain_name {
return $sth->fetchrow;
}
sub _search_id_domain($machine) {
return $machine if $machine =~ /^\d+$/;
_init_connector();
my $sth = $$CONNECTOR->dbh->prepare("SELECT id FROM domains where name=?");
$sth->execute($machine);
my $id = $sth->fetchrow;
return $id;
}
sub _send_message {
my $self = shift;
my $status = shift;
......
......@@ -368,6 +368,7 @@ sub _around_create_domain {
my $owner = Ravada::Auth::SQL->search_by_id($id_owner) or confess "Unknown user id: $id_owner";
my $base;
my $iso_name = delete $args{iso_name};
my $volatile = delete $args{volatile};
my $id_base = delete $args{id_base};
my $id_iso = delete $args{id_iso};
......@@ -404,6 +405,16 @@ sub _around_create_domain {
$args_create{spice_password} = $self->_define_spice_password($remote_ip);
$self->_pre_create_domain(%args_create);
if ($iso_name) {
my $iso = $self->search_iso_image($iso_name);
die "Error: iso '$iso_name' not found" if !$iso;
confess "Error: requested both id_iso=$id_iso && iso_name=$iso_name (id=$iso->{id})"
if $id_iso && $iso->{id} != $id_iso;
delete $args_create{iso_name};
$args_create{id_iso} = $iso->{id};
}
my $domain = $self->$orig(%args_create, volatile => $volatile);
$domain->add_volume_swap( size => $swap ) if $swap;
......@@ -435,6 +446,15 @@ sub _around_create_domain {
return $domain;
}
sub search_iso_image($self, $name) {
_init_connector();
my $sth = $$CONNECTOR->dbh->prepare("SELECT * FROM iso_images WHERE name like ?");
$sth->execute($name);
my $row = $sth->fetchrow_hashref;
return $row;
}
sub _define_spice_password($self, $remote_ip) {
my $spice_password = Ravada::Utils::random_name(4);
if ($remote_ip) {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment