diff --git a/lib/Ravada.pm b/lib/Ravada.pm index f4b7740b5..7f0e89884 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -5723,8 +5723,11 @@ sub _cmd_shutdown_node($self, $request) { sub _cmd_start_node($self, $request) { my $id_node = $request->args('id_node'); - my $node = Ravada::VM->open($id_node); - $node->start(); + my $node; + eval{ $node = Ravada::VM->open($id_node); + $node->start() if$node; + }; + Ravada::VM::_wake_on_lan($id_node) if !$node; } sub _cmd_connect_node($self, $request) { @@ -6544,7 +6547,10 @@ sub search_vm { ); $sth->execute($type, $host); my ($id) = $sth->fetchrow(); - return Ravada::VM->open($id) if $id; + my $vm; + $vm = Ravada::VM->open($id) if $id; + return if $host eq 'localhost' && $vm && !$vm->vm; + return if $host ne 'localhost'; my $vms = $self->_create_vm($type); diff --git a/lib/Ravada/Request.pm b/lib/Ravada/Request.pm index 40c733d17..67473ed26 100644 --- a/lib/Ravada/Request.pm +++ b/lib/Ravada/Request.pm @@ -253,6 +253,7 @@ our %COMMAND = ( ,priority => 4 ,commands => ['shutdown','shutdown_now', 'enforce_limits', 'set_time' ,'remove_domain', 'remove', 'refresh_machine_ports' + ,'connect_node','start_node','shutdown_node' ] } diff --git a/lib/Ravada/VM.pm b/lib/Ravada/VM.pm index 3ba8bb4ed..b3b995945 100644 --- a/lib/Ravada/VM.pm +++ b/lib/Ravada/VM.pm @@ -234,8 +234,13 @@ sub open { $args{security} = decode_json($row->{security}) if $row->{security}; my $vm = $self->new(%args); - return if !$vm || !$vm->vm; - $VM{$args{id}} = $vm unless $args{readonly}; + + my $internal_vm; + eval { + $internal_vm = $vm->vm; + }; + $VM{$args{id}} = $vm unless $args{readonly} || !$internal_vm; + return if $self->is_local && !$internal_vm; return $vm; } @@ -1283,9 +1288,10 @@ Returns wether this virtual manager is in the local host =cut sub is_local($self) { - return 1 if $self->host eq 'localhost' + return 1 if !$self->host + || $self->host eq 'localhost' || $self->host eq '127.0.0,1' - || !$self->host; + ; return 0; } @@ -1527,7 +1533,8 @@ sub _around_create_network($orig, $self,$data, $id_owner, $request=undef) { my ($found) = grep { $_->{$field} eq $data->{$field} } $self->list_virtual_networks(); - die "Error: network $field=$data->{$field} already exists\n" + die "Error: network $field=$data->{$field} already exists in " + .$self->name."\n" if $found; } @@ -1655,7 +1662,9 @@ sub is_active($self, $force=0) { sub _do_is_active($self, $force=undef) { my $ret = 0; if ( $self->is_local ) { + eval { $ret = 1 if $self->vm; + }; } else { my @ping_args = (); @ping_args = (undef,0) if $force; # no cache @@ -2398,17 +2407,26 @@ sub _store_mac_address($self, $force=0 ) { } } -sub _wake_on_lan( $self ) { - return if $self->is_local; +sub _wake_on_lan( $self=undef ) { + return if $self && ref($self) && $self->is_local; - die "Error: I don't know the MAC address for node ".$self->name - if !$self->_data('mac'); + my ($mac_addr, $id_node); + if (ref($self)) { + $mac_addr = $self->_data('mac'); + $id_node = $self->id; + } else { + $id_node = $self; + my $sth = $$CONNECTOR->dbh->prepare("SELECT mac FROM vms WHERE id=?"); + $sth->execute($id_node); + $mac_addr = $sth->fetchrow(); + } + die "Error: I don't know the MAC address for node $id_node" + if !$mac_addr; my $sock = new IO::Socket::INET(Proto=>'udp', Timeout => 60) or die "Error: I can't create an UDP socket"; my $host = '255.255.255.255'; my $port = 9; - my $mac_addr = $self->_data('mac'); my $ip_addr = inet_aton($host); my $sock_addr = sockaddr_in($port, $ip_addr); diff --git a/lib/Ravada/VM/Void.pm b/lib/Ravada/VM/Void.pm index 243675334..69316ed66 100644 --- a/lib/Ravada/VM/Void.pm +++ b/lib/Ravada/VM/Void.pm @@ -471,7 +471,8 @@ sub create_network($self, $data, $id_owner=undef, $request=undef) { sub remove_network($self, $name) { my $file_out = $self->dir_img."/networks/$name.yml"; - unlink $file_out or die "$! $file_out" if $self->file_exists($file_out); + return if !$self->file_exists($file_out); + $self->remove_file($file_out); } diff --git a/public/js/ravada.js b/public/js/ravada.js index e952955bc..a20256304 100644 --- a/public/js/ravada.js +++ b/public/js/ravada.js @@ -382,6 +382,9 @@ }; $scope.topology_changed = function() { + if (!$scope.showmachine.hardware['cpu']) { + return; + } var cpu = $scope.showmachine.hardware.cpu[0]; var item = cpu.cpu.topology; if(typeof(item) == undefined || !item) { diff --git a/t/lib/Test/Ravada.pm b/t/lib/Test/Ravada.pm index 2692512dc..60b12f38f 100644 --- a/t/lib/Test/Ravada.pm +++ b/t/lib/Test/Ravada.pm @@ -699,7 +699,10 @@ sub _discover() { uid => user_admin->id ,id_vm => $id_vm ); - wait_request(); + for ( 1 .. 10 ) { + wait_request(); + last if $req->status('done'); + } my $out = $req->output; warn $req->error if $req->error; next if !$out; @@ -1415,7 +1418,7 @@ sub wait_request { my $error = ($req->error or ''); next if $error =~ /waiting for processes/i; if ($req->command =~ m{rsync_back|set_base_vm|start}) { - like($error,qr{^($|.*port \d+ already used|rsync done)}) or confess $req->command; + like($error,qr{^($|.*port \d+ already used|.*rsync)}) or confess $req->command; } elsif($req->command eq 'refresh_machine_ports') { like($error,qr{^($|.*is not up|.*has ports down|nc: |Connection)}); $req->status('done'); @@ -1425,7 +1428,7 @@ sub wait_request { like($error,qr{^($|.*compacted)}); } elsif($req->command eq 'refresh_machine') { like($error,qr{^($|.*port.*already used|.*Domain not found)}); - } elsif($req->command eq 'force_shutdown') { + } elsif($req->command =~ /shutdown/) { like($error,qr{^($|.*Unknown domain)}); } elsif($req->command eq 'connect_node') { like($error,qr{^($|Connection OK)}); diff --git a/t/mojo/70_volatile.t b/t/mojo/70_volatile.t index 3322e4c8d..8a42aae35 100644 --- a/t/mojo/70_volatile.t +++ b/t/mojo/70_volatile.t @@ -96,19 +96,22 @@ sub bases($vm_name) { my $iso_name = 'Alpine%'; _download_iso($iso_name); - mojo_check_login($t); - my $name = new_domain_name()."-".$vm_name."-$$"; - $t->post_ok('/new_machine.html' => form => { - backend => $vm_name - ,id_iso => search_id_iso($iso_name) - ,name => $name - ,disk => 1 - ,ram => 1 - ,swap => 1 - ,submit => 1 - } - )->status_is(302); - die $t->tx->res->body if $t->tx->res->code() != 302; + for ( 1 .. 2 ) { + mojo_check_login($t); + my $name = new_domain_name()."-".$vm_name."-$$"; + $t->post_ok('/new_machine.html' => form => { + backend => $vm_name + ,id_iso => search_id_iso($iso_name) + ,name => $name + ,disk => 1 + ,ram => 1 + ,swap => 1 + ,submit => 1 + } + )->status_is(302); + die $t->tx->res->body if $t->tx->res->code() != 302; + push @names,($name); + } } my @bases; for my $name (@names) { @@ -131,15 +134,42 @@ sub bases($vm_name) { return @bases; } +sub _start_nodes() { + my $sth = connector->dbh->prepare("SELECT id,name FROM vms"); + $sth->execute(); + while ( my ($id_vm,$name) = $sth->fetchrow) { + Ravada::Request->start_node( + uid => user_admin->id + ,id_node => $id_vm + ); + my $node_domain = Ravada::Front::Domain->new(name => $name); + next if !$node_domain->is_known(); + Ravada::Request->start_domain(uid => user_admin->id + ,id_domain => $node_domain->id + ); + } + wait_request(); + +} + sub _set_base_vms($vm_name, $id_base, $network) { - my $sth = connector->dbh->prepare("SELECT id FROM vms WHERE vm_type=?"); + my $sth = connector->dbh->prepare("SELECT id,name FROM vms WHERE vm_type=?"); $sth->execute($vm_name); - while ( my ($id_vm) = $sth->fetchrow) { + my $count_nodes=0; + while ( my ($id_vm,$name) = $sth->fetchrow) { + $count_nodes++; mojo_request($t,"start_node" , { id_node => $id_vm }, 0); + my $node_domain = Ravada::Front::Domain->new(name => $name); + next if !$node_domain->is_known(); + Ravada::Request->start_domain(uid => user_admin->id + ,id_domain => $node_domain->id + ); } + die "Error: we need at least 2 $vm_name nodes , $count_nodes found" + if $count_nodes<2; $sth->execute($vm_name); - while ( my ($id_vm) = $sth->fetchrow) { + while ( my ($id_vm, $name) = $sth->fetchrow) { $t->post_ok("/node/enable/$id_vm.json"); my $id_req = mojo_request($t,"set_base_vm", { id_vm => $id_vm, id_domain => $id_base, value => 1 }, 0); @@ -171,7 +201,7 @@ sub _count_nodes($vm_name) { sub _new_network($vm_name,$id_vm) { - my ($req,$net); + my $net; for my $cont ( 140 .. 150 ) { my $req_new = Ravada::Request->new_network( @@ -186,17 +216,7 @@ sub _new_network($vm_name,$id_vm) { $net->{ip_address} =~ s/(\d+\.\d+\.)\d+(.*)/$1$cont$2/; my $name = $net->{name}; - my $user = create_user(); - $req = Ravada::Request->create_network( - uid => user_admin->id - ,id_vm => $id_vm - ,data => $net - ); - wait_request(check_error => 0); - - last if !$req->error; } - die $req->error if $req->error; _create_network_nodes($vm_name, $net); @@ -220,11 +240,18 @@ sub _create_network_nodes($vm_name, $net) { } } -sub test_clone($vm_name, $n=10) { +sub test_clone($vm_name, $n=undef) { + if (!defined $n) { + $n=1; + $n=10 if $ENV{TEST_LONG}; + } my $id_vm = _id_vm($vm_name); my @bases = bases($vm_name); + diag("Testing ".scalar(@bases)." bases in $vm_name"); + return if !scalar(@bases); + my $network = _new_network($vm_name, $id_vm); my $network_name = $network->{name}; @@ -248,17 +275,20 @@ sub test_clone($vm_name, $n=10) { is($base->_data('id_vm'), $id_vm) or die $base->name; } - my $times = 2; + my $times = 1; $times = 20 if $ENV{TEST_LONG}; my $seconds = 0; LOOP: for my $count0 ( 0 .. $times ) { + my $count_created=0; for my $count1 ( 0 .. $n*_count_nodes($vm_name) ) { for my $base ( @bases ) { next if !$base->is_base; next if $base->list_requests > 10; last LOOP if _too_loaded("clone"); + last LOOP if !$ENV{TEST_LONG} && _volatiles_in_nodes($base); + my $user = create_user(new_domain_name(),$$); my $ip = (0+$count0.$count1) % 255; @@ -274,6 +304,7 @@ sub test_clone($vm_name, $n=10) { ,options => { network => $network_name } ); delete_request('set_time','force_shutdown'); + $count_created++; next if $vm_name eq 'Void'; if (_slightly_loaded() ) { wait_request(debug => 1); @@ -303,6 +334,15 @@ sub test_clone($vm_name, $n=10) { } } +sub _volatiles_in_nodes($base) { + my %vms; + for my $clone ( $base->clones ) { + next if !$clone->{is_volatile}; + $vms{$clone->{id_vm}}++; + } + return scalar(keys(%vms)); +} + sub _search_domain_by_name($name) { my $sth = connector->dbh->prepare("SELECT id FROM domains " ." WHERE name=?" @@ -328,7 +368,6 @@ sub _too_loaded($msg="") { close $in; chomp $load; $load =~ s/\s.*//; - diag("$msg $load / $MAX_LOAD"); return $load>$MAX_LOAD; } @@ -396,15 +435,15 @@ sub _init() { } sub _clean_old_known($vm_name) { - my $sth = connector->dbh->prepare("SELECT name FROM domains " + my $sth = connector->dbh->prepare("SELECT name,is_base FROM domains " ." WHERE vm=?" ." AND name like 'tst_%'" - ." AND is_base=0" ); $sth->execute($vm_name); my $base_name = base_domain_name(); - while (my ($name) = $sth->fetchrow) { + while (my ($name, $is_base) = $sth->fetchrow) { + next if $vm_name eq 'KVM' && $is_base; next if $name !~ /^$base_name/; Ravada::Request->remove_domain(uid => user_admin->id ,name => $name @@ -412,7 +451,7 @@ sub _clean_old_known($vm_name) { } } -sub _clean_old_bases($vm_name) { +sub _clean_old_bases($vm_name, $wait=1) { my $sth = connector->dbh->prepare("SELECT name FROM domains " ." WHERE is_base=1 AND (id_base IS NULL or id_base=0)" ." AND name like 'zz-test%'" @@ -437,11 +476,11 @@ sub _clean_old_bases($vm_name) { ); } } - wait_request(); + wait_request() if$wait; } -sub _clean_old($vm_name) { - _clean_old_bases($vm_name); +sub _clean_old($vm_name, $wait=1) { + _clean_old_bases($vm_name, $wait); _clean_old_known($vm_name); _remove_unused_volumes(); } @@ -459,6 +498,7 @@ if (!ping_backend()) { } $Test::Ravada::BACKGROUND=1; +_start_nodes(); remove_networks_req(); $t = Test::Mojo->new($SCRIPT); @@ -472,14 +512,14 @@ _init_mojo_client(); login(); for my $vm_name (@{rvd_front->list_vm_types} ) { - diag("Testing new machine in $vm_name"); + diag("Testing volatile clones in $vm_name"); _clean_old($vm_name); test_clone($vm_name); + _clean_old($vm_name); } -remove_old_domains_req(0); # 0=do not wait for them remove_networks_req(); end();