Commit 137031ac authored by Francesc Guasch's avatar Francesc Guasch
Browse files

Merge branch 'main' into release/1.3.1

parents bdcf21cc d44cc6ea
......@@ -61,4 +61,4 @@ jobs:
- name: Test mock VM
run: prove -lr t/vm/60_new_args.t t/30_request.t
- name: Test create from ISO
run: prove -lr t/request/25_create_from_iso.t
run: prove -lr t/request/25_create_from_iso.t t/vm/d10_not_download.t
......@@ -706,7 +706,10 @@ sub _update_isos {
,xml => 'windows_10.xml'
,xml_volume => 'windows10-volume.xml'
,min_disk_size => '21'
,min_swap_size => '2'
,arch => 'x86_64'
,extra_iso => 'https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/archive-virtio/virtio-win-0.1.215-\d+/virtio-win-0.1.\d+.iso'
}
,windows_xp => {
name => 'Windows XP'
......@@ -1386,7 +1389,7 @@ sub _add_indexes_generic($self) {
$self->_clean_index_conflicts($table, $name);
print "+" if $FIRST_TIME_RUN;
if ($table eq 'domain_displays' && $name eq 'id_vm_port') {
if ($table eq 'domain_displays' && $name =~ /port/) {
my $sth_clean=$CONNECTOR->dbh->prepare(
"UPDATE domain_displays set port=NULL"
);
......@@ -2292,9 +2295,11 @@ sub _upgrade_tables {
$self->_upgrade_table('iso_images','file_re','char(64)');
$self->_upgrade_table('iso_images','device','varchar(255)');
$self->_upgrade_table('iso_images','min_disk_size','int (11) DEFAULT NULL');
$self->_upgrade_table('iso_images','min_swap_size','int (11) DEFAULT NULL');
$self->_upgrade_table('iso_images','options','varchar(255)');
$self->_upgrade_table('iso_images','has_cd','int (1) DEFAULT "1"');
$self->_upgrade_table('iso_images','downloading','int (1) DEFAULT "0"');
$self->_upgrade_table('iso_images','extra_iso','varchar(255)');
$self->_upgrade_table('users','language','char(40) DEFAULT NULL');
if ( $self->_upgrade_table('users','is_external','int(11) DEFAULT 0')) {
......@@ -2722,7 +2727,7 @@ sub create_domain {
}
my $vm_name = delete $args{vm};
my $start = $args{start};
my $start = delete $args{start};
my $id_base = $args{id_base};
my $data = delete $args{data};
my $id_owner = $args{id_owner} or confess "Error: missing id_owner ".Dumper(\%args);
......@@ -2746,6 +2751,10 @@ sub create_domain {
$request->status("creating machine") if $request;
unlock_hash(%args);
my $swap = delete $args{swap};
lock_hash(%args);
my $domain;
eval { $domain = $vm->create_domain(%args)};
......@@ -2758,29 +2767,92 @@ sub create_domain {
} elsif ($error) {
die $error;
}
if (!$error && $start) {
$request->status("starting") if $request;
eval {
my $remote_ip;
$remote_ip = $request->defined_arg('remote_ip') if $request;
$domain->start(
user => $user
,remote_ip => $remote_ip
)
};
my $error = $@;
die $error if $error && !$request;
$request->error($error) if $error;
return if !$domain;
my $req_add_swap;
if ($swap) {
$req_add_swap = Ravada::Request->add_hardware(
uid => $args{id_owner}
,id_domain => $domain->id
,name => 'disk'
,data => { size => $swap, type => 'swap' }
);
}
Ravada::Request->add_hardware(
uid => $args{id_owner}
,id_domain => $domain->id
,name => 'disk'
,data => { size => $data, type => 'data' }
) if $domain && $data;
my $req_add_data;
if ($data) {
$req_add_data = Ravada::Request->add_hardware(
uid => $args{id_owner}
,id_domain => $domain->id
,name => 'disk'
,data => { size => $data, type => 'data' }
);
$req_add_data->after_request($req_add_swap->id)
if $req_add_swap;
}
my $previous_req = ( $req_add_data or $req_add_swap );
$previous_req = _add_extra_iso($domain, $request,$previous_req);
_start_domain_after_create($domain, $request, $previous_req) if $start;
return $domain;
}
sub _start_domain_after_create($domain, $request, $previous_request) {
my $remote_ip;
$remote_ip = $request->defined_arg('remote_ip') if $request;
my $uid = $request->defined_arg('id_owner');
$uid = $request->defined_arg('uid') if !$uid;
my $req = Ravada::Request->start_domain(
uid => $uid
,id_domain => $domain->id
,remote_ip => $remote_ip
);
$req->after_request($previous_request->id) if $previous_request;
}
sub _search_iso($id) {
my $sth = $CONNECTOR->dbh->prepare(
"SELECT * FROM iso_images"
." WHERE id = ? "
);
$sth->execute($id);
my $row = $sth->fetchrow_hashref;
return $row;
}
sub _add_extra_iso($domain, $request, $previous_request) {
return if !$request;
my $id_iso = $request->defined_arg('id_iso');
return if !$id_iso;
my $iso = _search_iso($id_iso);
my $extra_iso = $iso->{extra_iso};
return if !$extra_iso;
my ($url, $file_re) = $extra_iso =~ m{(.*)/(.*)};
my $volume = $domain->_vm->search_volume_path_re(qr($file_re));
if (!$volume) {
my ($url) = $domain->_vm->_search_url_file($extra_iso);
my ($device) = $url =~ m{.*/(.*)};
$volume = $domain->_vm->dir_img()."/$device";
$domain->_vm->_download_file_external($url, $volume) ;
}
my $req = Ravada::Request->refresh_storage(id_vm => $domain->_vm->id);
$req->after_request($previous_request->id) if $previous_request;
return Ravada::Request->add_hardware(
name => 'disk'
,uid => Ravada::Utils::user_daemon->id
,id_domain => $domain->id
,data => {
file => $volume
,device => 'cdrom'
}
,after_request => $req->id
);
}
sub _check_args($args,@) {
my %args_check = %$args;
for my $field (@_) {
......@@ -2831,7 +2903,7 @@ sub remove_domain {
eval { $domain = Ravada::Domain->open(id => $id, _force => 1, id_vm => $vm->id) };
warn $@ if $@;
if (!$domain) {
warn "Warning: I can't find domain '$id', maybe already removed.";
warn "Warning: I can't find domain '$id', maybe already removed.\n";
Ravada::Domain::_remove_domain_data_db($id);
return;
};
......@@ -3812,8 +3884,8 @@ sub _cmd_create{
my $msg = '';
if ($domain) {
$msg = 'Domain '
."<a href=\"/machine/view/".$domain->id.".html\">"
$msg = 'Machine'
." <a href=\"/machine/manage/".$domain->id.".html\">"
.$request->args('name')."</a>"
." created."
;
......@@ -4093,8 +4165,8 @@ sub _cmd_start {
uid => Ravada::Utils::user_daemon->id
) if $domain->is_pool && $request->defined_arg('remote_ip');
my $msg = 'Domain '
."<a href=\"/machine/view/".$domain->id.".html\">"
my $msg = 'Machine'
." <a href=\"/machine/view/".$domain->id.".html\">"
.$domain->name."</a>"
." started"
;
......
......@@ -144,7 +144,7 @@ sub unshown_messages {
my $count = shift;
$count = 50 if !defined $count;
my $sth = $$CONNECTOR->dbh->prepare("SELECT id, subject, message FROM messages "
my $sth = $$CONNECTOR->dbh->prepare("SELECT id, subject, message, id_request FROM messages "
." WHERE id_user=? AND date_shown IS NULL"
." ORDER BY date_send DESC "
." LIMIT ?,?");
......
......@@ -528,6 +528,7 @@ sub add_volume {
}
sub _create_volume($self, $file, $format, $data=undef) {
confess "Undefined format" if !defined $format;
if ($format =~ /iso|raw|void/) {
$data->{format} = $format;
$self->_vm->write_file($file, Dump($data)),
......
......@@ -151,6 +151,7 @@ our %CMD_SEND_MESSAGE = map { $_ => 1 }
expose remove_expose
rebase rebase_volumes
shutdown_node reboot_node start_node
start_domain
);
our %CMD_NO_DUPLICATE = map { $_ => 1 }
......@@ -216,6 +217,11 @@ our %CMD_VALIDATE = (
clone => \&_validate_clone
,create => \&_validate_create_domain
,create_domain => \&_validate_create_domain
,start_domain => \&_validate_start_domain
,start => \&_validate_start_domain
,add_hardware=> \&_validate_change_hardware
,change_hardware=> \&_validate_change_hardware
,remove_hardware=> \&_validate_change_hardware
);
sub _init_connector {
......@@ -746,6 +752,35 @@ sub _validate($self) {
$method->($self);
}
sub _validate_start_domain($self) {
my $id_domain = $self->defined_arg('id_domain');
if (!$id_domain) {
my $domain_name = $self->defined_arg('name');
$id_domain = _search_domain_id(undef,$domain_name);
}
return if !$id_domain;
my $req = $self->_search_request('%_hardware', id_domain => $id_domain);
$self->after_request($req->id) if $req;
}
sub _validate_change_hardware($self) {
return if $self->after_request();
my $id_domain = $self->defined_arg('id_domain');
if (!$id_domain) {
my $domain_name = $self->defined_arg('name');
$id_domain = _search_domain_id(undef,$domain_name);
}
return if !$id_domain;
my $req = $self->_search_request('%_hardware', id_domain => $id_domain);
$self->after_request($req->id) if $req;
}
sub _validate_create_domain($self) {
my $base;
......@@ -770,17 +805,27 @@ sub _check_downloading($self) {
my $id_iso = $self->defined_arg('id_iso');
my $iso_file = $self->defined_arg('iso_file');
$iso_file = '' if $iso_file && $iso_file eq '<NONE>';
return if !$id_iso && !$iso_file;
my $sth = $$CONNECTOR->dbh->prepare(
"SELECT id,downloading,device,has_cd "
"SELECT id,downloading,device,has_cd,name,url "
." FROM iso_images "
." WHERE (id=? or device=?) "
);
$sth->execute($id_iso,$iso_file);
my ($id_iso2,$downloading, $device, $has_cd) = $sth->fetchrow;
my ($id_iso2,$downloading, $device, $has_cd, $iso_name, $iso_url)
= $sth->fetchrow;
return if !$downloading && $device;
my $req_download = $self->_search_request('download', id_iso => $id_iso2);
return $self->_status_error("done"
,"Error: ISO file required for $iso_name")
if $has_cd && !$device && !$iso_file && !$iso_url && !$device;
my $req_download = _search_request('download', id_iso => $id_iso2);
if ($has_cd && !$device && !$iso_file && !$req_download) {
$req_download = Ravada::Request->download(
id_iso => $id_iso2
......@@ -818,13 +863,16 @@ sub _mark_iso_downloaded($id_iso) {
$sth->execute($id_iso);
}
sub _search_request($command,%fields) {
my $sth= $$CONNECTOR->dbh->prepare(
"SELECT id, args FROM requests WHERE command = ?"
." AND status <> 'done' "
);
sub _search_request($self,$command,%fields) {
my $query =
"SELECT id, args FROM requests WHERE command like ?"
." AND status <> 'done' ";
$query .= "AND id <> ".$self->id if $self;
$query .= " ORDER BY date_req,id DESC ";
my $sth= $$CONNECTOR->dbh->prepare($query);
$sth->execute($command);
my @reqs;
while ( my ($id, $args_json) = $sth->fetchrow ) {
return Ravada::Request->open($id) if !keys %fields;
......@@ -836,9 +884,12 @@ sub _search_request($command,%fields) {
last;
}
}
return Ravada::Request->open($id) if $found;
next if !$found;
my $req = Ravada::Request->open($id);
return $req if !wantarray;
push @reqs,($req);
}
return @reqs;
}
sub _validate_clone($self
......@@ -1554,8 +1605,10 @@ sub requirements_done($self) {
my $ok = 0;
if ($after_request) {
$ok = 0;
my $req = Ravada::Request->open($self->after_request);
$ok = 1 if $req->status eq 'done';
my $req;
eval { $req = Ravada::Request->open($self->after_request) };
die $@ if $@ && $@!~ /I can't find|not found/i;
$ok = 1 if !$req || $req->status eq 'done';
}
if ($after_request_ok) {
$ok = 0;
......
......@@ -458,6 +458,23 @@ sub _iso_name($self, $iso, $request=undef, $verbose=0) {
return $name;
}
sub _search_url_file($self, $url) {
my ($url0,$file) = $url =~ m{(.*)/(.*)};
confess "Undefined file in url=$url" if !$file;
my $file0 = $file;
$file =~ s/(.*)\.\*(.*)/$1$2/;
$file =~ s/(.*)\.\+(.*)/$1.$2/;
$file =~ s/(.*)\[\\d.*?\]\+(.*)/${1}1$2/;
$file =~ s/(.*)\\d\+(.*)/${1}1$2/;
confess Dumper($url, $file0,$file) if $file =~ m{[*+\\]}
|| $file !~ /\.iso$/;
return "$url0/$file";
}
sub _download_file_external($self, $url, $device) {
}
#########################################################################3
1;
......@@ -67,20 +67,10 @@ sub _list_alerts($rvd, $args) {
my $ret_old = $args->{ret};
my @ret = map { $_->{time} = time; $_ } $user->unshown_messages();
my @ret2=();
my %new;
for my $alert (@ret) {
my $cmd_machine = $alert->{subject};
$cmd_machine =~ s{(.*?\s.*?)\s+.*}{$1};
$new{$cmd_machine}++;
}
my @ret2;
for my $alert (@$ret_old) {
my $cmd_machine = $alert->{subject};
$cmd_machine =~ s{(.*?\s.*?)\s+.*}{$1};
push @ret2,($alert) if time - $alert->{time} < 10
&& $cmd_machine && !$new{$cmd_machine};
&& ! grep { $_->{id_request} == $alert->{id_request} } @ret;
}
return [@ret2,@ret];
......@@ -222,7 +212,7 @@ sub _get_machine_info($rvd, $args) {
my ($id_domain) = $args->{channel} =~ m{/(\d+)};
my $domain = $rvd->search_domain_by_id($id_domain) or do {
warn "Error: domain $id_domain not found.";
return {};
return;
};
......@@ -459,6 +449,7 @@ sub _send_answer($self, $ws_client, $channel, $key = $ws_client) {
$self->clients->{$key}->{ret} = $ret;
}
$self->unsubscribe($key) if $channel eq 'ping_backend' && $ret eq 2;
$self->unsubscribe($key) if !$ret;
}
sub subscribe($self, %args) {
......
......@@ -169,6 +169,10 @@ ravadaApp.directive("solShowMachine", swMach)
$scope.showMinSize = false;
$scope.min_size = 1;
}
if ( $scope.swap.value < iso.min_swap_size ) {
$scope.swap.value = iso.min_swap_size + 0.1;
console.log($scope.swap.value);
}
return (iso.device != null) ? iso.device : "";
};
......@@ -416,6 +420,7 @@ ravadaApp.directive("solShowMachine", swMach)
$scope.auto_hide_clones = false;
$scope.show_active = false;
$scope.hide_clones = !value;
$scope.filter = '';
for (var i in $scope.list_machines){
mach = $scope.list_machines[i];
if (mach._level == 0 ) {
......@@ -552,6 +557,7 @@ ravadaApp.directive("solShowMachine", swMach)
}
};
$scope.do_show_active = function() {
$scope.filter = '';
$scope.show_active=true;
$scope.hide_clones = true;
$scope.n_active_hidden = 0;
......@@ -584,6 +590,33 @@ ravadaApp.directive("solShowMachine", swMach)
}
};
$scope.show_filter = function() {
$scope.hide_clones = true;
$scope.n_active_current = 0;
$scope.n_active_hidden = 0;
for (var [key, mach ] of Object.entries($scope.list_machines)) {
if ($scope.filter.length > 0) {
if ( mach.name.indexOf($scope.filter)>= 0) {
mach.show = true;
} else {
mach.show = false;
}
} else {
if (mach._level > 0 ) {
mach.show = false;
} else {
mach.show = true;
}
}
if (mach.status == 'active') {
$scope.n_active_current++;
if (!mach.show) {
$scope.n_active_hidden++;
}
}
}
};
//On load code
$scope.modalOpened=false;
$scope.rename= {new_name: 'new_name'};
......
......@@ -262,6 +262,12 @@
ws.onopen = function(event) { ws.send('machine_info/'+$scope.showmachineId) };
ws.onmessage = function(event) {
var data = JSON.parse(event.data);
if (data === null || typeof(data) == undefined ) {
console.log("close");
ws.close();
$scope.domain_removed = true;
return;
}
$scope.$apply(function () {
$scope.showmachine = data;
$scope.copy_is_volatile = $scope.showmachine.is_volatile;
......
......@@ -2527,6 +2527,7 @@ sub req_new_domain {
my $machine = ($c->param('machine') or '');
$machine =~ s/^string://;
$machine = '' if $machine eq '?';
my $start = $c->param('start');
my $data = ($c->param('data') or 0);
$data *= 1024*1024*1024;
......@@ -2534,6 +2535,7 @@ sub req_new_domain {
if (!$c->param('_advanced_options')) {
$swap = int(1024 * 1024 * 1024) if !$swap;
$data = int(1024 * 1024 * 1024) if !$data;
$start = 1 if !defined $start;
}
my %args = (
......@@ -2544,6 +2546,8 @@ sub req_new_domain {
,id_owner => $USER->id
,swap => $swap
,data => $data
,start => $start
,remote_ip => _remote_ip($c)
);
$args{iso_file} = $c->param('iso_file') if defined $c->param('iso_file');
$args{options}->{uefi} = 1 if $bios && $bios eq 'UEFI';
......
......@@ -119,10 +119,7 @@ sub test_req_create_domain_iso {
ok($req->status eq 'requested'
,"$$ Status of request is ".$req->status." it should be requested");
$ravada->process_requests();
$ravada->_wait_pids();
wait_request($req);
wait_request(request => $req, debug => 1);
ok($req->status eq 'done'
,"Status of request is ".$req->status." it should be done") or return ;
......@@ -219,7 +216,7 @@ sub test_unread_messages {
my @messages = $user->unread_messages();
ok(scalar @messages == $n_unread,"$test: Expecting $n_unread unread messages , got "
.scalar@messages." ".Dumper(\@messages));
.scalar@messages." ".Dumper(\@messages)) or confess;
$user->mark_all_messages_read();
}
......
......@@ -76,13 +76,9 @@ sub test_new_domain_req {
,id_base => $base->id
,id_owner => user_admin->id
,remote_ip => '127.0.0.1'
,start => 1
);
ok($req) or return;
rvd_back->_process_requests_dont_fork();
ok($req->status eq 'done');
like($req->error,qr(.));
wait_request($req);
my $domain = rvd_back->search_domain($name);
ok($domain) or return;
......
......@@ -310,6 +310,8 @@ sub create_domain_v2(%args) {
,name => 'disk'
,data => { size => $data*1024*1024, type => 'data' }
) if $domain && $data;
delete_request( 'enforce_limits', 'set_time' );
wait_request(debug => 1);
return $domain;
}
......@@ -1019,7 +1021,6 @@ sub create_ldap_user($name, $password, $keep=0) {
for my $field (qw(cn uid)) {
if (my $user = Ravada::Auth::LDAP::search_user(field => $field, name => $name) ) {
return if $keep;
diag("Removing ".$user->dn);
my $mesg;
for ( 1 .. 2 ) {
$mesg = $user->delete()->update($ldap);
......@@ -1176,7 +1177,7 @@ sub wait_request {
$done{$req->{id}}++;
if ($check_error) {
if ($req->command =~ /remove/) {
like($req->error,qr(^$|Unknown domain));
like($req->error,qr(^$|Unknown domain|Domain not found));
} elsif($req->command eq 'set_time') {
like($req->error,qr(^$|libvirt error code));
} else {
......@@ -1188,7 +1189,7 @@ sub wait_request {
like($error,qr{^($|.*is not up|.*has ports down|nc: |Connection)});
$req->status('done');
} elsif($req->command eq 'open_exposed_ports') {
like($error,qr{^($|.*No ip in domain|.*duplicated port)});
like($error,qr{^($|.*No ip in domain|.*duplicated port|I can't get the internal IP)});
} elsif($req->command eq 'compact') {
like($error,qr{^($|.*compacted)});
} else {
......
......@@ -830,7 +830,7 @@ for my $vm_name ( @{rvd_front->list_vm_types} ) {
push @bases,($base0->name);
test_admin_can_do_anything($t, $base0);
my $base2 =test_create_base($t, $vm_name, new_domain_name()."-$vm_name");
my $base2 =test_create_base($t, $vm_name, new_domain_name()."-$vm_name-$$");
push @bases,($base2->name);
mojo_request($t, "add_hardware", { id_domain => $base0->id, name => 'network' });
......@@ -863,6 +863,7 @@ for my $vm_name ( @{rvd_front->list_vm_types} ) {
test_login_non_admin($t, $base1, $base2);
delete_request('set_time','screenshot','refresh_machine_ports');
remove_machines(reverse @bases);
remove_old_domains_req(0); # 0=do not wait for them
}
ok(@bases,"Expecting some machines created");
delete_request('set_time','screenshot','refresh_machine_ports');
......
......@@ -30,7 +30,7 @@ sub test_create($vm, $with_iso_file=1, $no_cd=0) {
my $req = Ravada::Request->create_domain(@args);
unless ($no_cd) {