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 { ...@@ -1294,7 +1294,7 @@ sub _connect_dbh {
sleep 1; sleep 1;
warn "Try $try $@\n"; 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 =head2 display_ip
...@@ -1547,11 +1547,12 @@ sub create_domain { ...@@ -1547,11 +1547,12 @@ sub create_domain {
} }
} }
my $vm_name = delete $args{vm}; my $vm_name = delete $args{vm};
delete $args{uid};
my $start = $args{start}; my $start = $args{start};
my $id_base = $args{id_base}; my $id_base = $args{id_base};
my $id_owner = $args{id_owner} or confess "Error: missing id_owner ".Dumper(\%args); 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; confess "ERROR: Argument vm required" if !$id_base && !$vm_name;
...@@ -2944,14 +2945,14 @@ sub _cmd_change_hardware { ...@@ -2944,14 +2945,14 @@ sub _cmd_change_hardware {
); );
} }
sub _cmd_shutdown { sub _cmd_shutdown_machine {
my $self = shift; my $self = shift;
my $request = shift; my $request = shift;
my $uid = $request->args('uid'); my $uid = $request->args('uid');
my $name = $request->defined_arg('name'); my $name = $request->defined_arg('name');
my $id_domain = $request->defined_arg('id_domain'); 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'); my $id_vm = $request->defined_arg('id_vm');
confess "ERROR: Missing id_domain or name" if !$id_domain && !$name; confess "ERROR: Missing id_domain or name" if !$id_domain && !$name;
...@@ -3199,9 +3200,10 @@ sub _cmd_list_network_interfaces($self, $request) { ...@@ -3199,9 +3200,10 @@ sub _cmd_list_network_interfaces($self, $request) {
} }
sub _cmd_list_isos($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];
my $vm = Ravada::VM->open( type => $vm_type ); $vm = Ravada::VM->open( type => $vm_type ) if $vm_type;
my @isos = sort { "\L$a" cmp "\L$b" } $vm->search_volume_path_re(qr(.*\.iso$)); my @isos = sort { "\L$a" cmp "\L$b" } $vm->search_volume_path_re(qr(.*\.iso$));
$request->output(encode_json(\@isos)); $request->output(encode_json(\@isos));
...@@ -3422,15 +3424,21 @@ sub _req_method { ...@@ -3422,15 +3424,21 @@ sub _req_method {
clone => \&_cmd_clone clone => \&_cmd_clone
,start => \&_cmd_start ,start => \&_cmd_start
,start_domain => \&_cmd_start
,start_clones => \&_cmd_start_clones ,start_clones => \&_cmd_start_clones
,pause => \&_cmd_pause ,pause => \&_cmd_pause
,create => \&_cmd_create ,create => \&_cmd_create
,create_domain => \&_cmd_create
,remove => \&_cmd_remove ,remove => \&_cmd_remove
,remove_domain => \&_cmd_remove
,resume => \&_cmd_resume ,resume => \&_cmd_resume
,dettach => \&_cmd_dettach ,dettach => \&_cmd_dettach
,cleanup => \&_cmd_cleanup ,cleanup => \&_cmd_cleanup
,download => \&_cmd_download ,download => \&_cmd_download
,shutdown => \&_cmd_shutdown ,shutdown => \&_cmd_shutdown_machine
,shutdown_domain => \&_cmd_shutdown_machine
,hybernate => \&_cmd_hybernate ,hybernate => \&_cmd_hybernate
,set_driver => \&_cmd_set_driver ,set_driver => \&_cmd_set_driver
,screenshot => \&_cmd_screenshot ,screenshot => \&_cmd_screenshot
......
...@@ -33,7 +33,7 @@ my $COUNT = 0; ...@@ -33,7 +33,7 @@ my $COUNT = 0;
our %FIELD = map { $_ => 1 } qw(error output); our %FIELD = map { $_ => 1 } qw(error output);
our %FIELD_RO = map { $_ => 1 } qw(id name); 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_prepare = { id_domain => 1 , uid => 1 };
our $args_remove_base = { 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}; our $args_manage_iptables = {uid => 1, id_domain => 1, remote_ip => 1};
...@@ -45,6 +45,7 @@ our %VALID_ARG = ( ...@@ -45,6 +45,7 @@ our %VALID_ARG = (
,swap => 2 ,swap => 2
,id_iso => 2 ,id_iso => 2
,iso_file => 2 ,iso_file => 2
,iso_name => 2
,id_base => 2 ,id_base => 2
,id_owner => 1 ,id_owner => 1
,id_template => 2 ,id_template => 2
...@@ -60,8 +61,11 @@ our %VALID_ARG = ( ...@@ -60,8 +61,11 @@ our %VALID_ARG = (
,pause_domain => $args_manage ,pause_domain => $args_manage
,resume_domain => {%$args_manage, remote_ip => 1 } ,resume_domain => {%$args_manage, remote_ip => 1 }
,remove_domain => $args_manage ,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 } , id_vm => 2 }
},
,force_shutdown_domain => { id_domain => 1, uid => 1, 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 } ,screenshot_domain => { id_domain => 1, filename => 2 }
,domain_autostart => { id_domain => 1 , uid => 1, value => 2 } ,domain_autostart => { id_domain => 1 , uid => 1, value => 2 }
...@@ -107,10 +111,10 @@ our %VALID_ARG = ( ...@@ -107,10 +111,10 @@ our %VALID_ARG = (
,list_network_interfaces => { uid => 1, vm_type => 1, type => 2 } ,list_network_interfaces => { uid => 1, vm_type => 1, type => 2 }
#isos #isos
,list_isos => { vm_type => 1 } ,list_isos => { vm_type => 2 }
,manage_pools => { uid => 2, id_domain => 2 } ,manage_pools => { uid => 2, id_domain => 2 }
,ping_backend => {} ,ping_backend => { uid => 2 }
); );
our %CMD_SEND_MESSAGE = map { $_ => 1 } our %CMD_SEND_MESSAGE = map { $_ => 1 }
...@@ -405,25 +409,82 @@ sub _check_args { ...@@ -405,25 +409,82 @@ sub _check_args {
confess "Odd number of elements ".Dumper(\@_) if scalar(@_) % 2; confess "Odd number of elements ".Dumper(\@_) if scalar(@_) % 2;
my $args = { @_ }; my $args = { @_ };
my $valid_args = $VALID_ARG{$sub}; my $valid_args = valid_args($sub);
for (qw(at after_request)) { for (qw(at after_request uid)) {
$valid_args->{$_}=2 if !exists $valid_args->{$_}; $valid_args->{$_}=2 if !exists $valid_args->{$_};
} }
confess "Unknown method $sub" if !$valid_args; confess "Unknown method $sub" if !$valid_args;
for (keys %{$args}) { for (keys %{$args}) {
confess "Invalid argument $_ , valid args ".Dumper($valid_args) confess "Invalid argument $_ , valid args ".Dumper($valid_args)
if !$valid_args->{$_}; if !$valid_args->{$_};
} }
for (keys %{$VALID_ARG{$sub}}) { if (exists $args->{machine} && $VALID_ARG{$sub}->{id_domain}) {
next if $VALID_ARG{$sub}->{$_} == 2; # optional arg 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->{$_}; confess "Missing argument $_" if !exists $args->{$_};
} }
delete $args->{machine} if exists $args->{machine} && exists $args->{id_domain};
return $args; 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 =head2 force_shutdown_domain
Requests to stop a domain now ! Requests to stop a domain now !
...@@ -468,7 +529,7 @@ sub shutdown_domain { ...@@ -468,7 +529,7 @@ sub shutdown_domain {
my $self = {}; my $self = {};
bless($self,$class); 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) { sub new_request($self, $command, @args) {
...@@ -644,6 +705,17 @@ sub _search_domain_name { ...@@ -644,6 +705,17 @@ sub _search_domain_name {
return $sth->fetchrow; 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 { sub _send_message {
my $self = shift; my $self = shift;
my $status = shift; my $status = shift;
......
...@@ -368,6 +368,7 @@ sub _around_create_domain { ...@@ -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 $owner = Ravada::Auth::SQL->search_by_id($id_owner) or confess "Unknown user id: $id_owner";
my $base; my $base;
my $iso_name = delete $args{iso_name};
my $volatile = delete $args{volatile}; my $volatile = delete $args{volatile};
my $id_base = delete $args{id_base}; my $id_base = delete $args{id_base};
my $id_iso = delete $args{id_iso}; my $id_iso = delete $args{id_iso};
...@@ -404,6 +405,16 @@ sub _around_create_domain { ...@@ -404,6 +405,16 @@ sub _around_create_domain {
$args_create{spice_password} = $self->_define_spice_password($remote_ip); $args_create{spice_password} = $self->_define_spice_password($remote_ip);
$self->_pre_create_domain(%args_create); $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); my $domain = $self->$orig(%args_create, volatile => $volatile);
$domain->add_volume_swap( size => $swap ) if $swap; $domain->add_volume_swap( size => $swap ) if $swap;
...@@ -435,6 +446,15 @@ sub _around_create_domain { ...@@ -435,6 +446,15 @@ sub _around_create_domain {
return $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) { sub _define_spice_password($self, $remote_ip) {
my $spice_password = Ravada::Utils::random_name(4); my $spice_password = Ravada::Utils::random_name(4);
if ($remote_ip) { 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