Commit 7b4c9d8c authored by Francesc Guasch's avatar Francesc Guasch
Browse files

Merge branch 'main' into feature/host_device

parents a9e7df3c b225727c
......@@ -3,7 +3,7 @@ package Ravada;
use warnings;
use strict;
our $VERSION = '1.3.1';
our $VERSION = '1.4.0';
use Carp qw(carp croak cluck);
use Data::Dumper;
......@@ -2008,7 +2008,7 @@ sub _sql_create_tables($self) {
volumes => {
id => 'integer PRIMARY KEY AUTO_INCREMENT',
id_domain => 'integer NOT NULL references `domains` (`id`) ON DELETE CASCADE',
name => 'char(200) NOT NULL',
name => 'char(255) NOT NULL',
file => 'varchar(255) NOT NULL',
n_order => 'integer NOT NULL',
info => 'TEXT',
......
......@@ -2360,6 +2360,12 @@ sub _find_child($controller, $name) {
}
sub _remove_device($self, $index, $device, $attribute_name0=undef, $attribute_value=undef) {
confess "Error: I need index defined or attribute name=value"
if !defined $index && !defined $attribute_name0;
confess "Error: I attribute value to search must be defined"
if defined $attribute_name0 && !defined $attribute_value;
my $doc = XML::LibXML->load_xml(string => $self->xml_description_inactive);
my ($devices) = $doc->findnodes('/domain/devices');
my $ind=0;
......@@ -2372,11 +2378,17 @@ sub _remove_device($self, $index, $device, $attribute_name0=undef, $attribute_va
my $found_value = $item->getAttribute($attr_name);
push @found,($found_value or '');
$found = 1
if defined $found_value && $found_value =~ $attribute_value;
if ( $found_value =~ $attribute_value ) {
$found=1 if !defined $index || $ind == $index;
$ind++;
}
} else {
$found = 1 if defined $index && $ind == $index;
$ind++;
}
if($found || defined $index && $ind++==$index ){
if($found ){
my ($source ) = $controller->findnodes("source");
my $file;
$file = $source->getAttribute('file') if $source;
......@@ -2431,7 +2443,7 @@ sub _remove_controller_disk($self, $index, $attribute_name=undef, $attribute_va
}
sub _remove_controller_network($self, $index) {
$self->_remove_device($index,'interface', type => qr'(bridge|network)');
$self->_remove_device($index,'interface' );
}
=head2 pre_remove
......
......@@ -222,7 +222,13 @@ our %COMMAND = (
,important=> {
limit => 20
,priority => 1
,commands => ['clone','start','start_clones','shutdown_clones','create','open_iptables','list_network_interfaces','list_isos','ping_backend','refresh_machine','open_exposed_ports']
,commands => ['clone','start','start_clones','shutdown_clones','create','open_iptables','list_network_interfaces','list_isos','ping_backend','refresh_machine']
}
,iptables => {
limit => 1
,priority => 2
,commands => ['open_exposed_ports']
}
);
lock_hash %COMMAND;
......@@ -805,7 +811,7 @@ sub _validate_start_domain($self) {
next if !$req;
next if $req->at_time;
next if $command eq 'start' && !$req->after_request();
$self->after_request($req->id);
$self->after_request($req->id) if $req && $req->id < $self->id;
}
}
......@@ -821,7 +827,7 @@ sub _validate_change_hardware($self) {
return if !$id_domain;
my $req = $self->_search_request('%_hardware', id_domain => $id_domain);
$self->after_request($req->id) if $req;
$self->after_request($req->id) if $req && $req->id < $self->id;
}
sub _validate_create_domain($self) {
......@@ -922,7 +928,8 @@ sub _search_request($self,$command,%fields) {
my $args = decode_json($args_json);
my $found=1;
for my $key (keys %fields) {
if ( $args->{$key} ne $fields{$key} ) {
if (!exists $args->{$key} || !defined $args->{$key}
|| $args->{$key} ne $fields{$key} ) {
$found = 0;
last;
}
......
......@@ -335,7 +335,7 @@ ravadaApp.directive("solShowMachine", swMach)
|| $scope.list_machines[i].date_changed != mach.date_changed
){
var show=false;
if (mach._level == 0 ) {
if (mach._level == 0 && !$scope.filter && !$scope.show_active) {
mach.show=true;
}
if ($scope.show_machine[mach.id]) {
......@@ -357,6 +357,7 @@ ravadaApp.directive("solShowMachine", swMach)
}
$scope.n_active=n_active_current;
if ( $scope.show_active ) { $scope.do_show_active() };
if ( $scope.filter) { $scope.show_filter() };
});
}
}
......@@ -591,6 +592,7 @@ ravadaApp.directive("solShowMachine", swMach)
$scope.show_filter = function() {
$scope.hide_clones = true;
$scope.show_active = false;
$scope.n_active_current = 0;
$scope.n_active_hidden = 0;
for (var [key, mach ] of Object.entries($scope.list_machines)) {
......
......@@ -263,7 +263,6 @@
ws.onmessage = function(event) {
var data = JSON.parse(event.data);
if (data === null || typeof(data) == undefined ) {
console.log("close");
ws.close();
window.location.href="/";
return;
......@@ -636,17 +635,13 @@
}
var file = $scope.showmachine.hardware.disk[index].file;
if (typeof(file) != 'undefined' && file) {
$scope.showmachine.requests++;
$http.post('/request/remove_hardware/'
,JSON.stringify({
$scope.request('remove_hardware'
,{
'id_domain': $scope.showmachine.id
,'name': 'disk'
,'option': { 'source/file': file }
})
).then(function(response) {
});
item.remove = false;
return;
}
......@@ -655,10 +650,11 @@
if(typeof(item) == 'object') {
item.remove = false;
}
$http.get('/machine/hardware/remove/'
+$scope.showmachine.id+'/'+hardware+'/'+index).then(function(response) {
});
$scope.request('remove_hardware',{
'id_domain': $scope.showmachine.id
,'name': hardware
,'index': index
});
};
$scope.list_ldap_attributes= function() {
$scope.ldap_entries = 0;
......@@ -1043,7 +1039,6 @@
var already_subscribed_to_domain = false;
$scope.copy_password= function(driver) {
$scope.view_password=1;
console.log("copy-password "+driver);
var copyTextarea = document.querySelector('.js-copytextarea-'+driver);
if (copyTextarea) {
copyTextarea.select();
......@@ -1088,9 +1083,20 @@
already_subscribed_to_domain = true;
$scope.id_domain=data.id_domain;
$scope.subscribe_domain_info(url, data.id_domain);
$scope.open_ports(url, data.id_domain, id_request);
}
}
}
$scope.open_ports = function(url, id_domain, id_request) {
$http.post('/request/open_exposed_ports/'
,JSON.stringify(
{ 'id_domain': id_domain
,'after_request': id_request
})
).then(function(response) {
$scope.request_open_ports = true;
});
}
$scope.subscribe_domain_info= function(url, id_domain) {
already_subscribed_to_domain = true;
var ws = new WebSocket(url);
......@@ -1132,6 +1138,9 @@
redirected_display=true;
}
}
if ($scope.request_open_ports && $scope.domain.ip && $scope.domain.requests == 0) {
$scope.request_open_ports_done = true;
}
}
}
......
......@@ -321,6 +321,84 @@ sub test_add_hardware_custom($domain, $hardware) {
return $exec->($domain);
}
sub _set_three_devices($domain, $hardware) {
my %drivers = map { $_ => 1 } @{$domain->info(user_admin)->{drivers}->{$hardware}};
my $info_hw = $domain->info(user_admin)->{hardware};
my $items = [];
$items = $info_hw->{$hardware};
for my $item (@$items) {
delete $drivers{$item->{driver}} if ref($item);
}
for (1 .. 3-scalar(@$items)) {
my @driver;
if ($hardware eq 'display') {
my ($driver) = keys %drivers;
delete $drivers{$driver};
@driver =( data => { driver => $driver } );
}
Ravada::Request->add_hardware(
uid => user_admin->id
,id_domain => $domain->id
,name => $hardware
,@driver
);
}
wait_request(debug => 0);
}
sub test_remove_hardware_by_index_network_kvm($vm, $hardware) {
return if $hardware ne 'network' || $vm->type ne 'KVM';
my $domain = create_domain($vm);
_set_three_devices($domain, $hardware);
my $info_hw1 = $domain->info(user_admin)->{hardware};
my $items1 = [];
$items1 = $info_hw1->{$hardware};
$domain->_remove_device(1,"interface", type => qr'(bridge|network)');
my $info_hw2 = $domain->info(user_admin)->{hardware};
my $items2 = [];
$items2 = $info_hw2->{$hardware};
is($items2->[0]->{name},$items1->[0]->{name});
is($items2->[1]->{name},$items1->[2]->{name});
remove_domain($domain);
}
sub test_remove_hardware_by_index($vm, $hardware) {
return if $hardware eq 'usb';
my $domain = create_domain($vm);
_set_three_devices($domain, $hardware);
my $info_hw1 = $domain->info(user_admin)->{hardware};
my $items1 = [];
$items1 = $info_hw1->{$hardware};
Ravada::Request->remove_hardware(
uid => user_admin->id
,id_domain => $domain->id
,name => $hardware
,index => 1
);
wait_request();
my $info_hw2 = $domain->info(user_admin)->{hardware};
my $items2 = [];
$items2 = $info_hw2->{$hardware};
if (!ref($items2->[0])) {
is($items2->[0], $items1->[0]);
is($items2->[1], $items1->[2]);
} else {
is($items2->[0]->{name},$items1->[0]->{name});
is($items2->[1]->{name},$items1->[2]->{name});
}
$domain->remove(user_admin);
}
sub test_remove_hardware($vm, $domain, $hardware, $index) {
$domain->shutdown_now(user_admin) if $domain->is_active;
......@@ -783,6 +861,7 @@ ok($rvd_back,"Launch Ravada");# or exit;
ok($Ravada::CONNECTOR,"Expecting conector, got ".($Ravada::CONNECTOR or '<unde>'));
clean();
remove_old_domains();
remove_old_disks();
......@@ -808,6 +887,9 @@ for my $vm_name ( vm_names()) {
for my $hardware ( reverse sort keys %controllers ) {
diag("Testing $hardware controllers for VM $vm_name");
test_remove_hardware_by_index($vm, $hardware);
test_remove_hardware_by_index_network_kvm($vm, $hardware);
test_front_hardware($vm, $domain_b, $hardware);
test_add_hardware_custom($domain_b, $hardware);
......
......@@ -608,6 +608,34 @@ sub test_req_clone_deny($vm, $base_name) {
}
sub test_domain_name_iso($vm) {
my $name = new_domain_name()."-iso";
my $req = Ravada::Request->create_domain(
name => $name
,id_owner => user_admin->id
,vm => $vm->type
,id_iso => search_id_iso('%alpine%')
);
wait_request();
my ($domain) = $vm->search_domain($name);
ok($domain) or return;
$domain->prepare_base(user_admin);
my $name2 = new_domain_name."-iso";
my $req2 = Ravada::Request->clone(
id_domain => $domain->id
,uid => user_admin->id
,name => $name2
);
wait_request();
my ($clone) = $vm->search_domain($name2);
ok($clone) or return;
$clone->remove(user_admin) if $clone;
$domain->remove(user_admin) if $domain;
}
################################################
{
......@@ -645,6 +673,7 @@ for my $vm_name ( vm_names ) {
my $iso = $vm_connected->_search_iso($ID_ISO);
$vm_connected->_iso_name($iso, undef);
}
test_domain_name_iso($vm_connected);
test_swap($vm_name);
my $domain_name = test_req_create_domain_iso($vm_name);
......
......@@ -14,8 +14,7 @@ use feature qw(signatures);
use lib 't/lib';
use Test::Ravada;
no warnings "experimental::signatures";
use feature qw(signatures);
my @MOCK_ISOS;
init();
#############################################################################
......@@ -66,7 +65,6 @@ sub test_frontend_refresh {
}
sub test_remove_disk($vm, %options) {
diag(Dumper(\%options));
my $make_base = delete $options{make_base};
my $clone = delete $options{clone};
my $remove_by_file = ( delete $options{remove_by_file} or 0);
......@@ -80,7 +78,6 @@ sub test_remove_disk($vm, %options) {
if keys %options;
for my $index ( 0 .. 3 ) {
diag("\tindex=$index");
my $name = new_domain_name();
my $req = Ravada::Request->create_domain(
name => $name
......@@ -220,6 +217,10 @@ sub test_add_cd($vm, $data) {
my $n_disks0 = scalar(@{$info0->{hardware}->{disk}});
my %targets0 = map { $_->{target} => 1 } @{$info0->{hardware}->{disk}};
if ($data->{device} eq 'cdrom' && exists $data->{file} && $data->{file} =~ /tmp/) {
open my $out, ">>",$data->{file} or die "$! $data->{file}";
close $out;
}
my $req = Ravada::Request->add_hardware(
id_domain => $domain->id
,name => 'disk'
......@@ -244,6 +245,10 @@ sub test_add_cd($vm, $data) {
is($new_dev->{driver_type}, 'raw');
is($new_dev->{driver}, 'ide');
is($new_dev->{file},$data->{file});
if ($data->{device} eq 'cdrom' && exists $data->{file} && $data->{file} =~ /tmp/) {
unlink $data->{file} or die "$! $data->{file}";
}
}
sub test_add_disk {
......@@ -380,7 +385,7 @@ sub test_add_cd_kvm($vm) {
test_add_cd($vm
, { 'device' => 'cdrom'
,'driver' => 'ide'
,'file' => "/tmp/a.iso"
,'file' => "/tmp/".new_domain_name()."a.iso"
});
}
......@@ -404,8 +409,6 @@ sub _list_id_isos($vm) {
next if $iso->{name} =~ /Empty/;
next if $iso->{name} =~ /Android/i;
die Dumper($iso) if !defined $iso->{id};
$sth->execute($device, $iso->{id});
push @list, ( $iso->{id} );
}
......@@ -485,15 +488,36 @@ sub _req_remove_cd($domain) {
wait_request( debug => 0);
}
sub _create_mock_iso($vm) {
my $file = $vm->dir_img()."/".new_domain_name()."a.iso";
open my $out, ">>",$file or die "$! $file";
print $out "test\n";
close $out;
push @MOCK_ISOS,($file);
return $file;
}
sub remove_mock_isos() {
for my $file (@MOCK_ISOS) {
next if $file !~ m{/tst_};
unlink $file if -e $file;
}
}
sub _req_add_cd($domain) {
my $info = $domain->info(user_admin);
my $disks = $info->{hardware}->{disk};
my $file = _create_mock_iso($domain->_vm);
my $req = Ravada::Request->add_hardware(
uid => Ravada::Utils::user_daemon->id
,id_domain => $domain->id
,name => 'disk'
,data => { type => 'cdrom'
,file => "/var/tmp/a.iso"
,file => $file
}
);
wait_request(debug => 0);
......@@ -557,6 +581,7 @@ sub test_cdrom($vm) {
_req_add_cd($domain);
remove_domain($domain);
remove_mock_isos();
}
}
......@@ -588,10 +613,9 @@ for my $vm_name (vm_names() ) {
test_add_cd_kvm($vm) if $vm_name eq 'KVM';
for my $id_iso ( _list_id_isos($vm) ) {
diag("Testing id iso = ".($id_iso or '<UNDEF>'));
for my $by_file ( 1, 0 ) {
for my $by_index ( 0, 1 ) {
diag("by_file=$by_file, by_index=$by_index");
diag("Testing id_iso: $id_iso , by_file:$by_file, by_index:$by_index");
test_remove_disk($vm
,clone => 1
,id_iso => $id_iso
......
......@@ -6,8 +6,8 @@
</div>
<div class="col-lg-2">
<select name="device" ng-model="add_disk.device">
<option><%=l 'disk' %></option>
<option><%=l 'cdrom' %></option>
<option value="disk" selected><%=l 'disk' %></option>
<option value="cdrom"><%=l 'cdrom' %></option>
</select>
</div>
</div>
......@@ -50,7 +50,7 @@
<input type="text" name="allocation" ng-model="add_disk.allocation">
</div>
</div>
<div class="row" ng-show="add_disk.device == 'disk'">
<div class="row">
<div class="col-lg-2">
<label for="bus"><%=l 'bus' %></label>
</div>
......
......@@ -13,7 +13,6 @@
<div class="jumbotron" ng-cloak="">
<h2><%=l 'Running' %> {{domain.name}}</h2>
<div class="alert alert-warning">
<%=l 'A viewer is required to run the virtual machines.' %>
<a href="/requirements"><%=l 'Read more.' %></a>
......@@ -50,8 +49,14 @@
</ul>
</li>
</ul>
<h3 ng-show="domain.ports.length">Open ports</h3>
<div class="container pl-5">
<h3 ng-show="domain.ports.length">Open ports
<i ng-hide="request_open_ports && request_open_ports_done"
class="fas fa-sync-alt fa-spin"></i>
</h3>
<div class="container pl-5"
ng-show="request_open_ports && request_open_ports_done">
<table border="0">
<tr ng-repeat="port in domain.ports">
<td align="right">
......
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