From 20257ce077535f40b9254a18f8bbc04adb4398e0 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 20 Nov 2023 13:40:11 +0100 Subject: [PATCH 01/50] wip: node host devices --- lib/Ravada/Domain.pm | 8 ++ lib/Ravada/HostDevice.pm | 23 +++++- lib/Ravada/VM.pm | 11 ++- t/device/50_nodes.t | 162 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 200 insertions(+), 4 deletions(-) create mode 100644 t/device/50_nodes.t diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index eb3f35758..fbbf62238 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -5178,6 +5178,7 @@ sub set_base_vm($self, %args) { die $err; } $self->_set_clones_autostart(0); + $self->_set_base_vm_hd($vm); } else { $self->_set_vm($vm,1); # force set vm on domain $self->_do_remove_base($user); @@ -5191,6 +5192,13 @@ sub set_base_vm($self, %args) { return $self->_set_base_vm_db($vm->id, $value); } +sub _set_base_vm_hd($self, $node) { + my @node_hds = $node->list_host_devices(); + for my $hd ($self->list_host_devices) { + die Dumper($hd); + } +} + sub _check_all_parents_in_node($self, $vm) { my @bases; my $base = $self; diff --git a/lib/Ravada/HostDevice.pm b/lib/Ravada/HostDevice.pm index 6b4c87bab..2b5246ad9 100644 --- a/lib/Ravada/HostDevice.pm +++ b/lib/Ravada/HostDevice.pm @@ -83,8 +83,29 @@ sub search_by_id($self, $id) { return Ravada::HostDevice->new(%$row); } -sub list_devices($self) { +sub list_devices_nodes($self) { my $vm = Ravada::VM->open($self->id_vm); + my $sth = $$CONNECTOR->dbh->prepare( + "SELECT id FROM vms WHERE id <> ? AND vm_type=?"); + $sth->execute($vm->id, $vm->type); + + my @nodes = ($vm); + + while ( my ($id) = $sth->fetchrow) { + my $node = Ravada::VM->open($id); + push @nodes,($node); + } + + my @devices; + + for my $node (@nodes) { + push @devices, $self->list_devices($node); + } + + return @devices; +} + +sub list_devices($self, $vm = Ravada::VM->open($self->id_vm)) { die "Error: No list_command in host_device ".$self->id_vm if !$self->list_command; diff --git a/lib/Ravada/VM.pm b/lib/Ravada/VM.pm index 8e44ec4a0..a54f1de76 100644 --- a/lib/Ravada/VM.pm +++ b/lib/Ravada/VM.pm @@ -362,8 +362,8 @@ sub _connect_ssh($self) { confess "Don't connect to local ssh" if $self->is_local; - if ( $self->readonly || $> ) { - confess $self->name." readonly or not root, don't do ssh"; + if ( $self->readonly ) { + confess $self->name." readonly, don't do ssh"; return; } @@ -1626,7 +1626,12 @@ sub write_file( $self, $file, $contents ) { return $self->_write_file_local($file, $contents ) if $self->is_local; my $ssh = $self->_ssh or confess "Error: no ssh connection"; - my ($rin, $pid) = $self->_ssh->pipe_in("cat > $file") + unless ( $file =~ /^[a-z0-9 \/_:\-\.]+$/i ) { + my $file2=$file; + $file2 =~ tr/[a-z0-9 \/_:\-\.]/*/c; + die "Error: insecure character in '$file': '$file2'"; + } + my ($rin, $pid) = $self->_ssh->pipe_in("cat > '$file'") or die "pipe_in method failed ".$self->_ssh->error; print $rin $contents; diff --git a/t/device/50_nodes.t b/t/device/50_nodes.t new file mode 100644 index 000000000..12633b2ca --- /dev/null +++ b/t/device/50_nodes.t @@ -0,0 +1,162 @@ +use warnings; +use strict; + +use Carp qw(confess); +use Data::Dumper; +use File::Path qw(make_path); +use IPC::Run3 qw(run3); +use Mojo::JSON qw(decode_json encode_json); +use Ravada::HostDevice::Templates; +use Test::More; +use YAML qw( Dump ); + +use lib 't/lib'; +use Test::Ravada; + +no warnings "experimental::signatures"; +use feature qw(signatures); + +my $N_DEVICE = 0; + +my $PATH = "/var/tmp/$run_command("mkdir","-p",$PATH) if !$vm->file_exists($PATH); + + my $name = base_domain_name()."_${type} ID"; + + for my $n ( 1 .. $n_devices ) { + my $file= "$PATH/${name} $N_DEVICE$value${n} Foobar " + .$vm->name; + diag($file); + $vm->write_file($file,"fff6f017-3417-4ad3-b05e-17ae3e1a461".int(rand(10))); + } + $N_DEVICE ++; + + return ("find $PATH/",$name); +} + +sub test_devices($vm, $node) { + my ($list_command,$list_filter) = _create_mock_devices($vm, 3 , "USB" ); + my ($list_command2,$list_filter2) = _create_mock_devices($node, 3 , "USB" ); + + my $templates = Ravada::HostDevice::Templates::list_templates($vm->type); + my ($first) = $templates->[0]; + + $vm->add_host_device(template => $first->{name}); + my @list_hostdev = $vm->list_host_devices(); + my ($hd) = $list_hostdev[-1]; + $hd->_data('list_command',$list_command); + + my $vm_name = $vm->name; + my $node_name = $node->name; + + my @devices = $hd->list_devices; + ok(grep /$vm_name/,@devices); + ok(!grep /$node_name/,@devices); + + my @devices_nodes = $hd->list_devices_nodes; + ok(grep /$vm_name/,@devices_nodes); + ok(grep /$node_name/,@devices_nodes); + + test_assign($vm, $node, $hd); + + exit; +} + +sub test_assign($vm, $node, $hd) { + my $base = create_domain($vm); + $base->add_host_device($hd); + $base->prepare_base(user_admin); + $base->set_base_vm(id_vm => $node->id, user => user_admin); + is($node->list_host_devices,$vm->list_host_devices) or exit; + + my $req = Ravada::Request->clone( + uid => user_admin->id + ,id_domain => $base->id + ,number => scalar($hd->list_devices_nodes) + ); + wait_request(); + is(scalar($base->clones),scalar($hd->list_devices_nodes)); + for my $clone0 ($base->clones) { + my $req = Ravada::Request->start_domain( + uid => user_admin->id + ,id_domain => $clone0->{id} + ); + wait_request(); + my $domain = Ravada::Domain->open($clone0->{id}); + is($domain->is_active,1); + check_host_device($domain); + } +} + +sub check_host_device($domain) { + if ($domain->type eq 'Void') { + check_host_device_void($domain); + } else { + die "TODO"; + } +} + +sub check_host_device_void($domain) { + my $doc = $domain->_load(); + my @hostdev; + for my $dev ( @{ $doc->{hardware}->{host_devices} } ) { + push @hostdev,($dev); + for my $item ( keys %$dev ) { + like($item,qr/^\w+$/); + like($dev->{$item}, qr(^[0-9a-z]+$)) or die Dumper($dev); + } + } + + is(scalar(@hostdev),1) or do { + my $vm = Ravada::VM->open($domain->_data('id_vm')); + die $domain->name." ".$vm->name; + }; + warn Dumper([$domain->name,$domain->_data('id_vm'),\@hostdev]); +} + +sub _clean_devices(@nodes) { + my $base = base_domain_name(); + for my $vm (@nodes) { + next if !defined $vm; + $vm->run_command("mkdir","-p",$PATH) if !$vm->file_exists($PATH); + my ($out, $err) = $vm->run_command("ls",$PATH); + for my $line ( split /\n/,$out ) { + next if $line !~ /$base/; + $vm->run_command("rm","$PATH/$line"); + } + } +} +######################################################### + +init(); +clean(); + +for my $vm_name ( 'Void' ) { + my $vm; + eval { $vm = rvd_back->search_vm($vm_name) }; + + SKIP: { + + my $msg = "SKIPPED: $vm_name virtual manager not found ".($@ or ''); + + diag($msg) if !$vm; + skip($msg,10) if !$vm; + + diag("Testing host devices in $vm_name"); + + my $node = remote_node($vm_name) or next; + clean_remote_node($node); + + _clean_devices($vm, $node); + test_devices($vm, $node); + _clean_devices($vm, $node); + exit; + } +} + +end(); +done_testing(); From d445a8064b0c2497dde18facdc9e5eda9da021c2 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 20 Nov 2023 15:14:20 +0100 Subject: [PATCH 02/50] wip: store node host devices --- lib/Ravada.pm | 10 ++++++++++ lib/Ravada/Domain.pm | 7 ++++++- t/device/50_nodes.t | 6 +++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index bf8e8cc01..8dfe06cca 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -1589,6 +1589,9 @@ sub _add_indexes_generic($self) { "unique(name, id_vm)" ,"index(id_vm)" ] + ,host_devices_nodes => [ + "unique(id_vm, id_hd)" + ] ,host_device_templates => [ "unique(id_host_device,path)" ] @@ -2211,6 +2214,13 @@ sub _sql_create_tables($self) { => 'timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP' } ] + ,[ + host_devices_nodes => { + id => 'integer NOT NULL PRIMARY KEY AUTO_INCREMENT' + ,id_hd => 'integer not null references `host_devices`(`id`) ON DELETE CASCADE' + ,id_vm => 'integer NOT NULL references `vms`(`id`) ON DELETE CASCADE' + } + ] , [ host_device_templates=> { diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index fbbf62238..aaec90db8 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -5194,8 +5194,13 @@ sub set_base_vm($self, %args) { sub _set_base_vm_hd($self, $node) { my @node_hds = $node->list_host_devices(); + my $sth = $$CONNECTOR->dbh->prepare( + "INSERT INTO host_devices_nodes (id_hd,id_vm) " + ." VALUES( ?,? )" + ); for my $hd ($self->list_host_devices) { - die Dumper($hd); + warn Dumper([$hd->{id}, $node->id]); + $sth->execute($hd->{id}, $node->id); } } diff --git a/t/device/50_nodes.t b/t/device/50_nodes.t index 12633b2ca..d4fbef4e9 100644 --- a/t/device/50_nodes.t +++ b/t/device/50_nodes.t @@ -30,7 +30,6 @@ sub _create_mock_devices($vm, $n_devices, $type, $value="fff:fff") { for my $n ( 1 .. $n_devices ) { my $file= "$PATH/${name} $N_DEVICE$value${n} Foobar " .$vm->name; - diag($file); $vm->write_file($file,"fff6f017-3417-4ad3-b05e-17ae3e1a461".int(rand(10))); } $N_DEVICE ++; @@ -73,6 +72,11 @@ sub test_assign($vm, $node, $hd) { $base->set_base_vm(id_vm => $node->id, user => user_admin); is($node->list_host_devices,$vm->list_host_devices) or exit; + my $base2 = create_domain($vm); + $base2->add_host_device($hd); + $base2->prepare_base(user_admin); + $base2->set_base_vm(id_vm => $node->id, user => user_admin); + my $req = Ravada::Request->clone( uid => user_admin->id ,id_domain => $base->id From 34252f3e0ff6019f257c91255da01a4c62d8aadd Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Tue, 21 Nov 2023 16:32:33 +0100 Subject: [PATCH 03/50] wip: testing hds in nodes --- lib/Ravada.pm | 11 +--- lib/Ravada/Domain.pm | 39 ++++------- lib/Ravada/Domain/Void.pm | 3 +- lib/Ravada/HostDevice.pm | 12 ++-- t/device/50_nodes.t | 135 ++++++++++++++++++++++++++++++++------ 5 files changed, 137 insertions(+), 63 deletions(-) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index 8dfe06cca..0c0ae99b2 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -1589,9 +1589,6 @@ sub _add_indexes_generic($self) { "unique(name, id_vm)" ,"index(id_vm)" ] - ,host_devices_nodes => [ - "unique(id_vm, id_hd)" - ] ,host_device_templates => [ "unique(id_host_device,path)" ] @@ -2214,13 +2211,6 @@ sub _sql_create_tables($self) { => 'timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP' } ] - ,[ - host_devices_nodes => { - id => 'integer NOT NULL PRIMARY KEY AUTO_INCREMENT' - ,id_hd => 'integer not null references `host_devices`(`id`) ON DELETE CASCADE' - ,id_vm => 'integer NOT NULL references `vms`(`id`) ON DELETE CASCADE' - } - ] , [ host_device_templates=> { @@ -2246,6 +2236,7 @@ sub _sql_create_tables($self) { ,id_vm => 'integer NOT NULL references `vms`(`id`) ON DELETE CASCADE' ,id_domain => 'integer NOT NULL references `domains`(`id`) ON DELETE CASCADE' ,name => 'varchar(255)' + ,'time_changed' => 'integer' } ] ,[ diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index aaec90db8..c3f5c2fa0 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -5178,7 +5178,6 @@ sub set_base_vm($self, %args) { die $err; } $self->_set_clones_autostart(0); - $self->_set_base_vm_hd($vm); } else { $self->_set_vm($vm,1); # force set vm on domain $self->_do_remove_base($user); @@ -5192,18 +5191,6 @@ sub set_base_vm($self, %args) { return $self->_set_base_vm_db($vm->id, $value); } -sub _set_base_vm_hd($self, $node) { - my @node_hds = $node->list_host_devices(); - my $sth = $$CONNECTOR->dbh->prepare( - "INSERT INTO host_devices_nodes (id_hd,id_vm) " - ." VALUES( ?,? )" - ); - for my $hd ($self->list_host_devices) { - warn Dumper([$hd->{id}, $node->id]); - $sth->execute($hd->{id}, $node->id); - } -} - sub _check_all_parents_in_node($self, $vm) { my @bases; my $base = $self; @@ -7135,10 +7122,7 @@ sub _add_host_devices($self, @args) { return if !@host_devices; return if $self->is_active(); - my $vm_local = $self->_vm->new( host => 'localhost' ); - my $vm = $vm_local; - - my ($id_vm, $request); + my ($request); if (!(scalar(@args) % 2)) { my %args = @args; $request = delete $args{request} if exists $args{request}; @@ -7157,7 +7141,8 @@ sub _add_host_devices($self, @args) { } next if !$host_device->enabled(); - my ($device) = $host_device->list_available_devices(); + my ($device) = $host_device->list_available_devices($self->_data('id_vm')); + warn $device; if ( !$device ) { $device = _refresh_domains_with_locked_devices($host_device); if (!$device) { @@ -7247,10 +7232,10 @@ sub _lock_host_device($self, $host_device, $device=undef) { return 0 if defined $id_domain_locked; - my $query = "INSERT INTO host_devices_domain_locked (id_domain,id_vm,name) VALUES(?,?,?)"; + my $query = "INSERT INTO host_devices_domain_locked (id_domain,id_vm,name,time_changed) VALUES(?,?,?,?)"; my $sth = $$CONNECTOR->dbh->prepare($query); - eval { $sth->execute($self->id,$self->_vm->id, $device) }; + eval { $sth->execute($self->id,$self->_vm->id, $device,time) }; if ($@) { warn $@; $self->_data(status => 'shutdown'); @@ -7266,13 +7251,15 @@ sub _lock_host_device($self, $host_device, $device=undef) { } sub _unlock_host_devices($self) { + warn "Unlocking all host devices"; my $sth = $$CONNECTOR->dbh->prepare("DELETE FROM host_devices_domain_locked " - ." WHERE id_domain=?" + ." WHERE id_domain=? AND time_changedexecute($self->id); + $sth->execute($self->id, time-5); } sub _unlock_host_device($self, $name) { + warn "Unlocking host device $name"; my $sth = $$CONNECTOR->dbh->prepare("DELETE FROM host_devices_domain_locked " ." WHERE id_domain=? AND name=?" ); @@ -7282,12 +7269,12 @@ sub _unlock_host_device($self, $name) { sub _check_host_device_already_used($self, $device) { - my $query = "SELECT id_domain FROM host_devices_domain_locked " + my $query = "SELECT id_domain,time_changed FROM host_devices_domain_locked " ." WHERE id_vm=? AND name=?" ; my $sth = $$CONNECTOR->dbh->prepare($query); $sth->execute($self->_vm->id, $device); - my ($id_domain) = $sth->fetchrow; + my ($id_domain,$time_changed) = $sth->fetchrow; # warn "\n".($id_domain or '')." [".$self->id."] had locked $device\n"; return if !defined $id_domain; @@ -7295,8 +7282,10 @@ sub _check_host_device_already_used($self, $device) { my $domain = Ravada::Domain->open($id_domain); - return $id_domain if $domain->is_active; + return $id_domain if time-$time_changed < 10 || $domain->is_active; + warn "delete really not used ".(time - $time_changed); + confess if $id_domain==4; $sth = $$CONNECTOR->dbh->prepare("DELETE FROM host_devices_domain_locked " ." WHERE id_domain=?"); $sth->execute($id_domain); diff --git a/lib/Ravada/Domain/Void.pm b/lib/Ravada/Domain/Void.pm index 24ad2532e..f78430eab 100644 --- a/lib/Ravada/Domain/Void.pm +++ b/lib/Ravada/Domain/Void.pm @@ -1209,8 +1209,7 @@ sub get_config($self) { } sub reload_config($self, $data) { - eval { DumpFile($self->_config_file(), $data) }; - confess $@ if $@; + $self->_vm->write_file($self->_config_file(), Dump($data)); } sub has_nat_interfaces($self) { diff --git a/lib/Ravada/HostDevice.pm b/lib/Ravada/HostDevice.pm index 2b5246ad9..3040569e5 100644 --- a/lib/Ravada/HostDevice.pm +++ b/lib/Ravada/HostDevice.pm @@ -99,15 +99,15 @@ sub list_devices_nodes($self) { my @devices; for my $node (@nodes) { - push @devices, $self->list_devices($node); + push @devices, $self->list_devices($node->id); } return @devices; } -sub list_devices($self, $vm = Ravada::VM->open($self->id_vm)) { - - die "Error: No list_command in host_device ".$self->id_vm +sub list_devices($self, $id_vm=$self->id_vm) { + my $vm = Ravada::VM->open($id_vm); + die "Error: No list_command in host_device ".$self->id if !$self->list_command; my @command = split /\s+/, $self->list_command; @@ -144,9 +144,9 @@ sub _device_locked($self, $name) { return 1 if $is_locked; } -sub list_available_devices($self) { +sub list_available_devices($self, $id_vm=undef) { my @device; - for my $dev_entry ( $self->list_devices ) { + for my $dev_entry ( $self->list_devices($id_vm) ) { next if $self->_device_locked($dev_entry); push @device, ($dev_entry); } diff --git a/t/device/50_nodes.t b/t/device/50_nodes.t index d4fbef4e9..cd41daf6c 100644 --- a/t/device/50_nodes.t +++ b/t/device/50_nodes.t @@ -22,7 +22,7 @@ my $PATH = "/var/tmp/$run_command("mkdir","-p",$PATH) if !$vm->file_exists($PATH); my $name = base_domain_name()."_${type} ID"; @@ -37,9 +37,57 @@ sub _create_mock_devices($vm, $n_devices, $type, $value="fff:fff") { return ("find $PATH/",$name); } -sub test_devices($vm, $node) { - my ($list_command,$list_filter) = _create_mock_devices($vm, 3 , "USB" ); - my ($list_command2,$list_filter2) = _create_mock_devices($node, 3 , "USB" ); +sub _number($value, $length=3) { + my $dev = $value; + for ( length($dev) .. $length-1) { + $dev .= int(rand(10)); + } + return $dev; +} + +sub _hex($value, $length=4) { + my $hex=$value; + for ( length($hex) .. $length-1) { + $hex .= chr(ord('a')+int(rand(7))); + } + return $hex; +} +sub _create_mock_devices_kvm($vm, $n_devices, $type, $value="fff:fff") { + $vm->run_command("mkdir","-p",$PATH) if !$vm->file_exists($PATH); + + my $name = base_domain_name()."_${type}_KVM "; + for my $n ( 1 .. $n_devices ) { + my $dev = _number($N_DEVICE.$n); + my $bus = _number($N_DEVICE.$n); + my $vendor = _hex($N_DEVICE.$n); + my $id = _hex($N_DEVICE.$n); + + my $file= "$PATH/${name} ".$vm->name + ." Bus $bus Device $dev: ID $vendor:$id"; + + diag($file); + $vm->write_file($file,"fff6f017-3417-4ad3-b05e-17ae3e1a461".int(rand(10))); + } + $N_DEVICE ++; + + return ("find $PATH/",$name); + + +} + +sub _create_mock_devices($vm, $n_devices, $type, $value="fff:fff") { + if ($vm->type eq 'KVM') { + return _create_mock_devices_kvm($vm, $n_devices, $type, $value ); + } elsif ($vm->type eq 'Void') { + return _create_mock_devices_void($vm, $n_devices, $type, $value ); + } +} + +sub test_devices($vm, $node, $n_local=3, $n_node=3) { + + _clean_devices($vm, $node); + my ($list_command,$list_filter) = _create_mock_devices($vm, $n_local , "USB" ); + my ($list_command2,$list_filter2) = _create_mock_devices($node, $n_node , "USB" ); my $templates = Ravada::HostDevice::Templates::list_templates($vm->type); my ($first) = $templates->[0]; @@ -48,6 +96,7 @@ sub test_devices($vm, $node) { my @list_hostdev = $vm->list_host_devices(); my ($hd) = $list_hostdev[-1]; $hd->_data('list_command',$list_command); + $hd->_data('list_filter',$list_filter); my $vm_name = $vm->name; my $node_name = $node->name; @@ -56,21 +105,21 @@ sub test_devices($vm, $node) { ok(grep /$vm_name/,@devices); ok(!grep /$node_name/,@devices); - my @devices_nodes = $hd->list_devices_nodes; - ok(grep /$vm_name/,@devices_nodes); - ok(grep /$node_name/,@devices_nodes); - test_assign($vm, $node, $hd); - exit; + _clean_devices($vm, $node); } sub test_assign($vm, $node, $hd) { my $base = create_domain($vm); $base->add_host_device($hd); + Ravada::Request->add_hardware( + uid => user_admin->id + ,id_domain => $base->id + ,name => 'usb controller' + ); $base->prepare_base(user_admin); $base->set_base_vm(id_vm => $node->id, user => user_admin); - is($node->list_host_devices,$vm->list_host_devices) or exit; my $base2 = create_domain($vm); $base2->add_host_device($hd); @@ -84,23 +133,43 @@ sub test_assign($vm, $node, $hd) { ); wait_request(); is(scalar($base->clones),scalar($hd->list_devices_nodes)); + my $found_in_node=0; + my $found_in_vm=0; + my %dupe; for my $clone0 ($base->clones) { + diag($clone0->{id}." ".$clone0->{name}); my $req = Ravada::Request->start_domain( uid => user_admin->id ,id_domain => $clone0->{id} ); - wait_request(); + wait_request( check_error => 0); my $domain = Ravada::Domain->open($clone0->{id}); - is($domain->is_active,1); - check_host_device($domain); + $domain->_data('status','active'); + diag($req->error); + is($domain->is_active,1) if $vm->type eq 'Void'; + my $hd = check_host_device($domain); + push(@{$dupe{$hd}},($clone0->{name}." ".$clone0->{id})); + is(scalar(@{$dupe{$hd}}),1) or die Dumper(\%dupe); + $found_in_node++ if $domain->_data('id_vm')==$node->id; + $found_in_vm++ if $domain->_data('id_vm')==$vm->id; } + ok($found_in_node,"Expecting in node, found $found_in_node"); + ok($found_in_vm,"Expecting in node, found $found_in_vm"); + diag("In node: $found_in_node, in vm: $found_in_vm"); + + remove_domain($base2, $base); } sub check_host_device($domain) { + my $sth = connector->dbh->prepare("SELECT * FROM host_devices_domain_locked " + ." WHERE id_domain=?"); + $sth->execute($domain->id); + my $found = $sth->fetchrow_hashref; + ok($found); if ($domain->type eq 'Void') { - check_host_device_void($domain); + return check_host_device_void($domain); } else { - die "TODO"; + return check_host_device_kvm($domain); } } @@ -119,7 +188,23 @@ sub check_host_device_void($domain) { my $vm = Ravada::VM->open($domain->_data('id_vm')); die $domain->name." ".$vm->name; }; - warn Dumper([$domain->name,$domain->_data('id_vm'),\@hostdev]); + return ($hostdev[1] or undef); +} + +sub check_host_device_kvm($domain) { + my $doc = $domain->xml_description(); + my $xml = XML::LibXML->load_xml(string => $doc); + my ($hd_source) = $xml->findnodes("/domain/devices/hostdev/source"); + ok($hd_source) or return; + my ($vendor) = $hd_source->findnodes("vendor"); + my $vendor_id=$vendor->getAttribute('id'); + my ($product) = $hd_source->findnodes("product"); + my $product_id=$product->getAttribute('id'); + my ($address) = $hd_source->findnodes("address"); + + return "$vendor_id-$product_id-".$address->getAttribute('bus')."-" + .$address->getAttribute('device'); + } sub _clean_devices(@nodes) { @@ -130,7 +215,13 @@ sub _clean_devices(@nodes) { my ($out, $err) = $vm->run_command("ls",$PATH); for my $line ( split /\n/,$out ) { next if $line !~ /$base/; - $vm->run_command("rm","$PATH/$line"); + diag($line); + if ($vm->is_local) { + unlink "$PATH/$line" or die "$! $PATH/$line"; + next; + } + my ($out, $err) = $vm->run_command("rm","'$PATH/$line'"); + die $err if $err; } } } @@ -139,13 +230,17 @@ sub _clean_devices(@nodes) { init(); clean(); -for my $vm_name ( 'Void' ) { +for my $vm_name ( vm_names() ) { my $vm; eval { $vm = rvd_back->search_vm($vm_name) }; SKIP: { my $msg = "SKIPPED: $vm_name virtual manager not found ".($@ or ''); + if ($vm && $>) { + $msg = "SKIPPED: Test must run as root"; + $vm = undef; + } diag($msg) if !$vm; skip($msg,10) if !$vm; @@ -155,9 +250,9 @@ for my $vm_name ( 'Void' ) { my $node = remote_node($vm_name) or next; clean_remote_node($node); - _clean_devices($vm, $node); test_devices($vm, $node); - _clean_devices($vm, $node); + test_devices($vm, $node, 5,1); + test_devices($vm, $node, 1,5); exit; } } From 43d947499cafb5ae85d4c852af7cb6ae70b3c592 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Wed, 22 Nov 2023 13:45:04 +0100 Subject: [PATCH 04/50] wip: debugging assigned hostdev --- lib/Ravada/HostDevice.pm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Ravada/HostDevice.pm b/lib/Ravada/HostDevice.pm index 3040569e5..0fffcd420 100644 --- a/lib/Ravada/HostDevice.pm +++ b/lib/Ravada/HostDevice.pm @@ -141,7 +141,8 @@ sub _device_locked($self, $name) { $sth->execute($self->id_vm, $name); my ($is_locked) = $sth->fetchrow; $is_locked = 0 if !defined $is_locked; - return 1 if $is_locked; + warn "$is_locked : $name\n"; + return $is_locked; } sub list_available_devices($self, $id_vm=undef) { From d03e0410578579e36d84a19d1d45693c6d0fbd90 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Thu, 23 Nov 2023 15:23:46 +0100 Subject: [PATCH 05/50] wip: check in correct id vm --- lib/Ravada/Domain.pm | 2 +- lib/Ravada/HostDevice.pm | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index c3f5c2fa0..9e2d5df5e 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -7141,7 +7141,7 @@ sub _add_host_devices($self, @args) { } next if !$host_device->enabled(); - my ($device) = $host_device->list_available_devices($self->_data('id_vm')); + my ($device) = $host_device->list_available_devices($self->_vm->id); warn $device; if ( !$device ) { $device = _refresh_domains_with_locked_devices($host_device); diff --git a/lib/Ravada/HostDevice.pm b/lib/Ravada/HostDevice.pm index 0fffcd420..36326574a 100644 --- a/lib/Ravada/HostDevice.pm +++ b/lib/Ravada/HostDevice.pm @@ -134,21 +134,21 @@ sub is_device($self, $device) { } -sub _device_locked($self, $name) { +sub _device_locked($self, $name, $id_vm=$self->id_vm) { my $sth = $$CONNECTOR->dbh->prepare("SELECT id FROM host_devices_domain_locked " ." WHERE id_vm=? AND name=? " ); - $sth->execute($self->id_vm, $name); + $sth->execute($id_vm, $name); my ($is_locked) = $sth->fetchrow; $is_locked = 0 if !defined $is_locked; - warn "$is_locked : $name\n"; + warn "$is_locked $id_vm: $name\n"; return $is_locked; } sub list_available_devices($self, $id_vm=undef) { my @device; for my $dev_entry ( $self->list_devices($id_vm) ) { - next if $self->_device_locked($dev_entry); + next if $self->_device_locked($dev_entry, $id_vm); push @device, ($dev_entry); } return @device; From 45280e71cb39ee1c7e8eabdb3c5bfec3c23c4237 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Thu, 23 Nov 2023 16:43:41 +0100 Subject: [PATCH 06/50] wip: search for vm with avail hds --- lib/Ravada/Domain.pm | 37 ++++++++++++++++++++++++++++++------- lib/Ravada/HostDevice.pm | 1 - 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index 9e2d5df5e..edc28c7a5 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -323,7 +323,7 @@ sub _around_start($orig, $self, @arg) { $enable_host_devices = 1 if !defined $enable_host_devices; for (1 .. 5) { - eval { $self->_start_checks(@arg) }; + eval { $self->_start_checks(@arg, enable_host_devices => $enable_host_devices) }; my $error = $@; if ($error) { if ( $error =~/base file not found/ && !$self->_vm->is_local) { @@ -458,13 +458,14 @@ sub _start_checks($self, @args) { my $vm_local = $self->_vm->new( host => 'localhost' ); my $vm = $vm_local; - my ($id_vm, $request); + my ($id_vm, $request, $enable_host_devices); if (!(scalar(@args) % 2)) { my %args = @args; # We may be asked to start the machine in a specific id_vmanager $id_vm = delete $args{id_vm}; $request = delete $args{request} if exists $args{request}; + $enable_host_devices = delete $args{enable_host_devices}; } # If not specific id_manager we go to the last id_vmanager unless it was localhost # If the last VManager was localhost it will try to balance here. @@ -497,7 +498,7 @@ sub _start_checks($self, @args) { if ($id_vm) { $self->_set_vm($vm); } else { - $self->_balance_vm($request); + $self->_balance_vm($request, $enable_host_devices); } if ( !$self->is_volatile && !$self->_vm->is_local() ) { if (!base_in_vm($self->id_base, $self->_vm->id)) { @@ -575,7 +576,25 @@ sub _search_already_started($self, $fast = 0) { return keys %started; } -sub _balance_vm($self, $request=undef) { +sub _search_vm_available_hd($self) { + my $vm_free = $self->_vm; + + for my $vm ( $self->list_vms ) { + warn "Trying ".$vm->name; + my @host_devices = $self->list_host_devices(); + my $available=1; + for my $hd (@host_devices) { + if ( !$hd->list_devices_available ) { + $available=0; + last; + } + } + return $vm if $available; + } + die "No host devices available in any node.\n"; +} + +sub _balance_vm($self, $request=undef, $host_devices=undef) { return if $self->{_migrated}; my $base; @@ -583,7 +602,12 @@ sub _balance_vm($self, $request=undef) { my $vm_free; for (;;) { - $vm_free = $self->_vm->balance_vm($self->_data('id_owner'),$base, $self->id); + if ($host_devices) { + $vm_free = $self->_search_vm_available_hd(); + } else { + $vm_free = $self->_vm->balance_vm($self->_data('id_owner'),$base + , $self->id); + } return if !$vm_free; last if $vm_free->id == $self->_vm->id; @@ -7142,8 +7166,8 @@ sub _add_host_devices($self, @args) { next if !$host_device->enabled(); my ($device) = $host_device->list_available_devices($self->_vm->id); - warn $device; if ( !$device ) { + warn "No device found\n"; $device = _refresh_domains_with_locked_devices($host_device); if (!$device) { $self->_data(status => 'down'); @@ -7251,7 +7275,6 @@ sub _lock_host_device($self, $host_device, $device=undef) { } sub _unlock_host_devices($self) { - warn "Unlocking all host devices"; my $sth = $$CONNECTOR->dbh->prepare("DELETE FROM host_devices_domain_locked " ." WHERE id_domain=? AND time_changedid_vm) { $sth->execute($id_vm, $name); my ($is_locked) = $sth->fetchrow; $is_locked = 0 if !defined $is_locked; - warn "$is_locked $id_vm: $name\n"; return $is_locked; } From d782916f8248698914ffb05e2853705905e9d247 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 27 Nov 2023 12:55:54 +0100 Subject: [PATCH 07/50] wip: host devices in nodes --- lib/Ravada.pm | 6 ++- lib/Ravada/Domain.pm | 47 ++++++++-------- lib/Ravada/HostDevice.pm | 27 +++++++--- lib/Ravada/VM.pm | 5 +- lib/Ravada/WebSocket.pm | 17 +++++- public/js/admin.js | 3 ++ sql/mysql/vms.sql | 2 - sql/sqlite/vms.sql | 2 - t/device/50_nodes.t | 84 +++++++++++++++++++++-------- templates/main/node_hostdev.html.ep | 27 +++++++--- templates/main/vm_hostdev.html.ep | 8 +++ 11 files changed, 160 insertions(+), 68 deletions(-) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index 0c0ae99b2..12a919469 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -1635,7 +1635,7 @@ sub _add_indexes_generic($self) { ,vms=> [ "unique(hostname, vm_type): hostname_type" - ,"UNIQUE (name)" + ,"UNIQUE (name,vm_type)" ] ); @@ -2206,6 +2206,7 @@ sub _sql_create_tables($self) { ,list_filter => 'varchar(128) not null' ,template_args => 'varchar(255) not null' ,devices => 'TEXT' + ,devices_node => 'TEXT' ,enabled => "integer NOT NULL default 1" ,'date_changed' => 'timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP' @@ -4377,7 +4378,8 @@ sub _cmd_list_host_devices($self, $request) { $id_host_device ); - $hd->list_devices; + my %list= $hd->list_devices_nodes; + warn Dumper(\%list); } diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index edc28c7a5..13982ca87 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -576,22 +576,28 @@ sub _search_already_started($self, $fast = 0) { return keys %started; } -sub _search_vm_available_hd($self) { - my $vm_free = $self->_vm; +sub _filter_vm_available_hd($self, @vms) { - for my $vm ( $self->list_vms ) { - warn "Trying ".$vm->name; - my @host_devices = $self->list_host_devices(); - my $available=1; + my @host_devices = $self->list_host_devices(); + + return @vms if !@host_devices; + + my @vms_ret; + + for my $vm ( @vms ) { + my $available = 1; for my $hd (@host_devices) { - if ( !$hd->list_devices_available ) { + if (! $hd->list_available_devices($vm->id) ) { $available=0; last; } } - return $vm if $available; + push @vms_ret,($vm) if $available; } - die "No host devices available in any node.\n"; + + die "No host devices available in any node.\n" if !@vms_ret; + + return @vms_ret; } sub _balance_vm($self, $request=undef, $host_devices=undef) { @@ -602,12 +608,8 @@ sub _balance_vm($self, $request=undef, $host_devices=undef) { my $vm_free; for (;;) { - if ($host_devices) { - $vm_free = $self->_search_vm_available_hd(); - } else { - $vm_free = $self->_vm->balance_vm($self->_data('id_owner'),$base - , $self->id); - } + $vm_free = $self->_vm->balance_vm($self->_data('id_owner'),$base + , $self->id, $host_devices); return if !$vm_free; last if $vm_free->id == $self->_vm->id; @@ -5310,7 +5312,7 @@ Returns a list for virtual machine managers where this domain is base =cut -sub list_vms($self) { +sub list_vms($self, $check_host_devices=0) { confess "Domain is not base" if !$self->is_base; my $sth = $$CONNECTOR->dbh->prepare("SELECT id_vm FROM bases_vm WHERE id_domain=? AND enabled = 1"); @@ -5327,7 +5329,9 @@ sub list_vms($self) { push @vms,($vm_local); $self->set_base_vm(vm => $vm_local, user => Ravada::Utils::user_daemon); } - return @vms; + return @vms if !$check_host_devices; + + return $self->_filter_vm_available_hd(@vms); } =head2 base_in_vm @@ -7278,15 +7282,14 @@ sub _unlock_host_devices($self) { my $sth = $$CONNECTOR->dbh->prepare("DELETE FROM host_devices_domain_locked " ." WHERE id_domain=? AND time_changedexecute($self->id, time-5); + $sth->execute($self->id, time-60); } sub _unlock_host_device($self, $name) { - warn "Unlocking host device $name"; my $sth = $$CONNECTOR->dbh->prepare("DELETE FROM host_devices_domain_locked " - ." WHERE id_domain=? AND name=?" + ." WHERE id_domain=? AND name=? AND time_changedexecute($self->id, $name); + $sth->execute($self->id, $name,time-60); } @@ -7307,8 +7310,6 @@ sub _check_host_device_already_used($self, $device) { return $id_domain if time-$time_changed < 10 || $domain->is_active; - warn "delete really not used ".(time - $time_changed); - confess if $id_domain==4; $sth = $$CONNECTOR->dbh->prepare("DELETE FROM host_devices_domain_locked " ." WHERE id_domain=?"); $sth->execute($id_domain); diff --git a/lib/Ravada/HostDevice.pm b/lib/Ravada/HostDevice.pm index c1b93f30d..efde2141e 100644 --- a/lib/Ravada/HostDevice.pm +++ b/lib/Ravada/HostDevice.pm @@ -9,6 +9,7 @@ Ravada::HostDevice - Host Device basic library for Ravada =cut +use Carp qw(cluck); use Data::Dumper; use Hash::Util qw(lock_hash); use IPC::Run3 qw(run3); @@ -66,6 +67,12 @@ has 'devices' => ( ,default => '' ); +has 'devices_node' => ( + isa => 'Str' + ,is => 'rw' + ,default => '' +); + sub _init_connector { return if $CONNECTOR && $$CONNECTOR; $CONNECTOR = \$Ravada::CONNECTOR if $Ravada::CONNECTOR; @@ -79,6 +86,7 @@ sub search_by_id($self, $id) { my $row = $sth->fetchrow_hashref; die "Error: device id='$id' not found" if !exists $row->{id}; $row->{devices} = '' if !defined $row->{devices}; + $row->{devices_node} = encode_json({}) if !defined $row->{devices_node}; return Ravada::HostDevice->new(%$row); } @@ -93,16 +101,20 @@ sub list_devices_nodes($self) { while ( my ($id) = $sth->fetchrow) { my $node = Ravada::VM->open($id); - push @nodes,($node); + push @nodes,($node) if $node->is_active; } - my @devices; - + my %devices; for my $node (@nodes) { - push @devices, $self->list_devices($node->id); + my @current_devs; + eval { @current_devs = $self->list_devices($node->id) }; + warn $@ if $@; + # push @devices, @current_devs; + $devices{$node->name}=\@current_devs; } - return @devices; + $self->_data( devices_node => \%devices ); + return %devices; } sub list_devices($self, $id_vm=$self->id_vm) { @@ -120,8 +132,6 @@ sub list_devices($self, $id_vm=$self->id_vm) { for my $line (split /\n/, $out ) { push @device,($line) if !defined $filter || $line =~ qr($filter)i; } - my $encoded = encode_json(\@device); - $self->_data( devices => $encoded ); return @device; } @@ -227,6 +237,9 @@ sub _data($self, $field, $value=undef) { $value =~ m{["'`$()\[\];]} || $value !~ /^(ls|find)/); + cluck if $field eq 'devices_node' && !ref($value); + warn Dumper([ref($value),$value]) if $field eq 'devices_node'; + $value = encode_json($value) if ref($value); my $old_value = $self->_data($field); diff --git a/lib/Ravada/VM.pm b/lib/Ravada/VM.pm index a54f1de76..ca7038dd9 100644 --- a/lib/Ravada/VM.pm +++ b/lib/Ravada/VM.pm @@ -1830,14 +1830,14 @@ Arguments =cut -sub balance_vm($self, $uid, $base=undef, $id_domain=undef) { +sub balance_vm($self, $uid, $base=undef, $id_domain=undef, $host_devices=undef) { my @vms; if ($base) { confess "Error: base is not an object ".Dumper($base) if !ref($base); - @vms = $base->list_vms(); + @vms = $base->list_vms($host_devices); } else { @vms = $self->list_nodes(); } @@ -2478,6 +2478,7 @@ sub list_host_devices($self) { my @found; while (my $row = $sth->fetchrow_hashref) { $row->{devices} = '' if !defined $row->{devices}; + $row->{devices_node} = '' if !defined $row->{devices_node}; push @found,(Ravada::HostDevice->new(%$row)); } diff --git a/lib/Ravada/WebSocket.pm b/lib/Ravada/WebSocket.pm index f9b83f239..2b2f1761f 100644 --- a/lib/Ravada/WebSocket.pm +++ b/lib/Ravada/WebSocket.pm @@ -229,15 +229,30 @@ sub _list_host_devices($rvd, $args) { my $user = Ravada::Auth::SQL->new(name => $login) or die "Error: uknown user $login"; - my $sth = $rvd->_dbh->prepare( "SELECT id,name,list_command,list_filter,devices,date_changed " + my $sth = $rvd->_dbh->prepare( "SELECT id,name,list_command,list_filter,devices,devices_node,date_changed " ." FROM host_devices WHERE id_vm=?"); $sth->execute($id_vm); my @found; while (my $row = $sth->fetchrow_hashref) { + warn Dumper($row->{devices_node}); $row->{devices} = decode_json($row->{devices}) if $row->{devices}; + eval { + $row->{devices_node} = decode_json($row->{devices_node}) if $row->{devices_node}; + }; + warn $@ if $@; $row->{_domains} = _list_domains_with_device($rvd, $row->{id}); + $row->{_n_devices}=0; + if (ref($row->{devices_node})) { + $row->{_nodes} = [sort keys %{$row->{devices_node}}]; + for (@{$row->{_nodes}}) { + $row->{_n_devices} += scalar(@{$row->{devices_node}->{$_}}); + } + $row->{_loading} = 0; + } else { + $row->{_nodes} = []; + } push @found, $row; next unless _its_been_a_while_channel($args->{channel}); my $req = Ravada::Request->list_host_devices( diff --git a/public/js/admin.js b/public/js/admin.js index 13181bdf7..976d43b4d 100644 --- a/public/js/admin.js +++ b/public/js/admin.js @@ -1371,6 +1371,9 @@ ravadaApp.directive("solShowMachine", swMach) }; $scope.update_host_device = function(hdev) { + hdev._loading=true; + hdev.devices_node=[]; + hdev._nodes = []; $http.post('/node/host_device/update' ,JSON.stringify(hdev)) .then(function(response) { diff --git a/sql/mysql/vms.sql b/sql/mysql/vms.sql index 4522bd332..e77bcec57 100644 --- a/sql/mysql/vms.sql +++ b/sql/mysql/vms.sql @@ -7,6 +7,4 @@ create table vms ( `security` varchar(20) default null, `is_active` int default 0, PRIMARY KEY (`id`), - UNIQUE KEY `name` (`name`), - UNIQUE KEY `hostname_type` (`hostname`,`vm_type`) ); diff --git a/sql/sqlite/vms.sql b/sql/sqlite/vms.sql index a363f0477..2c6e01619 100644 --- a/sql/sqlite/vms.sql +++ b/sql/sqlite/vms.sql @@ -5,6 +5,4 @@ create table vms ( , `hostname` varchar(128) NOT NULL , `default_storage` varchar(64) DEFAULT 'default' , `security` varchar(20) default null -, UNIQUE (`name`) -, UNIQUE (`hostname`,`vm_type`) ); diff --git a/t/device/50_nodes.t b/t/device/50_nodes.t index cd41daf6c..da38f5902 100644 --- a/t/device/50_nodes.t +++ b/t/device/50_nodes.t @@ -65,7 +65,6 @@ sub _create_mock_devices_kvm($vm, $n_devices, $type, $value="fff:fff") { my $file= "$PATH/${name} ".$vm->name ." Bus $bus Device $dev: ID $vendor:$id"; - diag($file); $vm->write_file($file,"fff6f017-3417-4ad3-b05e-17ae3e1a461".int(rand(10))); } $N_DEVICE ++; @@ -105,12 +104,12 @@ sub test_devices($vm, $node, $n_local=3, $n_node=3) { ok(grep /$vm_name/,@devices); ok(!grep /$node_name/,@devices); - test_assign($vm, $node, $hd); + test_assign($vm, $node, $hd, $n_local, $n_node); _clean_devices($vm, $node); } -sub test_assign($vm, $node, $hd) { +sub test_assign($vm, $node, $hd, $n_expected_in_vm, $n_expected_in_node) { my $base = create_domain($vm); $base->add_host_device($hd); Ravada::Request->add_hardware( @@ -126,29 +125,24 @@ sub test_assign($vm, $node, $hd) { $base2->prepare_base(user_admin); $base2->set_base_vm(id_vm => $node->id, user => user_admin); - my $req = Ravada::Request->clone( - uid => user_admin->id - ,id_domain => $base->id - ,number => scalar($hd->list_devices_nodes) - ); wait_request(); - is(scalar($base->clones),scalar($hd->list_devices_nodes)); my $found_in_node=0; my $found_in_vm=0; my %dupe; - for my $clone0 ($base->clones) { - diag($clone0->{id}." ".$clone0->{name}); - my $req = Ravada::Request->start_domain( + for ($hd->list_devices_nodes) { + my $name = new_domain_name; + my $req = Ravada::Request->clone( uid => user_admin->id - ,id_domain => $clone0->{id} + ,id_domain => $base->id + ,name => $name + ,start => 1 ); wait_request( check_error => 0); - my $domain = Ravada::Domain->open($clone0->{id}); + my $domain = rvd_back->search_domain($name); $domain->_data('status','active'); - diag($req->error); is($domain->is_active,1) if $vm->type eq 'Void'; my $hd = check_host_device($domain); - push(@{$dupe{$hd}},($clone0->{name}." ".$clone0->{id})); + push(@{$dupe{$hd}},($base->name." ".$base->id)); is(scalar(@{$dupe{$hd}}),1) or die Dumper(\%dupe); $found_in_node++ if $domain->_data('id_vm')==$node->id; $found_in_vm++ if $domain->_data('id_vm')==$vm->id; @@ -156,10 +150,49 @@ sub test_assign($vm, $node, $hd) { ok($found_in_node,"Expecting in node, found $found_in_node"); ok($found_in_vm,"Expecting in node, found $found_in_vm"); diag("In node: $found_in_node, in vm: $found_in_vm"); + is($found_in_node, $n_expected_in_node); + is($found_in_vm, $n_expected_in_vm); + + test_clone_nohd($base); remove_domain($base2, $base); } +sub test_clone_nohd($base) { + my $name = new_domain_name(); + my $req0 = Ravada::Request->clone( + uid => user_admin->id + ,id_domain => $base->id + ,name => $name + ,start => 0 + ); + wait_request(); + my $domain0 = rvd_back->search_domain($name); + my $req = Ravada::Request->start_domain( + uid => user_admin->id + ,id_domain => $domain0->id + ); + + wait_request( check_error => 0); + like($req->error,qr/host devices/i) or exit; + Ravada::Request->refresh_machine(uid => user_admin->id, id_domain => $domain0->id); + + my $domain = rvd_back->search_domain($name); + is($domain->is_active,0); + + my $req2 = Ravada::Request->start_domain( + uid => user_admin->id + ,id_domain => $domain0->id + ,enable_host_devices => 0 + ); + + wait_request( check_error => 0); + + my $domain2 = rvd_back->search_domain($name); + is($domain2->is_active,1); + +} + sub check_host_device($domain) { my $sth = connector->dbh->prepare("SELECT * FROM host_devices_domain_locked " ." WHERE id_domain=?"); @@ -188,7 +221,11 @@ sub check_host_device_void($domain) { my $vm = Ravada::VM->open($domain->_data('id_vm')); die $domain->name." ".$vm->name; }; - return ($hostdev[1] or undef); + my $ret=''; + for my $key (sort keys %{$hostdev[0]}) { + $ret .= "$key: ".$hostdev[0]->{$key}; + } + return $ret; } sub check_host_device_kvm($domain) { @@ -215,7 +252,6 @@ sub _clean_devices(@nodes) { my ($out, $err) = $vm->run_command("ls",$PATH); for my $line ( split /\n/,$out ) { next if $line !~ /$base/; - diag($line); if ($vm->is_local) { unlink "$PATH/$line" or die "$! $PATH/$line"; next; @@ -230,7 +266,7 @@ sub _clean_devices(@nodes) { init(); clean(); -for my $vm_name ( vm_names() ) { +for my $vm_name (reverse vm_names() ) { my $vm; eval { $vm = rvd_back->search_vm($vm_name) }; @@ -250,10 +286,12 @@ for my $vm_name ( vm_names() ) { my $node = remote_node($vm_name) or next; clean_remote_node($node); - test_devices($vm, $node); - test_devices($vm, $node, 5,1); - test_devices($vm, $node, 1,5); - exit; + test_devices($vm, $node,2,2); + test_devices($vm, $node,3,1); + test_devices($vm, $node,1,3); + + clean_remote_node($node); + } } diff --git a/templates/main/node_hostdev.html.ep b/templates/main/node_hostdev.html.ep index 653dc8a62..d3f76fe4d 100644 --- a/templates/main/node_hostdev.html.ep +++ b/templates/main/node_hostdev.html.ep @@ -40,14 +40,29 @@
- list command: - filter: -
No devices found
-
-
    -
  • + list command: + filter: + +
    No devices found
    +
    +
      +
    • {{device}}
    +
      +
    • + {{node}} +
        +
      • <%=l 'none found' %>
      • +
      • + {{dev}} +
      • +
      +
    • +
    + + {{hdev}} +
diff --git a/templates/main/vm_hostdev.html.ep b/templates/main/vm_hostdev.html.ep index 68c8fe6e4..21db2bcff 100644 --- a/templates/main/vm_hostdev.html.ep +++ b/templates/main/vm_hostdev.html.ep @@ -16,6 +16,14 @@ {{device}} + nodes +
    +
  • + {{node}} +
  • +
+ {{hdev}} + From b654afd675b52c489944f7046124e3b55b412253 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 27 Nov 2023 16:10:16 +0100 Subject: [PATCH 08/50] wip: properly show devs in nodes --- templates/main/node_hostdev.html.ep | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/templates/main/node_hostdev.html.ep b/templates/main/node_hostdev.html.ep index d3f76fe4d..997cbaec6 100644 --- a/templates/main/node_hostdev.html.ep +++ b/templates/main/node_hostdev.html.ep @@ -43,18 +43,19 @@ list command: filter: -
No devices found
+
<%=l 'No devices found'%>
  • {{device}}
-
    +
    • {{node}}
        -
      • <%=l 'none found' %>
      • +
      • + <%=l 'No devices found'%>
      • {{dev}}
      • @@ -62,7 +63,5 @@
      - {{hdev}} - From 57132d6adeb92aae65d3a08fb5583d2f18a2eb40 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 27 Nov 2023 16:52:14 +0100 Subject: [PATCH 09/50] wip: test hostdevs --- lib/Ravada.pm | 4 +--- lib/Ravada/HostDevice.pm | 1 - lib/Ravada/VM/KVM.pm | 5 +++++ lib/Ravada/VM/Void.pm | 3 +++ public/js/admin.js | 12 +++++++----- public/js/ravada.js | 4 ++++ 6 files changed, 20 insertions(+), 9 deletions(-) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index 12a919469..ae15d6ac2 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -5541,10 +5541,8 @@ sub _cmd_list_cpu_models($self, $request) { my $id_domain = $request->args('id_domain'); my $domain = Ravada::Domain->open($id_domain); - my $info = $domain->get_info(); - my $vm = $domain->_vm->vm; - my @out = $vm->get_cpu_model_names('x86_64'); + my @out = $domain->_vm->get_cpu_model_names('x86_64'); $request->output(encode_json(\@out)); } diff --git a/lib/Ravada/HostDevice.pm b/lib/Ravada/HostDevice.pm index efde2141e..bcec92367 100644 --- a/lib/Ravada/HostDevice.pm +++ b/lib/Ravada/HostDevice.pm @@ -238,7 +238,6 @@ sub _data($self, $field, $value=undef) { || $value !~ /^(ls|find)/); cluck if $field eq 'devices_node' && !ref($value); - warn Dumper([ref($value),$value]) if $field eq 'devices_node'; $value = encode_json($value) if ref($value); diff --git a/lib/Ravada/VM/KVM.pm b/lib/Ravada/VM/KVM.pm index 01a03033d..8dbf27e15 100644 --- a/lib/Ravada/VM/KVM.pm +++ b/lib/Ravada/VM/KVM.pm @@ -2922,4 +2922,9 @@ sub get_library_version($self) { return $self->vm->get_library_version(); } +sub get_cpu_model_names($self,$arch='x86_64') { + return $self->vm->get_cpu_model_names($arch); +} + + 1; diff --git a/lib/Ravada/VM/Void.pm b/lib/Ravada/VM/Void.pm index 08f6b6c01..637c73019 100644 --- a/lib/Ravada/VM/Void.pm +++ b/lib/Ravada/VM/Void.pm @@ -647,6 +647,9 @@ sub active_storage_pool($self, $name, $value) { $self->write_file($file_sp, Dump( \@list)); } +sub get_cpu_model_names($self,$arch='x86_64') { + return qw(486 qemu32 qemu64); +} #########################################################################3 diff --git a/public/js/admin.js b/public/js/admin.js index 976d43b4d..75016e2f4 100644 --- a/public/js/admin.js +++ b/public/js/admin.js @@ -207,11 +207,13 @@ ravadaApp.directive("solShowMachine", swMach) && $scope.id_iso.options['machine']) { var types = $scope.machine_types[$scope.backend][$scope.id_iso.arch]; var option = $scope.id_iso.options['machine']; - for (var i=0; i Date: Tue, 28 Nov 2023 10:49:41 +0100 Subject: [PATCH 10/50] wip: show hds in domain --- lib/Ravada.pm | 1 - lib/Ravada/Domain.pm | 1 + templates/main/vm_hostdev.html.ep | 21 ++++++++++----------- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index ae15d6ac2..58c3b995b 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -4379,7 +4379,6 @@ sub _cmd_list_host_devices($self, $request) { ); my %list= $hd->list_devices_nodes; - warn Dumper(\%list); } diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index 13982ca87..77464dca9 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -7146,6 +7146,7 @@ sub list_host_devices_attached($self) { # adds host devices to domain instance # usually run right before startup sub _add_host_devices($self, @args) { +warn 1; my @host_devices = $self->list_host_devices(); return if !@host_devices; return if $self->is_active(); diff --git a/templates/main/vm_hostdev.html.ep b/templates/main/vm_hostdev.html.ep index 21db2bcff..590205dd1 100644 --- a/templates/main/vm_hostdev.html.ep +++ b/templates/main/vm_hostdev.html.ep @@ -11,22 +11,21 @@ ng-disabled="showmachine.is_active" ng-model="hdev.is_attached" ng-click="toggle_host_device(hdev.id)"/> {{hdev.name}} -
        -
      • - {{device}} -
      • -
      - nodes -
        -
      • +
          +
        • {{node}} +
            +
          • + <%=l 'No devices found'%>
          • +
          • + {{dev}} +
          • +
          +
        - {{hdev}} - - Manage Host Devices From b693ab8dfbda4012540c65bf94e1aba6a9dfa546 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Wed, 29 Nov 2023 12:06:14 +0100 Subject: [PATCH 11/50] wip: testing templates --- lib/Ravada/Domain.pm | 5 ++--- lib/Ravada/HostDevice.pm | 5 ++--- lib/Ravada/WebSocket.pm | 3 +-- t/device/00_host_device.t | 1 + t/device/10_templates.t | 15 +++++++++++++-- 5 files changed, 19 insertions(+), 10 deletions(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index 77464dca9..84b3c6dbb 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -7108,6 +7108,7 @@ sub list_host_devices($self) { my @found; while (my $row = $sth->fetchrow_hashref) { $row->{devices} = '' if !defined $row->{devices}; + $row->{devices_node} = '{}' if !defined $row->{devices_node}; push @found,(Ravada::HostDevice->new(%$row)); } @@ -7146,7 +7147,6 @@ sub list_host_devices_attached($self) { # adds host devices to domain instance # usually run right before startup sub _add_host_devices($self, @args) { -warn 1; my @host_devices = $self->list_host_devices(); return if !@host_devices; return if $self->is_active(); @@ -7172,7 +7172,6 @@ warn 1; my ($device) = $host_device->list_available_devices($self->_vm->id); if ( !$device ) { - warn "No device found\n"; $device = _refresh_domains_with_locked_devices($host_device); if (!$device) { $self->_data(status => 'down'); @@ -7283,7 +7282,7 @@ sub _unlock_host_devices($self) { my $sth = $$CONNECTOR->dbh->prepare("DELETE FROM host_devices_domain_locked " ." WHERE id_domain=? AND time_changedexecute($self->id, time-60); + $sth->execute($self->id, time-2); } sub _unlock_host_device($self, $name) { diff --git a/lib/Ravada/HostDevice.pm b/lib/Ravada/HostDevice.pm index bcec92367..d87a37c67 100644 --- a/lib/Ravada/HostDevice.pm +++ b/lib/Ravada/HostDevice.pm @@ -132,6 +132,7 @@ sub list_devices($self, $id_vm=$self->id_vm) { for my $line (split /\n/, $out ) { push @device,($line) if !defined $filter || $line =~ qr($filter)i; } + $self->_data( 'devices' => \@device) if $id_vm == $self->id_vm; return @device; } @@ -154,7 +155,7 @@ sub _device_locked($self, $name, $id_vm=$self->id_vm) { return $is_locked; } -sub list_available_devices($self, $id_vm=undef) { +sub list_available_devices($self, $id_vm=$self->id_vm) { my @device; for my $dev_entry ( $self->list_devices($id_vm) ) { next if $self->_device_locked($dev_entry, $id_vm); @@ -237,8 +238,6 @@ sub _data($self, $field, $value=undef) { $value =~ m{["'`$()\[\];]} || $value !~ /^(ls|find)/); - cluck if $field eq 'devices_node' && !ref($value); - $value = encode_json($value) if ref($value); my $old_value = $self->_data($field); diff --git a/lib/Ravada/WebSocket.pm b/lib/Ravada/WebSocket.pm index 2b2f1761f..53d7c9b29 100644 --- a/lib/Ravada/WebSocket.pm +++ b/lib/Ravada/WebSocket.pm @@ -236,9 +236,8 @@ sub _list_host_devices($rvd, $args) { my @found; while (my $row = $sth->fetchrow_hashref) { - warn Dumper($row->{devices_node}); - $row->{devices} = decode_json($row->{devices}) if $row->{devices}; eval { + $row->{devices} = decode_json($row->{devices}) if $row->{devices}; $row->{devices_node} = decode_json($row->{devices_node}) if $row->{devices_node}; }; warn $@ if $@; diff --git a/t/device/00_host_device.t b/t/device/00_host_device.t index 1e720c15e..18a3a6d00 100644 --- a/t/device/00_host_device.t +++ b/t/device/00_host_device.t @@ -421,6 +421,7 @@ sub test_host_device_usb_mock($vm, $n_hd=1) { is(scalar($clone->list_host_devices_attached()), $n_hd, $clone->name); push @clones,($clone); } + sleep 2; $clones[0]->shutdown_now(user_admin); _check_hostdev($clones[0], $n_hd); my @devs_attached = $clones[0]->list_host_devices_attached(); diff --git a/t/device/10_templates.t b/t/device/10_templates.t index 0776258f5..efe1ed526 100644 --- a/t/device/10_templates.t +++ b/t/device/10_templates.t @@ -138,6 +138,7 @@ sub test_hd_in_domain($vm , $hd) { _compare_hds($domain, $clone); test_device_unlocked($clone); + my $t0 = time; if ($hd->list_devices) { eval { $clone->start(user_admin) }; if (!$count) { @@ -149,6 +150,7 @@ sub test_hd_in_domain($vm , $hd) { test_device_locked($clone); test_hostdev_in_domain_config($clone, ($hd->name =~ /PCI/ && $vm->type eq 'KVM')); } + sleep(3) if time-$t0<3; $clone->shutdown_now(user_admin); test_device_unlocked($clone); @@ -431,21 +433,29 @@ sub test_templates_gone_usb_2($vm) { my $domain = _create_domain_hd($vm, $hd); _fix_usb_ports($domain); + my $t0=time; $domain->start(user_admin); my $dev_config = $domain->_device_already_configured($hd); ok($dev_config) or exit; is(scalar($hd->list_domains_with_device()),1); + sleep(3) if time-$t0<3; + $domain->shutdown_now(user_admin); $hd->_data('list_filter',"no match"); + Ravada::Request->list_host_devices( + uid => user_admin->id + ,id_host_device => $hd->id + ); + wait_request(); diag("try to start again, it should fail"); my $req = Ravada::Request->start_domain(uid => user_admin->id ,id_domain => $domain->id ); wait_request(check_error => 0, debug => 0); my $req2 = Ravada::Request->open($req->id); - like($req2->error,qr/No available devices/); + like($req2->error,qr/No available devices/) or exit; my $req_no_hd = Ravada::Request->start_domain(uid => user_admin->id ,id_domain => $domain->id @@ -489,6 +499,7 @@ sub test_templates_gone_usb($vm) { ok($dev_config) or exit; is(scalar($hd->list_domains_with_device()),1); + sleep 3; $domain->shutdown_now(user_admin); is($domain->_device_already_configured($hd), $dev_config) or exit; @@ -820,7 +831,7 @@ sub test_hd_remove($vm, $host_device) { clean(); -for my $vm_name ( vm_names()) { +for my $vm_name (reverse vm_names()) { my $vm; eval { $vm = rvd_back->search_vm($vm_name) }; From d6c2be3b9ff316c33c42b95974e76d08094f2cee Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Thu, 30 Nov 2023 12:32:43 +0100 Subject: [PATCH 12/50] wip: grace period for devices --- lib/Ravada/Domain.pm | 6 ++--- t/device/10_templates.t | 53 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index 84b3c6dbb..c834dc756 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -7278,11 +7278,11 @@ sub _lock_host_device($self, $host_device, $device=undef) { return 1; } -sub _unlock_host_devices($self) { +sub _unlock_host_devices($self, $time_changed=3) { my $sth = $$CONNECTOR->dbh->prepare("DELETE FROM host_devices_domain_locked " - ." WHERE id_domain=? AND time_changedexecute($self->id, time-2); + $sth->execute($self->id, time-$time_changed); } sub _unlock_host_device($self, $name) { diff --git a/t/device/10_templates.t b/t/device/10_templates.t index efe1ed526..e8068b57f 100644 --- a/t/device/10_templates.t +++ b/t/device/10_templates.t @@ -95,6 +95,25 @@ sub _fix_host_device($hd) { } elsif ($hd->{name} =~ /USB/ ) { _set_hd_usb($hd); } + _purge_hd($hd); +} + +sub _purge_hd($hd) { + my $sth = connector->dbh->prepare( + "DELETE FROM host_devices_domain WHERE id_host_device=? AND id_domain NOT IN (select id FROM domains)" + ); + $sth->execute($hd->{id}); + + $sth = connector->dbh->prepare( + "DELETE FROM host_devices_domain WHERE id_host_device=? AND id_domain NOT IN (select id FROM domains WHERE status='active')" + ); + $sth->execute($hd->{id}); + + $sth = connector->dbh->prepare( + "DELETE FROM host_devices_domain_locked WHERE id_domain NOT IN (select id FROM domains WHERE status='active')" + ); + $sth->execute(); + } sub _create_domain_hd($vm, $hd) { @@ -106,8 +125,18 @@ sub _create_domain_hd($vm, $hd) { return $domain; } +sub _shutdown_all($vm) { + for my $dom ($vm->list_domains) { + $dom->shutdown_now(user_admin); + $dom->_unlock_host_devices(0); + } + my $sth = connector->dbh->prepare("DELETE FROM host_devices_domain_locked"); + $sth->execute(); +} + sub test_hd_in_domain($vm , $hd) { + _shutdown_all($vm); my $domain = create_domain($vm); if ($vm->type eq 'KVM') { if ($hd->{name} =~ /PCI/) { @@ -131,6 +160,7 @@ sub test_hd_in_domain($vm , $hd) { } $domain->prepare_base(user_admin); + _shutdown_all($vm); my $n_locked = _count_locked(); for my $count (reverse 0 .. $hd->list_devices ) { my $clone = $domain->clone(name => new_domain_name() ,user => user_admin); @@ -147,6 +177,7 @@ sub test_hd_in_domain($vm , $hd) { last; } is(_count_locked(),++$n_locked) or exit; + next; test_device_locked($clone); test_hostdev_in_domain_config($clone, ($hd->name =~ /PCI/ && $vm->type eq 'KVM')); } @@ -305,9 +336,12 @@ sub _compare_hds($base, $clone) { } sub _count_locked() { - my $sth = connector->dbh->prepare("SELECT count(*) FROM host_devices_domain_locked "); + my $n=0; + my $sth = connector->dbh->prepare("SELECT * FROM host_devices_domain_locked ORDER BY id_domain, name"); $sth->execute(); - my ($n) = $sth->fetchrow; + while (my $row = $sth->fetchrow_hashref) { + $n++; + } return $n; } @@ -806,9 +840,12 @@ sub test_hd_remove($vm, $host_device) { _fix_usb_ports($domain); _fix_host_device($host_device) if $vm->type eq 'KVM'; $domain->add_host_device($host_device); - + _count_locked(); eval { $domain->start(user_admin) }; - is(''.$@, '') unless $start_fails; + if (!$start_fails) { + is(''.$@, '') or confess "Error starting ".$domain->name." " + ."[ ".$domain->id."]"; + } $domain->shutdown_now(user_admin); my $req = Ravada::Request->remove_host_device( @@ -825,6 +862,14 @@ sub test_hd_remove($vm, $host_device) { $sth->execute($host_device->id); my ($found) = $sth->fetchrow; ok(!$found); + + $sth = connector->dbh->prepare( + "SELECT * FROM host_devices_domain WHERE id_host_device=?" + ); + $sth->execute($host_device->id); + ($found) = $sth->fetchrow; + ok(!$found); + } #################################################################### From f08eb5b1533e68d2475e83743f3a90a1f40bc016 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 29 Apr 2024 11:49:23 +0200 Subject: [PATCH 13/50] wip: show devices by node --- lib/Ravada/HostDevice.pm | 2 -- lib/Ravada/WebSocket.pm | 8 ++++---- templates/main/node_hostdev.html.ep | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/Ravada/HostDevice.pm b/lib/Ravada/HostDevice.pm index 2d21109ed..44005347d 100644 --- a/lib/Ravada/HostDevice.pm +++ b/lib/Ravada/HostDevice.pm @@ -110,7 +110,6 @@ sub list_devices_nodes($self) { next; } my $node = Ravada::VM->open($ndata->[0]); - warn $node->name." ".$node->is_active; my @current_devs; eval { @current_devs = $self->list_devices($node->id) @@ -247,7 +246,6 @@ sub _data($self, $field, $value=undef) { $value =~ m{["'`$()\[\];]} || $value !~ /^(ls|find)/); - warn Dumper($value) if $field eq 'devices_node'; $value = encode_json($value) if ref($value); my $old_value = $self->_data($field); diff --git a/lib/Ravada/WebSocket.pm b/lib/Ravada/WebSocket.pm index ba1d6b528..c6813b65a 100644 --- a/lib/Ravada/WebSocket.pm +++ b/lib/Ravada/WebSocket.pm @@ -260,14 +260,14 @@ sub _list_devices_node($rvd, $row) { $row->{_n_devices}=0; my %ret; - if (@$devices) { - $row->{_nodes} = [sort keys %{$devices}}]; + if (%$devices) { + $row->{_nodes} = [sort keys %{$devices}]; for (@{$row->{_nodes}}) { - $row->{_n_devices} += scalar(@{$row->{devices_node}->{$_}}); + $row->{_n_devices} += scalar(@{$devices->{$_}}); } $row->{_loading} = 0; for my $node ( keys %$devices ) { - $ret{$node} = map { {name => $_ } } @{$devices->{$node}}; + $ret{$node} = [ map { {name => $_ } } @{$devices->{$node}} ]; } } else { $row->{_nodes} = []; diff --git a/templates/main/node_hostdev.html.ep b/templates/main/node_hostdev.html.ep index 937960699..4b5609f0c 100644 --- a/templates/main/node_hostdev.html.ep +++ b/templates/main/node_hostdev.html.ep @@ -59,7 +59,7 @@ {{node}}
      • From 1f8f908836fc68fee3ce87acf0dab51f4572240d Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Wed, 1 May 2024 11:13:17 +0200 Subject: [PATCH 16/50] wip: machine with device locked --- templates/main/node_hostdev.html.ep | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/templates/main/node_hostdev.html.ep b/templates/main/node_hostdev.html.ep index 67a2775b0..b65669484 100644 --- a/templates/main/node_hostdev.html.ep +++ b/templates/main/node_hostdev.html.ep @@ -59,7 +59,10 @@
      • <%=l 'No devices found'%>
      • - {{dev.domain.name}} {{dev.name}} From 332f066dbbd288eade7997b02054628b1cf1ec65 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Thu, 2 May 2024 11:34:34 +0200 Subject: [PATCH 17/50] wip: host devices by VM type --- lib/Ravada/Domain.pm | 1 + public/js/admin.js | 4 + script/rvd_front | 13 +++ templates/bootstrap/navigation.html.ep | 1 + templates/main/admin_hostdev.html.ep | 132 +++++++++++++++++++++++++ 5 files changed, 151 insertions(+) create mode 100644 templates/main/admin_hostdev.html.ep diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index 5e139a0f5..4a2f123ac 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -1050,6 +1050,7 @@ sub _check_free_vm_memory { my $self = shift; return if !Ravada::Front::setting(undef,"/backend/limits/startup_ram"); + return if !$self->is_known(); my $vm_free_mem = $self->_vm->free_memory; diff --git a/public/js/admin.js b/public/js/admin.js index 73b69b85a..d58419c4c 100644 --- a/public/js/admin.js +++ b/public/js/admin.js @@ -13,6 +13,7 @@ ravadaApp.directive("solShowMachine", swMach) .controller("settings_node",settings_node) .controller("settings_storage",settings_storage) .controller("settings_route",settings_route) + .controller("settings_host_devices",settings_host_devices) .controller("new_node", newNodeCtrl) .controller("new_storage", new_storage) .controller("settings_global", settings_global_ctrl) @@ -1199,6 +1200,9 @@ ravadaApp.directive("solShowMachine", swMach) }; + function settings_host_devices($scope, $http, $timeout) { + }; + function settings_route($scope, $http, $timeout) { var url_ws; $scope.init = function(id_network) { diff --git a/script/rvd_front b/script/rvd_front index e582ab96c..e7074e985 100644 --- a/script/rvd_front +++ b/script/rvd_front @@ -438,6 +438,19 @@ get '/admin/storage' => sub($c) { }; +get '/admin/host_devices' => sub($c) { + + _add_admin_libs($c); + _select_vm($c); + + $c->stash(tab => ($c->param('tab') or '')); + $c->stash('id' => $c->stash('id_vm')); + + return $c->render( template => '/main/admin_hostdev' ); + +}; + + get '/admin/networks/*id_vm' => { id_vm => undef} => sub($c) { return access_denied($c) unless $USER->is_admin diff --git a/templates/bootstrap/navigation.html.ep b/templates/bootstrap/navigation.html.ep index 5c8fab373..044062493 100644 --- a/templates/bootstrap/navigation.html.ep +++ b/templates/bootstrap/navigation.html.ep @@ -47,6 +47,7 @@ navbar-dark bg-dark fixed-top navbar-expand-lg navbar-inverse">  <%=l 'Nodes' %>  <%=l 'Routes' %>  <%=l 'Storage' %> +  <%=l 'Host Devices' %> % } % if ($_user->can_create_networks) { diff --git a/templates/main/admin_hostdev.html.ep b/templates/main/admin_hostdev.html.ep new file mode 100644 index 000000000..b65669484 --- /dev/null +++ b/templates/main/admin_hostdev.html.ep @@ -0,0 +1,132 @@ + +
        + + + + + +
        + +
        + +
        + + + + + + {{hdev.name}} + + + +
        +
        + This host device is configured in these virtual machines +
          +
        • {{name}}
        • +
        +
        + <%=l ' Are you sure you want to remove this host device ?' %> + + +
        + + + + + +
        + list command: + filter: + +
        No devices found
        +
        + + + +
        + Bases + + +
        + +
        + + +
        + +
        + Machines + + +
        +
        +
        + + +
        +
        + {{domain.name}} + {{domain.name}} + + {{domain.device}} +
        +
        +
        + +
        + + +
        +
        From fd856768977eb8decb6e7e439e11c3a3f73f6670 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Fri, 3 May 2024 12:58:23 +0200 Subject: [PATCH 18/50] wip: frontend manage hd --- templates/main/admin_hostdev.html.ep | 35 ++++++++++++++++++++++++---- templates/main/node_hostdev.html.ep | 3 ++- templates/main/vm_hostdev.html.ep | 2 +- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/templates/main/admin_hostdev.html.ep b/templates/main/admin_hostdev.html.ep index b65669484..f38cf9d76 100644 --- a/templates/main/admin_hostdev.html.ep +++ b/templates/main/admin_hostdev.html.ep @@ -1,5 +1,19 @@ + + +%= include 'bootstrap/header' + +
        + %= include 'bootstrap/navigation' +
        + + + +
        + +
        +

        <%=l 'Host Devices' %>

        +
        -
        From 8dc993c5e2a2feae05bc5db6dd48be5c702beae5 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Fri, 3 May 2024 15:26:46 +0200 Subject: [PATCH 20/50] wip: JS list host devices --- public/js/admin.js | 46 +++++++++++++++++++++++++++- templates/main/admin_hostdev.html.ep | 10 +++--- 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/public/js/admin.js b/public/js/admin.js index 95942172b..24bfc849b 100644 --- a/public/js/admin.js +++ b/public/js/admin.js @@ -1201,7 +1201,51 @@ ravadaApp.directive("solShowMachine", swMach) }; function manage_host_devices($scope, $http, $timeout) { - }; + $scope.init=function(id,url) { + subscribe_list_host_devices(id, url); + }; + + subscribe_list_host_devices= function(id, url) { + $scope.show_requests = false; + $scope.host_devices = []; + var ws = new WebSocket(url); + ws.onopen = function (event) { ws.send('list_host_devices/'+id) }; + ws.onclose = function() { + ws = new WebSocket(url); + }; + + ws.onmessage = function (event) { + var data = JSON.parse(event.data); + $scope.$apply(function () { + if (Object.keys($scope.host_devices).length != data.length) { + $scope.host_devices.length = data.length; + } + for (var i=0, iLength = data.length; ito_abs %>')" >
        -
        +
        - + +
        No devices found
        - -
          +
          • {{node}}
              From 291acf5d9aba9ac8460cec49a59e912df2f37bc2 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 6 May 2024 12:22:28 +0200 Subject: [PATCH 23/50] wip: check the hd is from the proper node issue #2035 --- t/device/50_nodes.t | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/t/device/50_nodes.t b/t/device/50_nodes.t index 4ab8f4fcf..3cd47aeba 100644 --- a/t/device/50_nodes.t +++ b/t/device/50_nodes.t @@ -139,7 +139,9 @@ sub test_assign($vm, $node, $hd, $n_expected_in_vm, $n_expected_in_node) { my $found_in_node=0; my $found_in_vm=0; my %dupe; - for ($hd->list_devices_nodes) { + + my %devices_nodes = $hd->list_devices_nodes(); + for (0 .. $n_expected_in_vm+$n_expected_in_node) { my $name = new_domain_name; my $req = Ravada::Request->clone( uid => user_admin->id @@ -151,6 +153,7 @@ sub test_assign($vm, $node, $hd, $n_expected_in_vm, $n_expected_in_node) { my $domain = rvd_back->search_domain($name); $domain->_data('status','active'); is($domain->is_active,1) if $vm->type eq 'Void'; + check_hd_from_node($domain,\%devices_nodes); my $hd = check_host_device($domain); push(@{$dupe{$hd}},($base->name." ".$base->id)); is(scalar(@{$dupe{$hd}}),1) or die Dumper(\%dupe); @@ -168,6 +171,23 @@ sub test_assign($vm, $node, $hd, $n_expected_in_vm, $n_expected_in_node) { remove_domain($base2, $base); } +sub check_hd_from_node($domain, $devices_node) { + my $id_vm = $domain->_data('id_vm'); + is($domain->_vm->id,$id_vm); + + my @devices = $domain->list_host_devices_attached(); + my ($locked) = grep { $_->{is_locked} } @devices; + + warn Dumper($locked); + + my $vm = Ravada::VM->open($id_vm); + my $devices = $devices_node->{$vm->name}; + + my ($match) = grep { $_ eq $locked->{name} } @$devices; + ok($match,"Expecting $match in ".Dumper($devices)); + exit; +} + sub test_clone_nohd($base) { my $name = new_domain_name(); my $req0 = Ravada::Request->clone( From 7ab237a69a50991e656a9458c5f3847ab17884e4 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 6 May 2024 15:29:29 +0200 Subject: [PATCH 24/50] wip: testing other nodes issue #2035 --- t/device/50_nodes.t | 100 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 95 insertions(+), 5 deletions(-) diff --git a/t/device/50_nodes.t b/t/device/50_nodes.t index 3cd47aeba..ae953a6a2 100644 --- a/t/device/50_nodes.t +++ b/t/device/50_nodes.t @@ -82,6 +82,30 @@ sub _create_mock_devices($vm, $n_devices, $type, $value="fff:fff") { } } +sub test_devices_v2($node, $number) { + _clean_devices(@$node); + my $vm = $node->[0]; + my ($list_command,$list_filter) = _create_mock_devices($node->[0], $number->[0], "USB" ); + for my $i (1..scalar(@$node)-1) { + die "Error, missing number[$i] ".Dumper($number) unless defined $number->[$i]; + _create_mock_devices($node->[$i], $number->[$i], "USB" ); + } + my $templates = Ravada::HostDevice::Templates::list_templates($vm->type); + my ($first) = $templates->[0]; + + $vm->add_host_device(template => $first->{name}); + my @list_hostdev = $vm->list_host_devices(); + my ($hd) = $list_hostdev[-1]; + $hd->_data('list_command',$list_command); + $hd->_data('list_filter',$list_filter); + + my %devices_nodes = $hd->list_devices_nodes(); + + test_assign_v2($hd,$node,$number); + + _clean_devices($vm, $node); +} + sub test_devices($vm, $node, $n_local=3, $n_node=3) { _clean_devices($vm, $node); @@ -119,6 +143,52 @@ sub test_devices($vm, $node, $n_local=3, $n_node=3) { _clean_devices($vm, $node); } +sub test_assign_v2($hd, $node, $number) { + my $vm = $node->[0]; + my $base = create_domain($vm); + $base->add_host_device($hd); + Ravada::Request->add_hardware( + uid => user_admin->id + ,id_domain => $base->id + ,name => 'usb controller' + ); + $base->prepare_base(user_admin); + for my $curr_node (@$node) { + $base->set_base_vm(id_vm => $curr_node->id, user => user_admin); + } + + wait_request(); + my %found; + my %dupe; + my $n_expected = 0; + map { $n_expected+= $_ } @$number; + + my %devices_nodes = $hd->list_devices_nodes(); + for my $n (1 .. $n_expected) { + diag("$n of $n_expected"); + my $name = new_domain_name; + my $req = Ravada::Request->clone( + uid => user_admin->id + ,id_domain => $base->id + ,name => $name + ,start => 1 + ); + wait_request( check_error => 0); + my $domain = rvd_back->search_domain($name); + $domain->_data('status','active'); + is($domain->is_active,1) if $vm->type eq 'Void'; + check_hd_from_node($domain,\%devices_nodes); + my $hd = check_host_device($domain); + push(@{$dupe{$hd}},($base->name." ".$base->id)); + is(scalar(@{$dupe{$hd}}),1) or die Dumper(\%dupe); + $found{$domain->_data('id_vm')}++; + } + test_clone_nohd($base, $hd); + die Dumper(\%found); + + remove_domain($base); +} + sub test_assign($vm, $node, $hd, $n_expected_in_vm, $n_expected_in_node) { my $base = create_domain($vm); $base->add_host_device($hd); @@ -141,7 +211,8 @@ sub test_assign($vm, $node, $hd, $n_expected_in_vm, $n_expected_in_node) { my %dupe; my %devices_nodes = $hd->list_devices_nodes(); - for (0 .. $n_expected_in_vm+$n_expected_in_node) { + for my $n (1 .. $n_expected_in_vm+$n_expected_in_node) { + diag("$n [$n_expected_in_vm + $n_expected_in_node]"); my $name = new_domain_name; my $req = Ravada::Request->clone( uid => user_admin->id @@ -166,7 +237,7 @@ sub test_assign($vm, $node, $hd, $n_expected_in_vm, $n_expected_in_node) { is($found_in_node, $n_expected_in_node); is($found_in_vm, $n_expected_in_vm); - test_clone_nohd($base); + test_clone_nohd($base, $hd); remove_domain($base2, $base); } @@ -178,17 +249,20 @@ sub check_hd_from_node($domain, $devices_node) { my @devices = $domain->list_host_devices_attached(); my ($locked) = grep { $_->{is_locked} } @devices; - warn Dumper($locked); + diag("locked=".($locked->{is_locked} or 0)." id_vm=$id_vm ".$locked->{name}); + ok($locked) or return; my $vm = Ravada::VM->open($id_vm); my $devices = $devices_node->{$vm->name}; my ($match) = grep { $_ eq $locked->{name} } @$devices; ok($match,"Expecting $match in ".Dumper($devices)); - exit; } -sub test_clone_nohd($base) { +sub test_clone_nohd($base, $hd) { + + my ($clone_hd) = $base->clones; + my $name = new_domain_name(); my $req0 = Ravada::Request->clone( uid => user_admin->id @@ -221,6 +295,16 @@ sub test_clone_nohd($base) { my $domain2 = rvd_back->search_domain($name); is($domain2->is_active,1); + Ravada::Request->shutdown_domain( uid => user_admin->id, id_domain => $domain2->id); + Ravada::Request->shutdown_domain( uid => user_admin->id, id_domain => $clone_hd->{id}); + wait_request(); + + $req2->status('requested'); + wait_request(); + check_host_device($domain2); + + my %devices_nodes = $hd->list_devices_nodes(); + check_hd_from_node($domain2,\%devices_nodes); } sub check_host_device($domain) { @@ -316,9 +400,15 @@ for my $vm_name (reverse vm_names() ) { my $node = remote_node($vm_name) or next; clean_remote_node($node); + my ($node1, $node2) = remote_node_2($vm_name); + test_devices_v2([$vm,$node1,$node2],[6,6,6]); + exit; + test_devices($vm, $node,2,2); test_devices($vm, $node,3,1); test_devices($vm, $node,1,3); + test_devices($vm, $node,6,5); + test_devices($vm, $node,8,7); clean_remote_node($node); From 88e5333dc90f13ce4baf79a7837bce13bf1cdac8 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 6 May 2024 18:32:48 +0200 Subject: [PATCH 25/50] wip: test with three nodes --- lib/Ravada/Domain.pm | 3 +- t/device/50_nodes.t | 99 +++++++++++++++++++++++++++++++++++--------- 2 files changed, 81 insertions(+), 21 deletions(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index f8e7da007..49dcdadd5 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -7255,7 +7255,8 @@ sub _add_host_devices($self, @args) { if (!$device) { $self->_data(status => 'down'); $self->_unlock_host_devices(); - die "Error: No available devices in ".$host_device->name."\n"; + die "Error: [".$self->_vm->id.",".$self->_vm->name."]" + ." No available devices in ".$host_device->name."\n"; } } diff --git a/t/device/50_nodes.t b/t/device/50_nodes.t index ae953a6a2..5ca3689fe 100644 --- a/t/device/50_nodes.t +++ b/t/device/50_nodes.t @@ -167,14 +167,7 @@ sub test_assign_v2($hd, $node, $number) { for my $n (1 .. $n_expected) { diag("$n of $n_expected"); my $name = new_domain_name; - my $req = Ravada::Request->clone( - uid => user_admin->id - ,id_domain => $base->id - ,name => $name - ,start => 1 - ); - wait_request( check_error => 0); - my $domain = rvd_back->search_domain($name); + my $domain = _req_clone($base, $name); $domain->_data('status','active'); is($domain->is_active,1) if $vm->type eq 'Void'; check_hd_from_node($domain,\%devices_nodes); @@ -183,12 +176,74 @@ sub test_assign_v2($hd, $node, $number) { is(scalar(@{$dupe{$hd}}),1) or die Dumper(\%dupe); $found{$domain->_data('id_vm')}++; } - test_clone_nohd($base, $hd); - die Dumper(\%found); + test_clone_nohd($hd, $base); + test_start_in_another_node($hd, $base); remove_domain($base); } +sub test_start_in_another_node($hd, $base) { + my ($clone1, $clone2); + for my $clone ($base->clones) { + next if $clone->{status} ne 'active'; + if (!defined $clone1) { + $clone1 = $clone; + next; + } + if ($clone->{id_vm} != $clone1->{id_vm}) { + $clone2 = $clone; + last; + } + } + die "Error. I couldn't find a clone in each node" unless $clone1 && $clone2; + + _req_shutdown($clone1->{id}); + _req_clone($base); + _req_shutdown($clone2->{id}); + + _req_start($clone1->{id}); + + my $clone1b = Ravada::Domain->open($clone1->{id}); + is($clone1b->_data('id_vm'), $clone2->{id_vm}); + + my %devices_nodes = $hd->list_devices_nodes(); + check_hd_from_node($clone1b,\%devices_nodes); + +} + +sub _req_shutdown($id) { + Ravada::Request->force_shutdown_domain( + uid => user_admin->id + ,id_domain => $id + ); + wait_request(); +} + +sub _req_start($id) { + Ravada::Request->start_domain( + uid => user_admin->id + ,id_domain => $id + ); + wait_request(); +} + + +sub _req_clone($base, $name=undef) { + $name = new_domain_name() if !defined $name; + my $req = Ravada::Request->clone( + uid => user_admin->id + ,id_domain => $base->id + ,name => $name + ,start => 1 + ); + wait_request(); + + my $domain = rvd_back->search_domain($name); + + die "Error: $name not created" if !$domain; + return $domain; +} + sub test_assign($vm, $node, $hd, $n_expected_in_vm, $n_expected_in_node) { my $base = create_domain($vm); $base->add_host_device($hd); @@ -237,7 +292,7 @@ sub test_assign($vm, $node, $hd, $n_expected_in_vm, $n_expected_in_node) { is($found_in_node, $n_expected_in_node); is($found_in_vm, $n_expected_in_vm); - test_clone_nohd($base, $hd); + test_clone_nohd($hd, $base); remove_domain($base2, $base); } @@ -249,7 +304,7 @@ sub check_hd_from_node($domain, $devices_node) { my @devices = $domain->list_host_devices_attached(); my ($locked) = grep { $_->{is_locked} } @devices; - diag("locked=".($locked->{is_locked} or 0)." id_vm=$id_vm ".$locked->{name}); + diag($domain->name." locked=".($locked->{is_locked} or 0)." id_vm=$id_vm ".$locked->{name}); ok($locked) or return; my $vm = Ravada::VM->open($id_vm); @@ -259,7 +314,7 @@ sub check_hd_from_node($domain, $devices_node) { ok($match,"Expecting $match in ".Dumper($devices)); } -sub test_clone_nohd($base, $hd) { +sub test_clone_nohd($hd, $base) { my ($clone_hd) = $base->clones; @@ -295,16 +350,18 @@ sub test_clone_nohd($base, $hd) { my $domain2 = rvd_back->search_domain($name); is($domain2->is_active,1); - Ravada::Request->shutdown_domain( uid => user_admin->id, id_domain => $domain2->id); - Ravada::Request->shutdown_domain( uid => user_admin->id, id_domain => $clone_hd->{id}); + Ravada::Request->force_shutdown_domain( uid => user_admin->id, id_domain => $domain2->id); + Ravada::Request->force_shutdown_domain( uid => user_admin->id, id_domain => $clone_hd->{id}); wait_request(); - $req2->status('requested'); - wait_request(); - check_host_device($domain2); + _req_start($domain0->id); + + my $domain2b = rvd_back->search_domain($name); + is($domain2b->is_active,1); + check_host_device($domain2b); my %devices_nodes = $hd->list_devices_nodes(); - check_hd_from_node($domain2,\%devices_nodes); + check_hd_from_node($domain2b,\%devices_nodes); } sub check_host_device($domain) { @@ -312,7 +369,7 @@ sub check_host_device($domain) { ." WHERE id_domain=?"); $sth->execute($domain->id); my $found = $sth->fetchrow_hashref; - ok($found); + ok($found) or confess "Domain ".$domain->name." has no hds locked"; if ($domain->type eq 'Void') { return check_host_device_void($domain); } else { @@ -401,6 +458,8 @@ for my $vm_name (reverse vm_names() ) { clean_remote_node($node); my ($node1, $node2) = remote_node_2($vm_name); + test_devices_v2([$vm,$node1,$node2],[1,1,1]); + test_devices_v2([$vm,$node1,$node2],[2,2,2]); test_devices_v2([$vm,$node1,$node2],[6,6,6]); exit; From 7738fe40fb39a3d2a52f554ad064e7824ed38190 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Tue, 7 May 2024 12:19:52 +0200 Subject: [PATCH 26/50] wip: change VM when no hostdev avails --- lib/Ravada/Domain.pm | 19 +++++++++++++++++++ lib/Ravada/VM.pm | 2 +- t/device/50_nodes.t | 38 +++++++++++++++++++++++++++++--------- 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index 49dcdadd5..e83295566 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -474,11 +474,15 @@ sub _start_checks($self, @args) { if !$id_vm && defined $self->_data('id_vm') && $self->_data('id_vm') != $vm_local->id; + # check the requested id_vm is suitable if ($id_vm) { $vm = Ravada::VM->open($id_vm); if ( !$vm->enabled || !$vm->ping ) { $vm = $vm_local; $id_vm = undef; + } elsif ($enable_host_devices && !$self->_available_hds($vm)) { + $vm = $vm_local; + $id_vm = undef; } } @@ -577,6 +581,21 @@ sub _search_already_started($self, $fast = 0) { return keys %started; } +sub _available_hds($self, $vm) { + + my @host_devices = $self->list_host_devices(); + return 1 if !@host_devices; + + my $available=1; + for my $hd (@host_devices) { + if (! $hd->list_available_devices($vm->id) ) { + $available=0; + last; + } + } + return $available; +} + sub _filter_vm_available_hd($self, @vms) { my @host_devices = $self->list_host_devices(); diff --git a/lib/Ravada/VM.pm b/lib/Ravada/VM.pm index ecd679091..0f3487b18 100644 --- a/lib/Ravada/VM.pm +++ b/lib/Ravada/VM.pm @@ -2065,12 +2065,12 @@ sub balance_vm($self, $uid, $base=undef, $id_domain=undef, $host_devices=undef) @vms = $self->list_nodes(); } - my @vms_active; for my $vm (@vms) { push @vms_active,($vm) if $vm && $vm->vm && $vm->is_active && $vm->enabled; } return $vms_active[0] if scalar(@vms_active)==1; + if ($base && $base->_data('balance_policy') == 1 ) { my $vm = $self->_balance_already_started($uid, $id_domain, \@vms_active); return $vm if $vm; diff --git a/t/device/50_nodes.t b/t/device/50_nodes.t index 5ca3689fe..0325d58f5 100644 --- a/t/device/50_nodes.t +++ b/t/device/50_nodes.t @@ -103,7 +103,7 @@ sub test_devices_v2($node, $number) { test_assign_v2($hd,$node,$number); - _clean_devices($vm, $node); + _clean_devices(@$node); } sub test_devices($vm, $node, $n_local=3, $n_node=3) { @@ -165,7 +165,6 @@ sub test_assign_v2($hd, $node, $number) { my %devices_nodes = $hd->list_devices_nodes(); for my $n (1 .. $n_expected) { - diag("$n of $n_expected"); my $name = new_domain_name; my $domain = _req_clone($base, $name); $domain->_data('status','active'); @@ -212,11 +211,21 @@ sub test_start_in_another_node($hd, $base) { } sub _req_shutdown($id) { - Ravada::Request->force_shutdown_domain( + my $sth_locked = connector->dbh->prepare( + "SELECT * FROM host_devices_domain_locked " + ." WHERE id_domain=?" + ); + $sth_locked->execute($id); + my ($locked) = $sth_locked->fetchrow; + my $req = Ravada::Request->force_shutdown_domain( uid => user_admin->id ,id_domain => $id ); wait_request(); + return if !$locked; + sleep 3; + $req->status('requested'); + wait_request(debug => 0); } sub _req_start($id) { @@ -267,7 +276,6 @@ sub test_assign($vm, $node, $hd, $n_expected_in_vm, $n_expected_in_node) { my %devices_nodes = $hd->list_devices_nodes(); for my $n (1 .. $n_expected_in_vm+$n_expected_in_node) { - diag("$n [$n_expected_in_vm + $n_expected_in_node]"); my $name = new_domain_name; my $req = Ravada::Request->clone( uid => user_admin->id @@ -288,7 +296,6 @@ sub test_assign($vm, $node, $hd, $n_expected_in_vm, $n_expected_in_node) { } ok($found_in_node,"Expecting in node, found $found_in_node"); ok($found_in_vm,"Expecting in node, found $found_in_vm"); - diag("In node: $found_in_node, in vm: $found_in_vm"); is($found_in_node, $n_expected_in_node); is($found_in_vm, $n_expected_in_vm); @@ -304,14 +311,13 @@ sub check_hd_from_node($domain, $devices_node) { my @devices = $domain->list_host_devices_attached(); my ($locked) = grep { $_->{is_locked} } @devices; - diag($domain->name." locked=".($locked->{is_locked} or 0)." id_vm=$id_vm ".$locked->{name}); ok($locked) or return; my $vm = Ravada::VM->open($id_vm); my $devices = $devices_node->{$vm->name}; my ($match) = grep { $_ eq $locked->{name} } @$devices; - ok($match,"Expecting $match in ".Dumper($devices)); + ok($match,"Expecting $locked->{name} in ".Dumper($devices)); } sub test_clone_nohd($hd, $base) { @@ -350,8 +356,12 @@ sub test_clone_nohd($hd, $base) { my $domain2 = rvd_back->search_domain($name); is($domain2->is_active,1); - Ravada::Request->force_shutdown_domain( uid => user_admin->id, id_domain => $domain2->id); - Ravada::Request->force_shutdown_domain( uid => user_admin->id, id_domain => $clone_hd->{id}); + _req_shutdown($domain2->id); + _req_shutdown($clone_hd->{id}); + + _check_no_hd_locked($domain2->id); + _check_no_hd_locked($clone_hd->{id}); + wait_request(); _req_start($domain0->id); @@ -364,6 +374,16 @@ sub test_clone_nohd($hd, $base) { check_hd_from_node($domain2b,\%devices_nodes); } +sub _check_no_hd_locked($id_domain) { + my $sth = connector->dbh->prepare( + "SELECT * FROM host_devices_domain_locked " + ." WHERE id_domain=?" + ); + $sth->execute($id_domain); + my $row = $sth->fetchrow_hashref; + ok(!$row) or die Dumper($row); +} + sub check_host_device($domain) { my $sth = connector->dbh->prepare("SELECT * FROM host_devices_domain_locked " ." WHERE id_domain=?"); From 37174fe4551bc4e5f121cc4abc33a18ffdc759b4 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Tue, 7 May 2024 12:35:11 +0200 Subject: [PATCH 27/50] wip: check used host devices by id --- lib/Ravada.pm | 1 - lib/Ravada/HostDevice.pm | 2 +- lib/Ravada/WebSocket.pm | 19 ++++++++++++------- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index d700eccae..91c1ffac6 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -1593,7 +1593,6 @@ sub _add_indexes_generic($self) { ,host_devices_domain_locked => [ "unique(id_vm,name)" ,"index(id_domain)" - ,"unique(name)" ], ,messages => [ "index(id_user)" diff --git a/lib/Ravada/HostDevice.pm b/lib/Ravada/HostDevice.pm index 3697adc52..207bb9ea7 100644 --- a/lib/Ravada/HostDevice.pm +++ b/lib/Ravada/HostDevice.pm @@ -111,7 +111,7 @@ sub list_devices_nodes($self) { }; warn $@ if $@; # push @devices, @current_devs; - $devices{$node->name}=\@current_devs; + $devices{$node->id}=\@current_devs; } $self->_data( devices_node => \%devices ); diff --git a/lib/Ravada/WebSocket.pm b/lib/Ravada/WebSocket.pm index 3a9c1bfb4..097928f5e 100644 --- a/lib/Ravada/WebSocket.pm +++ b/lib/Ravada/WebSocket.pm @@ -267,14 +267,17 @@ sub _list_devices_node($rvd, $row) { $row->{_n_devices} += scalar(@{$devices->{$_}}); } $row->{_loading} = 0; - for my $node ( keys %$devices ) { + for my $id_node ( keys %$devices ) { my @devs; - for my $name ( @{$devices->{$node}} ) { + for my $name ( @{$devices->{$id_node}} ) { my $dev = { name => $name }; - $dev->{domain} = $attached{$name} if exists $attached{$name}; + + $dev->{domain} = $attached{"$id_node.$name"} + if exists $attached{"$id_node.$name"}; + push @devs,($dev); } - $ret{$node} = \@devs; + $ret{$id_node} = \@devs; } } else { $row->{_nodes} = []; @@ -284,7 +287,9 @@ sub _list_devices_node($rvd, $row) { } sub _list_devices_attached($rvd) { - my $sth=$rvd->_dbh->prepare("SELECT d.id,d.name,d.is_base, d.status, l.id, l.name " + my $sth=$rvd->_dbh->prepare( + "SELECT d.id,d.name,d.is_base, d.status, l.id, l.name " + ." ,l.id_vm " ." FROM host_devices_domain hdd, domains d" ." LEFT JOIN host_devices_domain_locked l" ." ON d.id=l.id_domain " @@ -293,13 +298,13 @@ sub _list_devices_attached($rvd) { ); $sth->execute(); my %devices; - while ( my ($id,$name,$is_base, $status, $is_locked, $device) = $sth->fetchrow ) { + while ( my ($id,$name,$is_base, $status, $is_locked, $device, $id_vm) = $sth->fetchrow ) { next if !$device; $is_locked = 0 if !$is_locked || $status ne 'active'; my $domain = { id => $id ,name => $name, is_locked => $is_locked ,is_base => $is_base ,device => $device }; - $devices{$device} = $domain; + $devices{"$id_vm.$device"} = $domain; } return %devices; From 602d622bfed2ee95769c05e67d99499318452867 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Thu, 9 May 2024 12:04:11 +0200 Subject: [PATCH 28/50] wip: check assigned hd is in the right node issue #2035 --- lib/Ravada/Domain.pm | 9 +++++++++ t/device/50_nodes.t | 41 ++++++++++++++++++++++++++++++++++------- 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index e83295566..e9a824d80 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -7255,6 +7255,7 @@ sub _add_host_devices($self, @args) { $request = delete $args{request} if exists $args{request}; } + $self->_clean_old_hd_locks(); my $doc = $self->get_config(); for my $host_device ( @host_devices ) { my $device_configured = $self->_device_already_configured($host_device); @@ -7377,6 +7378,14 @@ sub _lock_host_device($self, $host_device, $device=undef) { return 1; } +sub _clean_old_hd_locks($self) { + my $sth = $$CONNECTOR->dbh->prepare("DELETE FROM host_devices_domain_locked " + ." WHERE id_domain=? AND id_vm <> ?" + ); + $sth->execute($self->id, $self->_vm->id); + +} + sub _unlock_host_devices($self, $time_changed=3) { my $sth = $$CONNECTOR->dbh->prepare("DELETE FROM host_devices_domain_locked " ." WHERE id_domain=? AND time_changed<=?" diff --git a/t/device/50_nodes.t b/t/device/50_nodes.t index 0325d58f5..6bf2147e4 100644 --- a/t/device/50_nodes.t +++ b/t/device/50_nodes.t @@ -200,6 +200,8 @@ sub test_start_in_another_node($hd, $base) { _req_clone($base); _req_shutdown($clone2->{id}); + _list_locked($clone1->{id}); + _force_wrong_lock($clone1->{id_vm},$clone1->{id}); _req_start($clone1->{id}); my $clone1b = Ravada::Domain->open($clone1->{id}); @@ -208,6 +210,24 @@ sub test_start_in_another_node($hd, $base) { my %devices_nodes = $hd->list_devices_nodes(); check_hd_from_node($clone1b,\%devices_nodes); + check_host_device($clone1b); +} + +sub _force_wrong_lock($id_vm, $id_domain) { + my $sth = connector->dbh->prepare( + "INSERT INTO host_devices_domain_locked " + ." ( id_vm, id_domain, name, time_changed )" + ." values (?, ?, ?, 0) " + ); + $sth->execute($id_vm, $id_domain, 'fake'); +} + +sub _list_locked($id_domain) { + my $sth = connector->dbh->prepare("SELECT * FROM host_devices_domain_locked WHERE id_domain=?"); + $sth->execute($id_domain); + while ( my $row = $sth->fetchrow_hashref ) { + warn Dumper($row); + } } sub _req_shutdown($id) { @@ -223,7 +243,7 @@ sub _req_shutdown($id) { ); wait_request(); return if !$locked; - sleep 3; + sleep 4; $req->status('requested'); wait_request(debug => 0); } @@ -309,15 +329,19 @@ sub check_hd_from_node($domain, $devices_node) { is($domain->_vm->id,$id_vm); my @devices = $domain->list_host_devices_attached(); - my ($locked) = grep { $_->{is_locked} } @devices; + my @locked = grep { $_->{is_locked} } @devices; - ok($locked) or return; + ok(@locked) or return; + is(scalar(@locked),1) or die Dumper(\@locked); + my ($locked) = @locked; my $vm = Ravada::VM->open($id_vm); - my $devices = $devices_node->{$vm->name}; + diag("Checking ".$domain->name." in node " + ." [ ".$vm->id." ] ".$vm->name); + my $devices = $devices_node->{$vm->id}; my ($match) = grep { $_ eq $locked->{name} } @$devices; - ok($match,"Expecting $locked->{name} in ".Dumper($devices)); + ok($match,"Expecting $locked->{name} in ".Dumper($devices)) or confess; } sub test_clone_nohd($hd, $base) { @@ -388,8 +412,11 @@ sub check_host_device($domain) { my $sth = connector->dbh->prepare("SELECT * FROM host_devices_domain_locked " ." WHERE id_domain=?"); $sth->execute($domain->id); - my $found = $sth->fetchrow_hashref; - ok($found) or confess "Domain ".$domain->name." has no hds locked"; + my @found; + while ( my $row = $sth->fetchrow_hashref) { + push @found,($row); + } + is(scalar(@found),1) or confess "Domain ".$domain->name." should have 1 HD locked\n".Dumper(\@found); if ($domain->type eq 'Void') { return check_host_device_void($domain); } else { From f3646968618320e1a7a31090609e6eaf76a17a2e Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Thu, 9 May 2024 16:12:59 +0200 Subject: [PATCH 29/50] wip: testing already configured devs --- lib/Ravada/Domain.pm | 13 +++++++++++++ lib/Ravada/VM.pm | 1 + t/device/50_nodes.t | 6 +++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index e9a824d80..7a6e1cdb5 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -7261,6 +7261,7 @@ sub _add_host_devices($self, @args) { my $device_configured = $self->_device_already_configured($host_device); if ( $device_configured ) { + warn "device already configured $device_configured"; if ( $host_device->enabled() && $host_device->is_device($device_configured) && $self->_lock_host_device($host_device) ) { next; } else { @@ -7362,6 +7363,18 @@ sub _lock_host_device($self, $host_device, $device=undef) { my $query = "INSERT INTO host_devices_domain_locked (id_domain,id_vm,name,time_changed) VALUES(?,?,?,?)"; + if ( $self->_vm->type eq 'Void' ) { + my $vm_name = $self->_vm->name; + confess Dumper( + [ $self->id + ,$self->name + ,$self->_vm->id + ,$self->_vm->name + ]) + if $device !~ /$vm_name$/; + } + warn Dumper(["Locking hd [".$self->id."]".$self->name, $self->_vm->id,$self->_vm->name,$device]); + my $sth = $$CONNECTOR->dbh->prepare($query); eval { $sth->execute($self->id,$self->_vm->id, $device,time) }; if ($@) { diff --git a/lib/Ravada/VM.pm b/lib/Ravada/VM.pm index 0f3487b18..c7cbacfe1 100644 --- a/lib/Ravada/VM.pm +++ b/lib/Ravada/VM.pm @@ -2703,6 +2703,7 @@ sub add_host_device($self, %args) { ." VALUES ( ".join(", ",map { '?' } keys %$info)." ) " ; + warn Dumper($info); my $sth = $$CONNECTOR->dbh->prepare($query); $sth->execute(map { $info->{$_} } sort keys %$info ); diff --git a/t/device/50_nodes.t b/t/device/50_nodes.t index 6bf2147e4..26a797e8d 100644 --- a/t/device/50_nodes.t +++ b/t/device/50_nodes.t @@ -508,13 +508,17 @@ for my $vm_name (reverse vm_names() ) { test_devices_v2([$vm,$node1,$node2],[1,1,1]); test_devices_v2([$vm,$node1,$node2],[2,2,2]); test_devices_v2([$vm,$node1,$node2],[6,6,6]); - exit; + test_devices_v2([$vm,$node1,$node2],[6,1,1]); + test_devices_v2([$vm,$node1,$node2],[1,6,1]); + test_devices_v2([$vm,$node1,$node2],[1,1,6]); test_devices($vm, $node,2,2); test_devices($vm, $node,3,1); test_devices($vm, $node,1,3); test_devices($vm, $node,6,5); test_devices($vm, $node,8,7); + test_devices($vm, $node,1,7); + test_devices($vm, $node,7,1); clean_remote_node($node); From d1d9797bcb6f25b9fa5edbad11870785e170d85d Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Fri, 10 May 2024 11:23:53 +0200 Subject: [PATCH 30/50] wip: check previous device belongs to current node issue #2035 --- lib/Ravada/Domain.pm | 8 ++++++-- lib/Ravada/HostDevice.pm | 4 ++-- templates/main/admin_hostdev.html.ep | 1 - 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index 7a6e1cdb5..0bb6aa4d7 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -7261,8 +7261,11 @@ sub _add_host_devices($self, @args) { my $device_configured = $self->_device_already_configured($host_device); if ( $device_configured ) { - warn "device already configured $device_configured"; - if ( $host_device->enabled() && $host_device->is_device($device_configured) && $self->_lock_host_device($host_device) ) { + warn "device already configured $device_configured "; + warn "current vm ".$self->_vm->id." ".$self->_vm->name; + if ( $host_device->enabled() + && $host_device->is_device($device_configured, $self->_vm->id) + && $self->_lock_host_device($host_device) ) { next; } else { $self->_dettach_host_device($host_device, $doc, $device_configured); @@ -7370,6 +7373,7 @@ sub _lock_host_device($self, $host_device, $device=undef) { ,$self->name ,$self->_vm->id ,$self->_vm->name + ,$device ]) if $device !~ /$vm_name$/; } diff --git a/lib/Ravada/HostDevice.pm b/lib/Ravada/HostDevice.pm index 207bb9ea7..b8db27e1e 100644 --- a/lib/Ravada/HostDevice.pm +++ b/lib/Ravada/HostDevice.pm @@ -137,9 +137,9 @@ sub list_devices($self, $id_vm=$self->id_vm) { return @device; } -sub is_device($self, $device) { +sub is_device($self, $device, $id_vm) { return if !defined $device; - for my $dev ( $self->list_devices ) { + for my $dev ( $self->list_devices($id_vm) ) { return 1 if $dev eq $device; } return 0; diff --git a/templates/main/admin_hostdev.html.ep b/templates/main/admin_hostdev.html.ep index cd23bb0eb..c4fba9950 100644 --- a/templates/main/admin_hostdev.html.ep +++ b/templates/main/admin_hostdev.html.ep @@ -85,7 +85,6 @@ ng-disabled="hdev._loading" ><%=l 'apply' %> -
              No devices found
              • From 96628e4fea6c689286d2d76f1e68be2d8e3aecfb Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Fri, 10 May 2024 11:36:19 +0200 Subject: [PATCH 31/50] wip: try better to check if xml is equal --- lib/Ravada/Domain/KVM.pm | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/Ravada/Domain/KVM.pm b/lib/Ravada/Domain/KVM.pm index 0a86f5fc3..2ddba6e77 100644 --- a/lib/Ravada/Domain/KVM.pm +++ b/lib/Ravada/Domain/KVM.pm @@ -3921,6 +3921,12 @@ sub remove_config_node($self, $path, $content, $doc) { sub _xml_equal_hostdev($doc1, $doc2) { return 1 if $doc1 eq $doc2; + + $doc1 =~ s/\n//g; + $doc2 =~ s/\n//g; + + return 1 if $doc1 eq $doc2; + my $parser = XML::LibXML->new() or die $!; $doc1 =~ s{(parse_string($doc1); From fa0abe0c871ada4c38f21ca3eb3b13e438773b7c Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Fri, 10 May 2024 15:39:25 +0200 Subject: [PATCH 32/50] wip: remove test hd --- lib/Ravada/VM.pm | 4 +++- t/device/50_nodes.t | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/Ravada/VM.pm b/lib/Ravada/VM.pm index c7cbacfe1..955563ec5 100644 --- a/lib/Ravada/VM.pm +++ b/lib/Ravada/VM.pm @@ -2703,9 +2703,11 @@ sub add_host_device($self, %args) { ." VALUES ( ".join(", ",map { '?' } keys %$info)." ) " ; - warn Dumper($info); my $sth = $$CONNECTOR->dbh->prepare($query); + eval { $sth->execute(map { $info->{$_} } sort keys %$info ); + }; + confess Dumper([$info,$@]) if $@; my $id = Ravada::Request->_last_insert_id( $$CONNECTOR ); diff --git a/t/device/50_nodes.t b/t/device/50_nodes.t index 26a797e8d..30a386023 100644 --- a/t/device/50_nodes.t +++ b/t/device/50_nodes.t @@ -104,6 +104,7 @@ sub test_devices_v2($node, $number) { test_assign_v2($hd,$node,$number); _clean_devices(@$node); + $hd->remove(); } sub test_devices($vm, $node, $n_local=3, $n_node=3) { @@ -141,6 +142,8 @@ sub test_devices($vm, $node, $n_local=3, $n_node=3) { test_assign($vm, $node, $hd, $n_local, $n_node); _clean_devices($vm, $node); + + $hd->remove(); } sub test_assign_v2($hd, $node, $number) { From 5cad17b8884434aa393ba32ca1d99274b17fbe26 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Tue, 14 May 2024 12:06:00 +0200 Subject: [PATCH 33/50] wip(test): check KVM mocking USB devices --- lib/Ravada/Domain.pm | 4 --- lib/Ravada/VM.pm | 2 +- t/device/50_nodes.t | 75 +++++++++++++++++++++++++++++--------------- 3 files changed, 50 insertions(+), 31 deletions(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index 0bb6aa4d7..b49a4ba1e 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -7261,8 +7261,6 @@ sub _add_host_devices($self, @args) { my $device_configured = $self->_device_already_configured($host_device); if ( $device_configured ) { - warn "device already configured $device_configured "; - warn "current vm ".$self->_vm->id." ".$self->_vm->name; if ( $host_device->enabled() && $host_device->is_device($device_configured, $self->_vm->id) && $self->_lock_host_device($host_device) ) { @@ -7377,8 +7375,6 @@ sub _lock_host_device($self, $host_device, $device=undef) { ]) if $device !~ /$vm_name$/; } - warn Dumper(["Locking hd [".$self->id."]".$self->name, $self->_vm->id,$self->_vm->name,$device]); - my $sth = $$CONNECTOR->dbh->prepare($query); eval { $sth->execute($self->id,$self->_vm->id, $device,time) }; if ($@) { diff --git a/lib/Ravada/VM.pm b/lib/Ravada/VM.pm index 955563ec5..6d6262c64 100644 --- a/lib/Ravada/VM.pm +++ b/lib/Ravada/VM.pm @@ -2659,7 +2659,7 @@ sub _check_equal_storage_pools($vm1, $vm2) { my ($path1, $path2) = ($vm1->_storage_path($pool), $vm2->_storage_path($pool)); - die "Error: Storage pool '$pool' different. In ".$vm1->name." $path1 , " + confess "Error: Storage pool '$pool' different. In ".$vm1->name." $path1 , " ." in ".$vm2->name." $path2" if $path1 ne $path2; } return 1; diff --git a/t/device/50_nodes.t b/t/device/50_nodes.t index 30a386023..5a7b997be 100644 --- a/t/device/50_nodes.t +++ b/t/device/50_nodes.t @@ -17,12 +17,14 @@ no warnings "experimental::signatures"; use feature qw(signatures); my $N_DEVICE = 0; +my $MOCK_DEVICES = 1; my $PATH = "/var/tmp/$run_command("mkdir","-p",$PATH) if !$vm->file_exists($PATH); my $name = base_domain_name()."_${type} ID"; @@ -105,6 +107,7 @@ sub test_devices_v2($node, $number) { _clean_devices(@$node); $hd->remove(); + } sub test_devices($vm, $node, $n_local=3, $n_node=3) { @@ -126,14 +129,12 @@ sub test_devices($vm, $node, $n_local=3, $n_node=3) { my $node_name = $node->name; my %devices_nodes = $hd->list_devices_nodes(); - warn Dumper(\%devices_nodes); my %dupe; for my $node (keys %devices_nodes) { for my $dev (@{$devices_nodes{$node}}) { $dupe{$dev}++; } } - warn Dumper(\%dupe); is(scalar(keys %dupe), $n_local+ $n_node); for my $dev (keys %dupe) { is($dupe{$dev},1); @@ -144,6 +145,7 @@ sub test_devices($vm, $node, $n_local=3, $n_node=3) { _clean_devices($vm, $node); $hd->remove(); + } sub test_assign_v2($hd, $node, $number) { @@ -187,7 +189,9 @@ sub test_assign_v2($hd, $node, $number) { sub test_start_in_another_node($hd, $base) { my ($clone1, $clone2); for my $clone ($base->clones) { - next if $clone->{status} ne 'active'; + next if $clone->{status} ne 'active' + && !( $base->type eq 'KVM' && $MOCK_DEVICES ); + if (!defined $clone1) { $clone1 = $clone; next; @@ -203,7 +207,7 @@ sub test_start_in_another_node($hd, $base) { _req_clone($base); _req_shutdown($clone2->{id}); - _list_locked($clone1->{id}); + #_list_locked($clone1->{id}); _force_wrong_lock($clone1->{id_vm},$clone1->{id}); _req_start($clone1->{id}); @@ -252,6 +256,13 @@ sub _req_shutdown($id) { } sub _req_start($id) { + + my $domain = Ravada::Domain->open($id); + + _mock_start($domain); + + return if $domain->type eq 'KVM' && $MOCK_DEVICES; + Ravada::Request->start_domain( uid => user_admin->id ,id_domain => $id @@ -262,20 +273,38 @@ sub _req_start($id) { sub _req_clone($base, $name=undef) { $name = new_domain_name() if !defined $name; + my $start=1; + $start=0 if $base->type eq 'KVM' && $MOCK_DEVICES; my $req = Ravada::Request->clone( uid => user_admin->id ,id_domain => $base->id ,name => $name - ,start => 1 + ,start => $start ); - wait_request(); + wait_request(debug => 1); my $domain = rvd_back->search_domain($name); - die "Error: $name not created" if !$domain; + + _mock_start($domain); + return $domain; } +sub _mock_start($domain) { + return unless $domain->type eq 'KVM' && $MOCK_DEVICES; + + eval { + $domain->_start_checks( enable_host_devices => 1 ); + }; + my $error = $@; + return if ($error && $error =~ /No host devices available/); + + warn $error if $error; + $domain->_add_host_devices(); + $domain->_data('status' => 'active'); +} + sub test_assign($vm, $node, $hd, $n_expected_in_vm, $n_expected_in_node) { my $base = create_domain($vm); $base->add_host_device($hd); @@ -339,8 +368,6 @@ sub check_hd_from_node($domain, $devices_node) { my ($locked) = @locked; my $vm = Ravada::VM->open($id_vm); - diag("Checking ".$domain->name." in node " - ." [ ".$vm->id." ] ".$vm->name); my $devices = $devices_node->{$vm->id}; my ($match) = grep { $_ eq $locked->{name} } @$devices; @@ -394,7 +421,7 @@ sub test_clone_nohd($hd, $base) { _req_start($domain0->id); my $domain2b = rvd_back->search_domain($name); - is($domain2b->is_active,1); + is($domain2b->is_active,1) unless $domain2b->type eq 'KVM' && $MOCK_DEVICES; check_host_device($domain2b); my %devices_nodes = $hd->list_devices_nodes(); @@ -487,7 +514,7 @@ sub _clean_devices(@nodes) { init(); clean(); -for my $vm_name (reverse vm_names() ) { +for my $vm_name (vm_names() ) { my $vm; eval { $vm = rvd_back->search_vm($vm_name) }; @@ -504,27 +531,23 @@ for my $vm_name (reverse vm_names() ) { diag("Testing host devices in $vm_name"); - my $node = remote_node($vm_name) or next; - clean_remote_node($node); - my ($node1, $node2) = remote_node_2($vm_name); + clean_remote_node($node1, $node2); + test_devices_v2([$vm,$node1,$node2],[1,1,1]); - test_devices_v2([$vm,$node1,$node2],[2,2,2]); - test_devices_v2([$vm,$node1,$node2],[6,6,6]); - test_devices_v2([$vm,$node1,$node2],[6,1,1]); - test_devices_v2([$vm,$node1,$node2],[1,6,1]); - test_devices_v2([$vm,$node1,$node2],[1,1,6]); - - test_devices($vm, $node,2,2); - test_devices($vm, $node,3,1); - test_devices($vm, $node,1,3); - test_devices($vm, $node,6,5); - test_devices($vm, $node,8,7); + # test_devices_v2([$vm,$node1,$node2],[1,6,1]); + # test_devices_v2([$vm,$node1,$node2],[1,1,6]); + + clean_remote_node($node1, $node2); + + my $node = remote_node($vm_name) or next; + clean_remote_node($node); test_devices($vm, $node,1,7); - test_devices($vm, $node,7,1); + # test_devices($vm, $node,7,1); clean_remote_node($node); + $MOCK_DEVICES=0; } } From de48e38a2cfb53d29f29c1e15081274093e5c972 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Tue, 14 May 2024 12:29:11 +0200 Subject: [PATCH 34/50] wip: properly test --- t/device/50_nodes.t | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/t/device/50_nodes.t b/t/device/50_nodes.t index 5a7b997be..5953b174b 100644 --- a/t/device/50_nodes.t +++ b/t/device/50_nodes.t @@ -535,17 +535,22 @@ for my $vm_name (vm_names() ) { clean_remote_node($node1, $node2); test_devices_v2([$vm,$node1,$node2],[1,1,1]); - # test_devices_v2([$vm,$node1,$node2],[1,6,1]); - # test_devices_v2([$vm,$node1,$node2],[1,1,6]); + test_devices_v2([$vm,$node1,$node2],[1,3,1]); + test_devices_v2([$vm,$node1,$node2],[1,1,3]); clean_remote_node($node1, $node2); + $node1->remove(); + $node2->remove(); - my $node = remote_node($vm_name) or next; - clean_remote_node($node); - test_devices($vm, $node,1,7); - # test_devices($vm, $node,7,1); + if ($ENV{TEST_LONG}) { + my $node = remote_node($vm_name) or next; + clean_remote_node($node); + test_devices($vm, $node,1,3); + test_devices($vm, $node,3,1); - clean_remote_node($node); + clean_remote_node($node); + $node->remove(); + } $MOCK_DEVICES=0; } From aea7a205efad879c891d624046680d8f29e3fc15 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Fri, 17 May 2024 11:12:57 +0200 Subject: [PATCH 35/50] wip: show nodes name --- lib/Ravada/Domain.pm | 1 - lib/Ravada/Front.pm | 29 ++++++ public/js/admin.js | 8 ++ script/rvd_front | 7 ++ t/lib/Test/Ravada.pm | 3 +- templates/main/admin_hostdev.html.ep | 2 +- templates/main/node_hostdev.html.ep | 134 +-------------------------- 7 files changed, 48 insertions(+), 136 deletions(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index 0e64a8efd..65e48e7af 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -7438,7 +7438,6 @@ sub _check_host_device_already_used($self, $device) { ; my $sth = $$CONNECTOR->dbh->prepare($query); $sth->execute($self->_data('id_vm'), $device); - my ($id_domain) = $sth->fetchrow; my ($id_domain,$time_changed) = $sth->fetchrow; # warn "\n".($id_domain or '')." [".$self->id."] had locked $device\n"; diff --git a/lib/Ravada/Front.pm b/lib/Ravada/Front.pm index 4386afa56..02ba8784a 100644 --- a/lib/Ravada/Front.pm +++ b/lib/Ravada/Front.pm @@ -618,6 +618,35 @@ sub list_vms($self, $type=undef) { return @list; } +=head2 list_nodes_by_id + +Returns a list of Nodes by id + +=cut + +sub list_nodes_by_id($self, $type=undef) { + + my $sql = "SELECT id,name,hostname,is_active, vm_type, enabled FROM vms "; + + my @args = (); + if ($type) { + $sql .= "WHERE (vm_type=? or vm_type=?)"; + my $type2 = $type; + $type2 = 'qemu' if $type eq 'KVM'; + @args = ( $type, $type2); + } + my $sth = $CONNECTOR->dbh->prepare($sql." ORDER BY vm_type,name"); + $sth->execute(@args); + + my %list; + while (my $row = $sth->fetchrow_hashref) { + $list{$row->{id}}= $row->{name}; + } + $sth->finish; + return \%list; +} + + sub _list_bases_vm($self, $id_node) { my $sth = $CONNECTOR->dbh->prepare( "SELECT d.id FROM domains d,bases_vm bv" diff --git a/public/js/admin.js b/public/js/admin.js index fe4e0de09..cbeb60d2e 100644 --- a/public/js/admin.js +++ b/public/js/admin.js @@ -1208,8 +1208,16 @@ ravadaApp.directive("solShowMachine", swMach) subscribe_list_host_devices(id, url); list_templates(id); list_backends(); + list_nodes(); }; + list_nodes=function() { + $http.get('/list_nodes_by_id.json') + .then(function(response) { + $scope.nodes = response.data; + }); + }; + list_backends=function() { $http.get('/list_vm_types.json') .then(function(response) { diff --git a/script/rvd_front b/script/rvd_front index 716c3be7b..41d495206 100644 --- a/script/rvd_front +++ b/script/rvd_front @@ -545,6 +545,13 @@ get '/list_nodes.json' => sub { $c->render(json => [$RAVADA->list_vms]); }; +get '/list_nodes_by_id.json' => sub { + my $c = shift; + return _access_denied($c) if !$USER || !$USER->is_admin; + + $c->render(json => $RAVADA->list_nodes_by_id); +}; + get '/list_host_devices/(:id_vm)' => sub($c) { $c->render(json => Ravada::WebSocket::_list_host_devices($RAVADA , { diff --git a/t/lib/Test/Ravada.pm b/t/lib/Test/Ravada.pm index 651dd9676..3ea1ac497 100644 --- a/t/lib/Test/Ravada.pm +++ b/t/lib/Test/Ravada.pm @@ -2223,7 +2223,7 @@ sub start_node($node) { for my $try ( 1 .. 3) { my $is_active; - for ( 1 .. 60 ) { + for ( 1 .. 90 ) { eval { $node->disconnect; $node->clear_netssh(); @@ -2272,6 +2272,7 @@ sub start_node($node) { for ( reverse 1 .. 120 ) { $node->is_active(1); $node->enabled(1); + next if !$node2; $node2 = Ravada::VM->open(id => $node->id); last if $node2->is_active(1) && $node2->ip && $node2->_ssh; diag("Waiting for node ".$node2->name." active ... $_") if !($_ % 10); diff --git a/templates/main/admin_hostdev.html.ep b/templates/main/admin_hostdev.html.ep index c4fba9950..d7584bfae 100644 --- a/templates/main/admin_hostdev.html.ep +++ b/templates/main/admin_hostdev.html.ep @@ -88,7 +88,7 @@
                • - {{node}} + {{nodes[node]}}
                  • <%=l 'No devices found'%> diff --git a/templates/main/node_hostdev.html.ep b/templates/main/node_hostdev.html.ep index 360e50210..6ecf5ddb8 100644 --- a/templates/main/node_hostdev.html.ep +++ b/templates/main/node_hostdev.html.ep @@ -1,133 +1 @@ - -
                    - - - - - -
                    - -
                    - -
                    - - - - - - {{hdev.name}} - - - -
                    -
                    - This host device is configured in these virtual machines -
                      -
                    • {{name}}
                    • -
                    -
                    - <%=l ' Are you sure you want to remove this host device ?' %> - - -
                    - - - - - -
                    - list command: - filter: - -
                    No devices found
                    -
                    - -
                      -
                    • - {{node}} - -
                    • -
                    - -
                    - Bases - - -
                    - -
                    - - -
                    - -
                    - Machines - - -
                    -
                    -
                    - - -
                    -
                    - {{domain.name}} - {{domain.name}} - - {{domain.device}} -
                    -
                    -
                    - -
                    - - -
                    -
                    +<%=l 'Host Devices' %> From cc8c864dadb6ff2047c0719a5b3ea156b2c3d5cd Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Fri, 17 May 2024 13:01:25 +0200 Subject: [PATCH 36/50] wip: check the HD is in the current node --- lib/Ravada/Domain.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index 65e48e7af..78438f3e6 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -7311,13 +7311,13 @@ sub _attach_host_devices($self, @args) { } sub _search_free_device($self, $host_device) { - my ($device) = $host_device->list_available_devices(); + my ($device) = $host_device->list_available_devices($self->_data('id_vm')); if ( !$device ) { $device = _refresh_domains_with_locked_devices($host_device); if (!$device) { $self->_data(status => 'down'); $self->_unlock_host_devices(); - die "Error: No available devices in ".$host_device->name."\n"; + die "Error: No available devices in ".$self->_vm->name." for ".$host_device->name."\n"; } } return $device; From 59fa1a5f71ea81dbd728dc054de2f04c821a7998 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Tue, 21 May 2024 11:05:49 +0200 Subject: [PATCH 37/50] wip: remove old requests and improved tests for nodes --- lib/Ravada/Domain.pm | 12 +--- lib/Ravada/Domain/KVM.pm | 10 +++ lib/Ravada/Front.pm | 1 - lib/Ravada/HostDevice.pm | 19 +++++- lib/Ravada/Request.pm | 46 +++++++++++++ lib/Ravada/WebSocket.pm | 4 +- t/device/00_host_device.t | 11 +++- t/device/10_templates.t | 135 +++++++++++++++++++++++++++----------- t/device/20_misc.t | 5 +- t/device/30_pci.t | 3 +- t/lib/Test/Ravada.pm | 5 +- 11 files changed, 186 insertions(+), 65 deletions(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index 78438f3e6..4c6ac367e 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -7328,6 +7328,7 @@ sub _dettach_host_devices($self) { for my $host_device ( @host_devices ) { $self->_dettach_host_device($host_device); } + $self->_unlock_host_devices(); $self->_restore_config_no_hd(); } @@ -7378,17 +7379,6 @@ sub _lock_host_device($self, $host_device, $device=undef) { my $query = "INSERT INTO host_devices_domain_locked (id_domain,id_vm,name,time_changed) VALUES(?,?,?,?)"; - if ( $self->_vm->type eq 'Void' ) { - my $vm_name = $self->_vm->name; - confess Dumper( - [ $self->id - ,$self->name - ,$self->_vm->id - ,$self->_vm->name - ,$device - ]) - if $device !~ /$vm_name$/; - } my $sth = $$CONNECTOR->dbh->prepare($query); my $id_vm = $self->_data('id_vm'); $id_vm = $self->_vm->id if !$id_vm; diff --git a/lib/Ravada/Domain/KVM.pm b/lib/Ravada/Domain/KVM.pm index c8be6f937..d3e398eca 100644 --- a/lib/Ravada/Domain/KVM.pm +++ b/lib/Ravada/Domain/KVM.pm @@ -3650,12 +3650,22 @@ sub _validate_xml($self, $doc) { } } +sub _fix_uuid($self, $doc) { + my ($uuid) = $doc->findnodes("/domain/uuid/text()"); + confess "I cant'find /domain/uuid in ".$self->name if !$uuid; + + $uuid->setData($self->domain->get_uuid_string); + +} + sub reload_config($self, $doc) { if (!ref($doc)) { $doc = XML::LibXML->load_xml(string => $doc); } $self->_validate_xml($doc) if $self->_vm->vm->get_major_version >= 4; + $self->_fix_uuid($doc); + my $new_domain; eval { diff --git a/lib/Ravada/Front.pm b/lib/Ravada/Front.pm index 02ba8784a..30002b6ef 100644 --- a/lib/Ravada/Front.pm +++ b/lib/Ravada/Front.pm @@ -1654,7 +1654,6 @@ Update the host device information, then it requests a list of the current avail sub update_host_device($self, $args) { my $id = delete $args->{id} or die "Error: missing id ".Dumper($args); Ravada::Utils::check_sql_valid_params(keys %$args); - $args->{devices} = undef; my $query = "UPDATE host_devices SET ".join(" , ", map { "$_=?" } sort keys %$args); $query .= " WHERE id=?"; my $sth = $self->_dbh->prepare($query); diff --git a/lib/Ravada/HostDevice.pm b/lib/Ravada/HostDevice.pm index b8db27e1e..45803ef2d 100644 --- a/lib/Ravada/HostDevice.pm +++ b/lib/Ravada/HostDevice.pm @@ -167,17 +167,21 @@ sub list_available_devices($self, $id_vm=$self->id_vm) { sub remove($self) { _init_connector(); + my $id = $self->id; my $sth = $$CONNECTOR->dbh->prepare("SELECT id_domain FROM host_devices_domain " ." WHERE id_host_device=?" ); - $sth->execute($self->id); + $sth->execute($id); while ( my ( $id_domain ) = $sth->fetchrow) { my $domain = Ravada::Domain->open($id_domain); $domain->remove_host_device($self); } + $sth = $$CONNECTOR->dbh->prepare("DELETE FROM host_devices WHERE id=?"); - $sth->execute($self->id); + $sth->execute($id); + + Ravada::Request::remove('requested', id_host_device => $id ); } sub _fetch_template_args($self, $device) { @@ -249,7 +253,16 @@ sub _data($self, $field, $value=undef) { ); $sth->execute($value, $self->id); $self->meta->get_attribute($field)->set_value($self, $value); - $self->_dettach_in_domains() if $field =~ /^(devices|list_)/; + if ( $field =~ /^(devices|list_)/ ) { + $self->_dettach_in_domains(); + if ($field =~ /^list_/) { + Ravada::Request->list_host_devices( + uid => Ravada::Utils::user_daemon->id + ,id_host_device => $self->id + ); + $self->_data('devices_node' => ''); + } + } return $value; } else { my $sth = $$CONNECTOR->dbh->prepare("SELECT * FROM host_devices" diff --git a/lib/Ravada/Request.pm b/lib/Ravada/Request.pm index 67473ed26..833e2c59b 100644 --- a/lib/Ravada/Request.pm +++ b/lib/Ravada/Request.pm @@ -1822,6 +1822,52 @@ sub redo($self) { $sth->execute($self->id); } +sub remove($status, %args) { + my $sth = _dbh->prepare( + "SELECT * FROM requests where status = ? " + ); + $sth->execute($status); + + my $sth_del = _dbh->prepare("DELETE FROM requests where id=?"); + + while ( my $row = $sth->fetchrow_hashref ) { + my $req_args = {}; + delete $req_args->{uid}; + + eval { + $req_args = decode_json($row->{args}) if $row->{args}; + }; + warn "Warning: $@ ".$row->{args} + ."\n".Dumper($row) if $@; + + next if scalar(keys%args) != scalar(keys(%$req_args)); + + my $found = 1; + for my $key (%$req_args) { + + next if exists $args{$key} + && !defined $args{$key} && !defined $req_args->{$key}; + + $found=0 if + !exists $args{$key} || + $args{$key} ne $req_args->{$key}; + } + next if !$found; + + for my $key (%args) { + next if exists $req_args->{$key} + && !defined $args{$key} && !defined $req_args->{$key}; + + $found=0 if + !exists $req_args->{$key} || + $args{$key} ne $req_args->{$key}; + } + next if !$found; + + $sth_del->execute($row->{id}); + } +} + sub AUTOLOAD { my $self = shift; diff --git a/lib/Ravada/WebSocket.pm b/lib/Ravada/WebSocket.pm index 097928f5e..45a2a90de 100644 --- a/lib/Ravada/WebSocket.pm +++ b/lib/Ravada/WebSocket.pm @@ -231,7 +231,7 @@ sub _list_host_devices($rvd, $args) { my $user = Ravada::Auth::SQL->new(name => $login) or die "Error: uknown user $login"; - my $sth = $rvd->_dbh->prepare( "SELECT id,name,list_command,list_filter,devices,devices_node,date_changed " + my $sth = $rvd->_dbh->prepare( "SELECT id,name,list_command,list_filter,devices_node,date_changed " ." FROM host_devices WHERE id_vm=?"); $sth->execute($id_vm); @@ -240,7 +240,6 @@ sub _list_host_devices($rvd, $args) { while (my $row = $sth->fetchrow_hashref) { _list_domains_with_device($rvd, $row); _list_devices_node($rvd, $row); - warn Dumper([$row->{devices_node},$@]) if $@; push @found, $row; next unless _its_been_a_while_channel($args->{channel}); my $req = Ravada::Request->list_host_devices( @@ -347,7 +346,6 @@ sub _list_domains_with_device($rvd,$row) { $row->{_domains} = \@domains; $row->{_bases} = \@bases; - $row->{devices} = [values %devices]; } sub _get_domain_with_device($rvd, $dev) { diff --git a/t/device/00_host_device.t b/t/device/00_host_device.t index 89946bd36..8366d80b0 100644 --- a/t/device/00_host_device.t +++ b/t/device/00_host_device.t @@ -306,7 +306,7 @@ sub test_host_device_usb($vm) { ); my @list_hostdev_c = $clone->list_host_devices(); is(scalar @list_hostdev_c, 1) or exit; - my $device = $list_hostdev_c[0]->{devices}; + my ($device) = $list_hostdev_c[0]->list_devices; test_kvm_usb_template_args($device, $list_hostdev_c[0]); @@ -342,6 +342,8 @@ sub test_host_device_usb($vm) { } sub test_kvm_usb_template_args($device_usb, $hostdev) { + + confess "Error: undefined device_usb " if !defined $device_usb; my ($bus, $device, $vendor_id, $product_id) = $device_usb =~ /Bus 0*([0-9a-f]+) Device 0*([0-9a-f]+).*ID 0*([0-9a-f]+):0*([0-9a-f]+) /; my $args = $hostdev->_fetch_template_args($device_usb); @@ -424,10 +426,12 @@ sub test_host_device_usb_mock($vm, $n_hd=1) { } sleep 2; $clones[0]->shutdown_now(user_admin); + sleep 1; + $clones[0]->shutdown_now(user_admin); _check_hostdev($clones[0], 0); my @devs_attached = $clones[0]->list_host_devices_attached(); is(scalar(@devs_attached), $n_hd); - is($devs_attached[0]->{is_locked},0); + is($devs_attached[0]->{is_locked},0) or die Dumper(\@devs_attached); for (@list_hostdev) { $_->_data('enabled' => 0 ); @@ -602,11 +606,12 @@ sub test_check_list_command($vm) { } } - for my $something ('lssomething' , 'findsomething') { + for my $something ('ls' , 'find') { $hdev->_data('list_command' => $something); is($hdev->list_command, $something); is($hdev->_data('list_command'), $something); } + wait_request(); $hdev->remove(); } diff --git a/t/device/10_templates.t b/t/device/10_templates.t index df500cb12..348613470 100644 --- a/t/device/10_templates.t +++ b/t/device/10_templates.t @@ -122,9 +122,21 @@ sub _create_domain_hd($vm, $hd) { return $domain if $vm->type ne 'KVM'; + if ($domain->type eq 'KVM' && $hd->{name} =~ /USB/) { + _req_add_usb($domain); + } return $domain; } +sub _req_add_usb($domain) { + Ravada::Request->add_hardware( + uid => user_admin->id + ,id_domain => $domain->id + ,name => 'usb controller' + ); + wait_request(debug => 0); +} + sub _shutdown_all($vm) { for my $dom ($vm->list_domains) { $dom->shutdown_now(user_admin); @@ -372,6 +384,8 @@ sub test_templates_start_nohd($vm) { my @list_hostdev = $vm->list_host_devices(); my ($hd) = $list_hostdev[-1]; + next if !config_host_devices($hd->name,0); + _fix_host_device($hd) if $vm->type eq 'KVM'; next if !$hd->list_devices; @@ -611,13 +625,22 @@ sub _mangle_dom_hd_kvm($domain) { } sub _create_host_devices($vm, $n) { + my @hd; if ($vm->type eq 'Void') { - return _create_host_devices_void($vm,$n); + @hd = _create_host_devices_void($vm,$n); } elsif ($vm->type eq 'KVM') { - return _create_host_devices_kvm($vm,$n); + @hd = _create_host_devices_kvm($vm,$n); } else { die "Error: I don't know how to create host devices for ".$vm->type; } + wait_request(debug => 0); + for my $hd (@hd) { + my $devices_node = $hd->_data('devices_node'); + die unless $devices_node; + my $data = decode_json($devices_node); + die unless keys %$data; + } + return @hd; } sub _create_host_devices_void($vm, $n) { @@ -636,12 +659,16 @@ sub _create_host_devices_kvm($vm,$n) { my ($template) = grep { $_->{list_command} =~ /lspci/ } @$templates; + if (!config_host_devices($template->{name},0)) { + ($template) = grep { $_->{list_command} =~ /lsusb/ } @$templates; + } + my @hds; for ( 1 .. $n ) { my $id_hd = $vm->add_host_device(template => $template->{name}); my $hd = Ravada::HostDevice->search_by_id($id_hd); - my $config = config_host_devices('pci'); + my $config = config_host_devices($template->{name}); $hd->_data('list_filter' => $config); push @hds,($hd); } @@ -671,16 +698,22 @@ sub test_frontend_list($vm) { my ($dev_attached) = ($domain->list_host_devices_attached); my $found=0; + my $fd_found; for my $fd ( @$front_devices ) { - for my $dev ( @{$fd->{devices}} ) { - if ($dev->{name} eq $dev_attached->{name}) { - ok($dev->{domain} , "Expecting domains listed in ".$dev->{name}) or next; + next unless $fd->{name} eq $hd1->name; + $fd_found = $fd; + my $dn = $fd->{devices_node}; + for my $node (keys %$dn) { + for my $dev ( @{$dn->{$node}} ) { + next if !$dev->{domain}; is($dev->{domain}->{id}, $domain->id,"Expecting ".$domain->name." attached in ".$dev->{name}); + is($dev->{domain}->{device},$dev_attached->{name}); $found++ if $dev->{domain}->{id} == $domain->id; } } } - is($found,2) or die Dumper($front_devices); + is($found,1,"Expected device '".$hd1->name."' in " + .$domain->id) or die Dumper($fd_found); remove_domain($domain); @@ -746,12 +779,12 @@ sub test_templates_change_devices($vm) { my ($dev_attached) = ($domain->list_host_devices_attached); $domain->shutdown_now(user_admin); - is($hostdev->is_device($dev_attached->{name}),1) or exit; + is($hostdev->is_device($dev_attached->{name},$vm->id),1) or exit; my $file = "$path/".$dev_attached->{name}; unlink $file or die "$! $file"; - is($hostdev->is_device($dev_attached->{name}),0) or exit; + is($hostdev->is_device($dev_attached->{name}, $vm->id),0) or exit; $domain->start(user_admin); my ($dev_attached2) = ($domain->list_host_devices_attached); @@ -769,6 +802,7 @@ sub test_templates_change_filter($vm) { $vm->add_host_device(template => $first->{name}); my @list_hostdev = $vm->list_host_devices(); my ($hd) = $list_hostdev[-1]; + next if $vm->type eq 'KVM' && !config_host_devices($first->{name},0); _fix_host_device($hd) if $vm->type eq 'KVM'; @@ -806,6 +840,27 @@ sub _remove_host_devices($vm) { } } +sub _get_frontend_devices($vm, $id_hd) { + + my $ws_args = { + channel => '/'.$vm->id + ,login => user_admin->name + }; + my $front_devs = Ravada::WebSocket::_list_host_devices(rvd_front(), $ws_args); + my @devices; + my $n_hds = scalar(@$front_devs); + for my $curr_hd ( @$front_devs ) { + next unless $curr_hd->{id} == $id_hd; + for my $node ( keys %{$curr_hd->{devices_node}} ) { + my $dn = $curr_hd->{devices_node}->{$node}; + for my $dev (@$dn) { + push @devices, ($dev->{name}) + } + } + } + return ($n_hds, \@devices); +} + sub test_templates($vm) { my $templates = Ravada::HostDevice::Templates::list_templates($vm->type); ok(@$templates); @@ -830,6 +885,7 @@ sub test_templates($vm) { my $host_device = $list_hostdev[-1]; + next if $vm->type eq 'KVM' && !config_host_devices($host_device->{name},0); _fix_host_device($host_device) if $vm->type eq 'KVM'; test_hd_in_domain($vm, $host_device); @@ -842,45 +898,32 @@ sub test_templates($vm) { ); wait_request( debug => 0); is($req->status, 'done'); - my $ws_args = { - channel => '/'.$vm->id - ,login => user_admin->name - }; - my $devices = Ravada::WebSocket::_list_host_devices(rvd_front(), $ws_args); - is(scalar(@$devices), 2+$n) or die Dumper($devices, $host_device); + my ($n_hd,$devices) = _get_frontend_devices($vm, $host_device->id); + is($n_hd, 2+$n) or die Dumper($devices, $host_device); $n+=2; - next if !(scalar(@{$devices->[-1]->{devices}})>1); my $list_filter = $host_device->_data('list_filter'); $host_device->_data('list_filter' => 'fail match'); - my $req2 = Ravada::Request->list_host_devices( - uid => user_admin->id - ,id_host_device => $host_device->id - ,_force => 1 - ); - wait_request(); - is($req2->status, 'done'); - is($req2->error, ''); - my $devices2 = Ravada::WebSocket::_list_host_devices(rvd_front(), $ws_args); + wait_request(debug => 0); + my ($n_hd2, $devices2) = _get_frontend_devices($vm, $host_device->id); my $equal; - my $dev0 = $devices->[-1]->{devices}; - my $dev2 = $devices2->[-1]->{devices}; - $equal = scalar(@$dev0) == scalar (@$dev2); + $equal = scalar(@$devices) == scalar (@$devices2); if ($equal ) { - for ( 0 .. scalar(@$dev0)-1) { - if ($dev0->[$_] ne $dev2->[$_]) { + for ( 0 .. scalar(@$devices)-1) { + if ($devices->[$_] ne $devices2->[$_]) { $equal = 0; last; } } } - ok(!$equal) or die Dumper($dev0, $dev2); + ok(!$equal) or die Dumper($devices, $devices2); $host_device->_data('list_filter' => $list_filter); } my $n = $vm->list_host_devices; for my $hd ( $vm->list_host_devices ) { - _fix_host_device($hd); + _fix_host_device($hd) unless $vm->type eq 'KVM' && !config_host_devices($hd->{name},0); + diag("remove ".$hd->name); test_hd_remove($vm, $hd); is($vm->list_host_devices,--$n, $hd->name) or die Dumper([$vm->list_host_devices]); } @@ -907,8 +950,26 @@ sub test_hd_dettach($vm, $host_device) { $domain->remove(user_admin); } +sub _req_remove($host_device) { + + my $req = Ravada::Request->remove_host_device( + uid => user_admin->id + ,id_host_device => $host_device->id + ); + wait_request(debug => 0); + is($req->status,'done'); + is($req->error, '') or exit; + +} + sub test_hd_remove($vm, $host_device) { my $start_fails; + my $type = $host_device->{name}; + $type = 'usb' if $type =~ /usb/i; + if ( $vm->type eq 'KVM' && !config_host_devices($type,0)) { + _req_remove($host_device); + return; + } if ($host_device->name =~ /^PCI/ && $vm->type eq 'KVM' ) { _set_hd_nvidia($host_device); if (!$host_device->list_devices) { @@ -930,13 +991,7 @@ sub test_hd_remove($vm, $host_device) { } $domain->shutdown_now(user_admin); - my $req = Ravada::Request->remove_host_device( - uid => user_admin->id - ,id_host_device => $host_device->id - ); - wait_request(); - is($req->status,'done'); - is($req->error, '') or exit; + my $req = _req_remove($host_device); my $sth = connector->dbh->prepare( "SELECT * FROM host_devices WHERE id=?" @@ -958,7 +1013,7 @@ sub test_hd_remove($vm, $host_device) { clean(); -for my $vm_name (reverse vm_names()) { +for my $vm_name (vm_names()) { my $vm; eval { $vm = rvd_back->search_vm($vm_name) }; diff --git a/t/device/20_misc.t b/t/device/20_misc.t index dc79144f9..84c572b4c 100644 --- a/t/device/20_misc.t +++ b/t/device/20_misc.t @@ -230,10 +230,11 @@ clean(); for my $vm_name ( 'KVM' ) { SKIP: { - my $vm = rvd_back->search_vm($vm_name); + my $vm; + $vm = rvd_back->search_vm($vm_name) if !$>; my $msg = "SKIPPED test: No $vm_name VM found "; - if ($vm && $>) { + if ($vm_name eq 'KVM' && $>) { $msg = "SKIPPED: Test must run as root"; $vm = undef; } diff --git a/t/device/30_pci.t b/t/device/30_pci.t index e0e798bfd..746ebabad 100644 --- a/t/device/30_pci.t +++ b/t/device/30_pci.t @@ -62,7 +62,8 @@ clean(); for my $vm_name ( 'KVM' ) { SKIP: { - my $vm = rvd_back->search_vm($vm_name); + my $vm; + $vm = rvd_back->search_vm($vm_name) if !$<; my $msg = "SKIPPED test: No $vm_name VM found "; if ($vm && $>) { diff --git a/t/lib/Test/Ravada.pm b/t/lib/Test/Ravada.pm index 3ea1ac497..b298b4c07 100644 --- a/t/lib/Test/Ravada.pm +++ b/t/lib/Test/Ravada.pm @@ -193,7 +193,10 @@ sub config_host_devices($type, $die=1) { die "Error loading $FILE_CONFIG_HOST_DEVICES $@" if $@; - die "Error: no host devices config in $FILE_CONFIG_HOST_DEVICES for $type" + $type = lc($type); + $type = 'usb' if $type =~ /usb/i; + $type = 'pci' if $type =~ /pci/i; + confess "Error: no host devices config in $FILE_CONFIG_HOST_DEVICES for $type" if ( !exists $config->{$type} || !$config->{$type} ) && $die; return $config->{$type}; } From 9bfd085388fa53c547788755cddbc9d13c1c8526 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Wed, 22 May 2024 11:32:53 +0200 Subject: [PATCH 38/50] wip: clone volatile with HDs --- lib/Ravada/Domain.pm | 11 +++++++++-- lib/Ravada/VM.pm | 11 ++++++++++- lib/Ravada/VM/KVM.pm | 12 +++++++----- t/device/50_nodes.t | 9 ++++++--- t/lib/Test/Ravada.pm | 13 +++++++------ 5 files changed, 39 insertions(+), 17 deletions(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index 4c6ac367e..93c5d041f 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -332,7 +332,7 @@ sub _around_start($orig, $self, @arg) { next; } elsif ($error =~ /No free memory/) { warn $error; - die $error if $self->is_local; + die $error if $self->is_local || $self->is_volatile; my $vm_local = $self->_vm->new( host => 'localhost' ); $self->migrate($vm_local, $request); next; @@ -504,7 +504,8 @@ sub _start_checks($self, @args) { if ($id_vm) { $self->_set_vm($vm); } else { - $self->_balance_vm($request, $enable_host_devices); + $self->_balance_vm($request, $enable_host_devices) + if !$self->is_volatile; } if ( !$self->is_volatile && !$self->_vm->is_local() ) { if (!base_in_vm($self->id_base, $self->_vm->id)) { @@ -2981,6 +2982,7 @@ sub clone { $vm = $node if $node->is_local; } } + my $clone = $vm->create_domain( name => $name ,id_base => $self->id @@ -7260,6 +7262,10 @@ sub _restore_config_no_hd($self) { sub _attach_host_devices($self, @args) { my @host_devices = $self->list_host_devices(); + warn $self->name." attching hds ".scalar(@host_devices) + ." is_active=".$self->is_active() + ; + confess if $self->is_active; return if !@host_devices; return if $self->is_active(); @@ -7287,6 +7293,7 @@ sub _attach_host_devices($self, @args) { } } $device = $self->_search_free_device($host_device) if !$device; + warn $device; $self->_lock_host_device($host_device, $device); diff --git a/lib/Ravada/VM.pm b/lib/Ravada/VM.pm index 968a64788..61d9a64aa 100644 --- a/lib/Ravada/VM.pm +++ b/lib/Ravada/VM.pm @@ -494,7 +494,11 @@ sub _around_create_domain { unless $owner->allowed_access($base->id); $volatile = $base->volatile_clones if (! defined($volatile)); - $create_volatile=$volatile if !$base->list_host_devices(); + if ( $base->list_host_devices() ) { + $create_volatile=0; + } else { + $create_volatile=$volatile; + } if ($add_to_pool) { confess "Error: you can't add to pool and also pick from pool" if $from_pool; $from_pool = 0; @@ -532,7 +536,11 @@ sub _around_create_domain { $args_create{listen_ip} = $self->listen_ip($remote_ip); } + warn $args_create{name}." volatile=".($create_volatile or 0 ) + ." start=".($args_create{start} or 0 ); + my $domain = $self->$orig(%args_create, volatile => $create_volatile); + confess Dumper(\%args_create) if $domain->is_active; $self->_add_instance_db($domain->id); $domain->add_volume_swap( size => $swap ) if $swap; $domain->_data('is_compacted' => 1); @@ -550,6 +558,7 @@ sub _around_create_domain { $domain->expose(%port); } $base->_copy_host_devices($domain); + $domain->_attach_host_devices(); $domain->_clone_filesystems(); my @displays = $base->_get_controller_display(); for my $display (@displays) { diff --git a/lib/Ravada/VM/KVM.pm b/lib/Ravada/VM/KVM.pm index ef3487231..2c773cc75 100644 --- a/lib/Ravada/VM/KVM.pm +++ b/lib/Ravada/VM/KVM.pm @@ -1105,7 +1105,7 @@ sub _domain_create_common { my %args = @_; my $id_owner = delete $args{id_owner} or confess "ERROR: The id_owner is mandatory"; - my $is_volatile = delete $args{is_volatile}; + my $volatile = delete $args{volatile}; my $listen_ip = delete $args{listen_ip}; my $spice_password = delete $args{spice_password}; my $user = Ravada::Auth::SQL->search_by_id($id_owner) @@ -1133,7 +1133,7 @@ sub _domain_create_common { for ( 1 .. 10 ) { eval { - if ($user->is_temporary || $is_volatile && !$host_devices ) { + if ( $volatile) { $dom = $self->vm->create_domain($xml->toString()); } else { $dom = $self->vm->define_domain($xml->toString()); @@ -1168,7 +1168,7 @@ sub _domain_create_common { , domain => $dom , storage => $self->storage_pool , id_owner => $id_owner - , active => ($user->is_temporary || $is_volatile || $host_devices) + , active => $volatile ); return ($domain, $spice_password); } @@ -1254,9 +1254,11 @@ sub _domain_create_from_base { $base = $vm_local->_search_domain_by_id($args{id_base}) if $args{id_base}; confess "Unknown base id: $args{id_base}" if !$base; + confess Dumper(\%args) if $args{name} eq 'tst_device_50_nodes_01' + && ( !exists $args{volatile} || !defined $args{volatile}); + my $volatile = $base->volatile_clones; $volatile = delete $args{volatile} if exists $args{volatile} && defined $args{volatile}; - my $options = delete $args{options}; my $network = delete $options->{network}; @@ -1282,7 +1284,7 @@ sub _domain_create_from_base { $self->_xml_set_network($xml, $network) if $network; my ($domain, $spice_password) - = $self->_domain_create_common($xml,%args, is_volatile=>$volatile, base => $base); + = $self->_domain_create_common($xml,%args, volatile=>$volatile, base => $base); $domain->_insert_db(name=> $args{name}, id_base => $base->id, id_owner => $args{id_owner} , id_vm => $self->id ); diff --git a/t/device/50_nodes.t b/t/device/50_nodes.t index 5953b174b..0c4e9f87d 100644 --- a/t/device/50_nodes.t +++ b/t/device/50_nodes.t @@ -84,7 +84,7 @@ sub _create_mock_devices($vm, $n_devices, $type, $value="fff:fff") { } } -sub test_devices_v2($node, $number) { +sub test_devices_v2($node, $number, $volatile=0) { _clean_devices(@$node); my $vm = $node->[0]; my ($list_command,$list_filter) = _create_mock_devices($node->[0], $number->[0], "USB" ); @@ -103,7 +103,7 @@ sub test_devices_v2($node, $number) { my %devices_nodes = $hd->list_devices_nodes(); - test_assign_v2($hd,$node,$number); + test_assign_v2($hd,$node,$number, $volatile); _clean_devices(@$node); $hd->remove(); @@ -148,10 +148,11 @@ sub test_devices($vm, $node, $n_local=3, $n_node=3) { } -sub test_assign_v2($hd, $node, $number) { +sub test_assign_v2($hd, $node, $number, $volatile=0) { my $vm = $node->[0]; my $base = create_domain($vm); $base->add_host_device($hd); + $base->volatile_clones($volatile); Ravada::Request->add_hardware( uid => user_admin->id ,id_domain => $base->id @@ -534,6 +535,8 @@ for my $vm_name (vm_names() ) { my ($node1, $node2) = remote_node_2($vm_name); clean_remote_node($node1, $node2); + test_devices_v2([$vm,$node1,$node2],[1,1,1],1); + test_devices_v2([$vm,$node1,$node2],[1,1,1]); test_devices_v2([$vm,$node1,$node2],[1,3,1]); test_devices_v2([$vm,$node1,$node2],[1,1,3]); diff --git a/t/lib/Test/Ravada.pm b/t/lib/Test/Ravada.pm index b298b4c07..8c94b27d1 100644 --- a/t/lib/Test/Ravada.pm +++ b/t/lib/Test/Ravada.pm @@ -2275,13 +2275,14 @@ sub start_node($node) { for ( reverse 1 .. 120 ) { $node->is_active(1); $node->enabled(1); - next if !$node2; $node2 = Ravada::VM->open(id => $node->id); - last if $node2->is_active(1) && $node2->ip && $node2->_ssh; - diag("Waiting for node ".$node2->name." active ... $_") if !($_ % 10); - $node2->disconnect(); - $node2->connect(); - $node2->clear_netssh(); + if ($node2) { + last if $node2->is_active(1) && $node2->ip && $node2->_ssh; + diag("Waiting for node ".$node2->name." active ... $_") if !($_ % 10); + $node2->disconnect(); + $node2->connect(); + $node2->clear_netssh(); + } sleep 1; } eval { $node2->run_command("hwclock","--hctosys") }; From 0d91e0a6e82eb89fa3a5aae9cc4d06780f7fd3b5 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Wed, 22 May 2024 12:34:20 +0200 Subject: [PATCH 39/50] hostdevs in node no volatile by now --- lib/Ravada/Domain.pm | 5 ----- lib/Ravada/VM.pm | 17 +++++------------ t/device/50_nodes.t | 7 ++++++- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index 93c5d041f..99ee17526 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -7262,10 +7262,6 @@ sub _restore_config_no_hd($self) { sub _attach_host_devices($self, @args) { my @host_devices = $self->list_host_devices(); - warn $self->name." attching hds ".scalar(@host_devices) - ." is_active=".$self->is_active() - ; - confess if $self->is_active; return if !@host_devices; return if $self->is_active(); @@ -7293,7 +7289,6 @@ sub _attach_host_devices($self, @args) { } } $device = $self->_search_free_device($host_device) if !$device; - warn $device; $self->_lock_host_device($host_device, $device); diff --git a/lib/Ravada/VM.pm b/lib/Ravada/VM.pm index 61d9a64aa..e4789f549 100644 --- a/lib/Ravada/VM.pm +++ b/lib/Ravada/VM.pm @@ -483,7 +483,6 @@ sub _around_create_domain { confess "ERROR: Unknown args ".Dumper(\%args) if keys %args; $self->_check_duplicate_name($name); - my $create_volatile; if ($id_base) { my $vm_local = $self; $vm_local = $self->new( host => 'localhost') if !$vm_local->is_local; @@ -493,11 +492,10 @@ sub _around_create_domain { die "Error: user ".$owner->name." can not clone from ".$base->name unless $owner->allowed_access($base->id); - $volatile = $base->volatile_clones if (! defined($volatile)); if ( $base->list_host_devices() ) { - $create_volatile=0; - } else { - $create_volatile=$volatile; + $args_create{volatile}=0; + } elsif (!defined $args_create{volatile}) { + $args_create{volatile} = $base->volatile_clones; } if ($add_to_pool) { confess "Error: you can't add to pool and also pick from pool" if $from_pool; @@ -527,8 +525,7 @@ sub _around_create_domain { return $base->_search_pool_clone($owner) if $from_pool; - if ($self->is_local && $base && $base->is_base - && ( $volatile || $owner->is_temporary )) { + if ($self->is_local && $base && $base->is_base) { $request->status("balancing") if $request; my $vm = $self->balance_vm($owner->id, $base) or die "Error: No free nodes available."; $request->status("creating machine on ".$vm->name) if $request; @@ -536,11 +533,7 @@ sub _around_create_domain { $args_create{listen_ip} = $self->listen_ip($remote_ip); } - warn $args_create{name}." volatile=".($create_volatile or 0 ) - ." start=".($args_create{start} or 0 ); - - my $domain = $self->$orig(%args_create, volatile => $create_volatile); - confess Dumper(\%args_create) if $domain->is_active; + my $domain = $self->$orig(%args_create); $self->_add_instance_db($domain->id); $domain->add_volume_swap( size => $swap ) if $swap; $domain->_data('is_compacted' => 1); diff --git a/t/device/50_nodes.t b/t/device/50_nodes.t index 0c4e9f87d..59ebc075d 100644 --- a/t/device/50_nodes.t +++ b/t/device/50_nodes.t @@ -282,7 +282,12 @@ sub _req_clone($base, $name=undef) { ,name => $name ,start => $start ); - wait_request(debug => 1); + wait_request(debug => 1, check_error => 0); + if ($base->type eq 'KVM' && $MOCK_DEVICES) { + diag($req->error); + } else { + is($req->error, ''); + } my $domain = rvd_back->search_domain($name); die "Error: $name not created" if !$domain; From ea5d88112acbd1f982d75e15436d639e2bc5b537 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Wed, 22 May 2024 16:22:16 +0200 Subject: [PATCH 40/50] wip: balance volatile before --- lib/Ravada/VM.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Ravada/VM.pm b/lib/Ravada/VM.pm index e4789f549..6045e83ad 100644 --- a/lib/Ravada/VM.pm +++ b/lib/Ravada/VM.pm @@ -525,7 +525,7 @@ sub _around_create_domain { return $base->_search_pool_clone($owner) if $from_pool; - if ($self->is_local && $base && $base->is_base) { + if ($self->is_local && $base && $base->is_base && $args_create{volatile}) { $request->status("balancing") if $request; my $vm = $self->balance_vm($owner->id, $base) or die "Error: No free nodes available."; $request->status("creating machine on ".$vm->name) if $request; From d52273a512aab0c54a3feb29bea7757487a083d0 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Thu, 23 May 2024 11:26:21 +0200 Subject: [PATCH 41/50] wip: testing volatile impossible with mock devs in KVM --- t/device/50_nodes.t | 27 ++++++++++++++------------- t/kvm/n10_nodes.t | 2 -- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/t/device/50_nodes.t b/t/device/50_nodes.t index 59ebc075d..fa745d9eb 100644 --- a/t/device/50_nodes.t +++ b/t/device/50_nodes.t @@ -176,10 +176,14 @@ sub test_assign_v2($hd, $node, $number, $volatile=0) { $domain->_data('status','active'); is($domain->is_active,1) if $vm->type eq 'Void'; check_hd_from_node($domain,\%devices_nodes); - my $hd = check_host_device($domain); - push(@{$dupe{$hd}},($base->name." ".$base->id)); - is(scalar(@{$dupe{$hd}}),1) or die Dumper(\%dupe); - $found{$domain->_data('id_vm')}++; + my $hd_checked = check_host_device($domain); + next if $MOCK_DEVICES; + diag($hd_checked); + push(@{$dupe{$hd_checked}},($domain->name." ".$base->id)); + is(scalar(@{$dupe{$hd_checked}}),1) or die Dumper(\%dupe,\%devices_nodes); + my $id_vm = $domain->_data('id_vm'); + $found{$id_vm}++; + is($found{$id_vm},1) or die Dumper(\%found); } test_clone_nohd($hd, $base); test_start_in_another_node($hd, $base); @@ -188,6 +192,8 @@ sub test_assign_v2($hd, $node, $number, $volatile=0) { } sub test_start_in_another_node($hd, $base) { + return if $base->volatile_clones && $MOCK_DEVICES; + my ($clone1, $clone2); for my $clone ($base->clones) { next if $clone->{status} ne 'active' @@ -282,7 +288,7 @@ sub _req_clone($base, $name=undef) { ,name => $name ,start => $start ); - wait_request(debug => 1, check_error => 0); + wait_request(debug => 0, check_error => 0); if ($base->type eq 'KVM' && $MOCK_DEVICES) { diag($req->error); } else { @@ -335,14 +341,7 @@ sub test_assign($vm, $node, $hd, $n_expected_in_vm, $n_expected_in_node) { my %devices_nodes = $hd->list_devices_nodes(); for my $n (1 .. $n_expected_in_vm+$n_expected_in_node) { my $name = new_domain_name; - my $req = Ravada::Request->clone( - uid => user_admin->id - ,id_domain => $base->id - ,name => $name - ,start => 1 - ); - wait_request( check_error => 0); - my $domain = rvd_back->search_domain($name); + my $domain = _req_clone($base, $name); $domain->_data('status','active'); is($domain->is_active,1) if $vm->type eq 'Void'; check_hd_from_node($domain,\%devices_nodes); @@ -381,6 +380,7 @@ sub check_hd_from_node($domain, $devices_node) { } sub test_clone_nohd($hd, $base) { + return if $base->volatile_clones && $MOCK_DEVICES; my ($clone_hd) = $base->clones; @@ -392,6 +392,7 @@ sub test_clone_nohd($hd, $base) { ,start => 0 ); wait_request(); + my $domain0 = rvd_back->search_domain($name); my $req = Ravada::Request->start_domain( uid => user_admin->id diff --git a/t/kvm/n10_nodes.t b/t/kvm/n10_nodes.t index 7b1a88ab5..ec5dcae7c 100644 --- a/t/kvm/n10_nodes.t +++ b/t/kvm/n10_nodes.t @@ -1183,8 +1183,6 @@ SKIP: { test_bases_node($vm_name, $node); test_clone_make_base($vm_name, $node); - test_migrate_back($node); - test_sync_base($vm_name, $node); test_sync_back($node); From 960d186217ca6fad7e4ced132e690bdce4a9f03d Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Fri, 24 May 2024 10:56:44 +0200 Subject: [PATCH 42/50] wip: test attaching in nodes --- lib/Ravada/Domain.pm | 7 +++++-- lib/Ravada/VM.pm | 1 - t/device/50_nodes.t | 26 ++++++++++++++++++-------- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index 99ee17526..f3f264837 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -338,6 +338,7 @@ sub _around_start($orig, $self, @arg) { next; } } + warn $error if $error; die $error if $error; if (!defined $listen_ip) { my $display_ip; @@ -7375,6 +7376,10 @@ sub _lock_host_device($self, $host_device, $device=undef) { } my $id_domain_locked = $self->_check_host_device_already_used($device); + + my $id_vm = $self->_data('id_vm'); + $id_vm = $self->_vm->id if !$id_vm; + return 1 if defined $id_domain_locked && $self->id == $id_domain_locked; return 0 if defined $id_domain_locked; @@ -7382,8 +7387,6 @@ sub _lock_host_device($self, $host_device, $device=undef) { my $query = "INSERT INTO host_devices_domain_locked (id_domain,id_vm,name,time_changed) VALUES(?,?,?,?)"; my $sth = $$CONNECTOR->dbh->prepare($query); - my $id_vm = $self->_data('id_vm'); - $id_vm = $self->_vm->id if !$id_vm; cluck if !$id_vm; eval { $sth->execute($self->id,$id_vm, $device,time) }; if ($@) { diff --git a/lib/Ravada/VM.pm b/lib/Ravada/VM.pm index 6045e83ad..c057ede04 100644 --- a/lib/Ravada/VM.pm +++ b/lib/Ravada/VM.pm @@ -551,7 +551,6 @@ sub _around_create_domain { $domain->expose(%port); } $base->_copy_host_devices($domain); - $domain->_attach_host_devices(); $domain->_clone_filesystems(); my @displays = $base->_get_controller_display(); for my $display (@displays) { diff --git a/t/device/50_nodes.t b/t/device/50_nodes.t index fa745d9eb..b2eeebb5a 100644 --- a/t/device/50_nodes.t +++ b/t/device/50_nodes.t @@ -7,6 +7,7 @@ use File::Path qw(make_path); use IPC::Run3 qw(run3); use Mojo::JSON qw(decode_json encode_json); use Ravada::HostDevice::Templates; +use Ravada::WebSocket; use Test::More; use YAML qw( Dump ); @@ -24,7 +25,6 @@ my $PATH = "/var/tmp/$run_command("mkdir","-p",$PATH) if !$vm->file_exists($PATH); my $name = base_domain_name()."_${type} ID"; @@ -77,6 +77,9 @@ sub _create_mock_devices_kvm($vm, $n_devices, $type, $value="fff:fff") { } sub _create_mock_devices($vm, $n_devices, $type, $value="fff:fff") { + + $MOCK_DEVICES=1; + if ($vm->type eq 'KVM') { return _create_mock_devices_kvm($vm, $n_devices, $type, $value ); } elsif ($vm->type eq 'Void') { @@ -169,16 +172,22 @@ sub test_assign_v2($hd, $node, $number, $volatile=0) { my $n_expected = 0; map { $n_expected+= $_ } @$number; + my $list_nodes = [ map { {$_->id, $_->name} } @$node]; + my %devices_nodes = $hd->list_devices_nodes(); + my $ws = { channel => "/".$vm->id + ,login => user_admin->name + }; for my $n (1 .. $n_expected) { + + my $fd = Ravada::WebSocket::_list_host_devices(rvd_front(),$ws); + my $name = new_domain_name; my $domain = _req_clone($base, $name); - $domain->_data('status','active'); is($domain->is_active,1) if $vm->type eq 'Void'; check_hd_from_node($domain,\%devices_nodes); my $hd_checked = check_host_device($domain); next if $MOCK_DEVICES; - diag($hd_checked); push(@{$dupe{$hd_checked}},($domain->name." ".$base->id)); is(scalar(@{$dupe{$hd_checked}}),1) or die Dumper(\%dupe,\%devices_nodes); my $id_vm = $domain->_data('id_vm'); @@ -192,7 +201,7 @@ sub test_assign_v2($hd, $node, $number, $volatile=0) { } sub test_start_in_another_node($hd, $base) { - return if $base->volatile_clones && $MOCK_DEVICES; + return if $base->volatile_clones; my ($clone1, $clone2); for my $clone ($base->clones) { @@ -292,7 +301,7 @@ sub _req_clone($base, $name=undef) { if ($base->type eq 'KVM' && $MOCK_DEVICES) { diag($req->error); } else { - is($req->error, ''); + is($req->error, '') or confess; } my $domain = rvd_back->search_domain($name); @@ -380,7 +389,7 @@ sub check_hd_from_node($domain, $devices_node) { } sub test_clone_nohd($hd, $base) { - return if $base->volatile_clones && $MOCK_DEVICES; + return if $MOCK_DEVICES; my ($clone_hd) = $base->clones; @@ -521,7 +530,7 @@ sub _clean_devices(@nodes) { init(); clean(); -for my $vm_name (vm_names() ) { +for my $vm_name (reverse vm_names() ) { my $vm; eval { $vm = rvd_back->search_vm($vm_name) }; @@ -541,7 +550,8 @@ for my $vm_name (vm_names() ) { my ($node1, $node2) = remote_node_2($vm_name); clean_remote_node($node1, $node2); - test_devices_v2([$vm,$node1,$node2],[1,1,1],1); + # TODO: volatile clones + # test_devices_v2([$vm,$node1,$node2],[1,1,1],1); test_devices_v2([$vm,$node1,$node2],[1,1,1]); test_devices_v2([$vm,$node1,$node2],[1,3,1]); From d37c6a489e9997f6d4abc63115fee131af868c6c Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Fri, 24 May 2024 15:43:58 +0200 Subject: [PATCH 43/50] wip: pci test --- t/device/50_nodes.t | 105 +++++++++++++++++++++++++++++++++----------- 1 file changed, 79 insertions(+), 26 deletions(-) diff --git a/t/device/50_nodes.t b/t/device/50_nodes.t index b2eeebb5a..b77b4d64a 100644 --- a/t/device/50_nodes.t +++ b/t/device/50_nodes.t @@ -87,24 +87,58 @@ sub _create_mock_devices($vm, $n_devices, $type, $value="fff:fff") { } } -sub test_devices_v2($node, $number, $volatile=0) { - _clean_devices(@$node); +sub _create_host_devices($node,$number, $type=undef) { + my $vm = $node->[0]; + + my $templates = Ravada::HostDevice::Templates::list_templates($vm->type); + my ($first) = $templates->[0]; + if ($type) { + ($first) = grep { $_->{name} =~ /$type/ } @$templates; + } + diag($first->{name}); + + $vm->add_host_device(template => $first->{name}); + + my $config = config_host_devices($first->{name},0); + my ($hd, $found); + if ($config) { + my @list_hostdev = $vm->list_host_devices(); + ($hd) = $list_hostdev[-1]; + $hd->_data('list_filter' => $config); + + my %devices_nodes = $hd->list_devices_nodes(); + + $found=1; + for my $n (0 .. scalar(@$node)-1) { + my $curr_node = $node->[$n]; + my $devices = $devices_nodes{$curr_node->id}; + $found=0 unless scalar(@$devices) >= $number->[0]; + } + $MOCK_DEVICES=0; + return $hd if $found; + + } + return if $type && !$found; + diag("creating mock devices because not enough found"); my ($list_command,$list_filter) = _create_mock_devices($node->[0], $number->[0], "USB" ); for my $i (1..scalar(@$node)-1) { die "Error, missing number[$i] ".Dumper($number) unless defined $number->[$i]; _create_mock_devices($node->[$i], $number->[$i], "USB" ); } - my $templates = Ravada::HostDevice::Templates::list_templates($vm->type); - my ($first) = $templates->[0]; - $vm->add_host_device(template => $first->{name}); - my @list_hostdev = $vm->list_host_devices(); - my ($hd) = $list_hostdev[-1]; $hd->_data('list_command',$list_command); $hd->_data('list_filter',$list_filter); - my %devices_nodes = $hd->list_devices_nodes(); + return $hd; +} + +sub test_devices_v2($node, $number, $volatile=0, $type=undef) { + _clean_devices(@$node); + my $vm = $node->[0]; + + my $hd = _create_host_devices($node, $number); + test_assign_v2($hd,$node,$number, $volatile); @@ -132,6 +166,7 @@ sub test_devices($vm, $node, $n_local=3, $n_node=3) { my $node_name = $node->name; my %devices_nodes = $hd->list_devices_nodes(); + warn Dumper(\%devices_nodes); my %dupe; for my $node (keys %devices_nodes) { for my $dev (@{$devices_nodes{$node}}) { @@ -178,9 +213,10 @@ sub test_assign_v2($hd, $node, $number, $volatile=0) { my $ws = { channel => "/".$vm->id ,login => user_admin->name }; + my $fd; for my $n (1 .. $n_expected) { - my $fd = Ravada::WebSocket::_list_host_devices(rvd_front(),$ws); + $fd = Ravada::WebSocket::_list_host_devices(rvd_front(),$ws); my $name = new_domain_name; my $domain = _req_clone($base, $name); @@ -189,11 +225,11 @@ sub test_assign_v2($hd, $node, $number, $volatile=0) { my $hd_checked = check_host_device($domain); next if $MOCK_DEVICES; push(@{$dupe{$hd_checked}},($domain->name." ".$base->id)); - is(scalar(@{$dupe{$hd_checked}}),1) or die Dumper(\%dupe,\%devices_nodes); my $id_vm = $domain->_data('id_vm'); $found{$id_vm}++; - is($found{$id_vm},1) or die Dumper(\%found); } + warn Dumper($fd); + ok(scalar(keys %found) > 1); test_clone_nohd($hd, $base); test_start_in_another_node($hd, $base); @@ -388,27 +424,42 @@ sub check_hd_from_node($domain, $devices_node) { ok($match,"Expecting $locked->{name} in ".Dumper($devices)) or confess; } +sub _count_devices($hd) { + my %devices_nodes = $hd->list_devices_nodes(); + my $n = 0; + for my $id ( keys %devices_nodes) { + $n+= scalar( @{$devices_nodes{$id}}); + } + return $n; +} + sub test_clone_nohd($hd, $base) { return if $MOCK_DEVICES; my ($clone_hd) = $base->clones; - my $name = new_domain_name(); - my $req0 = Ravada::Request->clone( - uid => user_admin->id - ,id_domain => $base->id - ,name => $name - ,start => 0 - ); - wait_request(); + my ($name, $req, $domain0); + for ( 1 .. _count_devices($hd) ) { + diag("trying to overflow"); + $name = new_domain_name(); + my $req0 = Ravada::Request->clone( + uid => user_admin->id + ,id_domain => $base->id + ,name => $name + ,start => 0 + ); + wait_request(); - my $domain0 = rvd_back->search_domain($name); - my $req = Ravada::Request->start_domain( - uid => user_admin->id - ,id_domain => $domain0->id - ); + $domain0 = rvd_back->search_domain($name); + $req = Ravada::Request->start_domain( + uid => user_admin->id + ,id_domain => $domain0->id + ); - wait_request( check_error => 0); + wait_request( check_error => 0); + + last if $req->error; + } like($req->error,qr/host devices/i) or exit; Ravada::Request->refresh_machine(uid => user_admin->id, id_domain => $domain0->id); @@ -530,7 +581,7 @@ sub _clean_devices(@nodes) { init(); clean(); -for my $vm_name (reverse vm_names() ) { +for my $vm_name (vm_names() ) { my $vm; eval { $vm = rvd_back->search_vm($vm_name) }; @@ -553,6 +604,8 @@ for my $vm_name (reverse vm_names() ) { # TODO: volatile clones # test_devices_v2([$vm,$node1,$node2],[1,1,1],1); + test_devices_v2([$vm,$node1,$node2],[1,1,1], undef, 'pci'); + test_devices_v2([$vm,$node1,$node2],[1,1,1]); test_devices_v2([$vm,$node1,$node2],[1,3,1]); test_devices_v2([$vm,$node1,$node2],[1,1,3]); From 81363cd503a903e11f02b7b686547bff98b0c28a Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Fri, 24 May 2024 16:32:18 +0200 Subject: [PATCH 44/50] wip: testing real devs --- lib/Ravada/Domain.pm | 2 ++ t/device/50_nodes.t | 23 +++++++++++++++++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index f3f264837..41b4a7293 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -7169,6 +7169,8 @@ sub add_host_device($self, $host_device) { my $id_hd = $host_device; $id_hd = $host_device->id if ref($host_device); + confess if !$id_hd; + my $sth = $$CONNECTOR->dbh->prepare("INSERT INTO host_devices_domain " ."(id_host_device, id_domain) " ." VALUES ( ?, ? ) " diff --git a/t/device/50_nodes.t b/t/device/50_nodes.t index b77b4d64a..8a68e1ca6 100644 --- a/t/device/50_nodes.t +++ b/t/device/50_nodes.t @@ -73,7 +73,6 @@ sub _create_mock_devices_kvm($vm, $n_devices, $type, $value="fff:fff") { return ("find $PATH/",$name); - } sub _create_mock_devices($vm, $n_devices, $type, $value="fff:fff") { @@ -94,17 +93,19 @@ sub _create_host_devices($node,$number, $type=undef) { my $templates = Ravada::HostDevice::Templates::list_templates($vm->type); my ($first) = $templates->[0]; if ($type) { - ($first) = grep { $_->{name} =~ /$type/ } @$templates; + ($first) = grep { $_->{name} =~ /$type/i } @$templates; + die "Error no template $type found in ".Dumper($templates) if !$first; } diag($first->{name}); $vm->add_host_device(template => $first->{name}); my $config = config_host_devices($first->{name},0); + my ($hd, $found); + my @list_hostdev = $vm->list_host_devices(); + ($hd) = $list_hostdev[-1]; if ($config) { - my @list_hostdev = $vm->list_host_devices(); - ($hd) = $list_hostdev[-1]; $hd->_data('list_filter' => $config); my %devices_nodes = $hd->list_devices_nodes(); @@ -119,7 +120,10 @@ sub _create_host_devices($node,$number, $type=undef) { return $hd if $found; } - return if $type && !$found; + if ( $type && !$found ) { + $hd->remove; + return; + } diag("creating mock devices because not enough found"); my ($list_command,$list_filter) = _create_mock_devices($node->[0], $number->[0], "USB" ); for my $i (1..scalar(@$node)-1) { @@ -137,8 +141,10 @@ sub test_devices_v2($node, $number, $volatile=0, $type=undef) { _clean_devices(@$node); my $vm = $node->[0]; - my $hd = _create_host_devices($node, $number); + my $hd = _create_host_devices($node, $number, $type); + return if $type && !$hd; + die "Error: no hd found" if !$hd; test_assign_v2($hd,$node,$number, $volatile); @@ -206,6 +212,7 @@ sub test_assign_v2($hd, $node, $number, $volatile=0) { my %dupe; my $n_expected = 0; map { $n_expected+= $_ } @$number; + die Dumper($number) if !$n_expected; my $list_nodes = [ map { {$_->id, $_->name} } @$node]; @@ -214,6 +221,7 @@ sub test_assign_v2($hd, $node, $number, $volatile=0) { ,login => user_admin->name }; my $fd; + warn $n_expected; for my $n (1 .. $n_expected) { $fd = Ravada::WebSocket::_list_host_devices(rvd_front(),$ws); @@ -227,6 +235,9 @@ sub test_assign_v2($hd, $node, $number, $volatile=0) { push(@{$dupe{$hd_checked}},($domain->name." ".$base->id)); my $id_vm = $domain->_data('id_vm'); $found{$id_vm}++; + + warn Dumper(\%found); + } warn Dumper($fd); ok(scalar(keys %found) > 1); From ae028aa68f646e9666b7fd3a18c14453cce8a32a Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 27 May 2024 12:26:51 +0200 Subject: [PATCH 45/50] wip: remove pending requests from old hd --- lib/Ravada/Request.pm | 7 ++++--- t/device/10_templates.t | 29 +++++++++++++++++++++-------- t/device/50_nodes.t | 3 +-- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/lib/Ravada/Request.pm b/lib/Ravada/Request.pm index 833e2c59b..72dda7366 100644 --- a/lib/Ravada/Request.pm +++ b/lib/Ravada/Request.pm @@ -1832,7 +1832,6 @@ sub remove($status, %args) { while ( my $row = $sth->fetchrow_hashref ) { my $req_args = {}; - delete $req_args->{uid}; eval { $req_args = decode_json($row->{args}) if $row->{args}; @@ -1840,10 +1839,12 @@ sub remove($status, %args) { warn "Warning: $@ ".$row->{args} ."\n".Dumper($row) if $@; + next if $row->{status} ne $status; + delete $req_args->{uid}; next if scalar(keys%args) != scalar(keys(%$req_args)); my $found = 1; - for my $key (%$req_args) { + for my $key (keys %$req_args) { next if exists $args{$key} && !defined $args{$key} && !defined $req_args->{$key}; @@ -1854,7 +1855,7 @@ sub remove($status, %args) { } next if !$found; - for my $key (%args) { + for my $key (keys %args) { next if exists $req_args->{$key} && !defined $args{$key} && !defined $req_args->{$key}; diff --git a/t/device/10_templates.t b/t/device/10_templates.t index 348613470..b0d8be1f8 100644 --- a/t/device/10_templates.t +++ b/t/device/10_templates.t @@ -163,7 +163,6 @@ sub test_hd_in_domain($vm , $hd) { _set_hd_usb($hd); } } - diag("Testing HD ".$hd->{name}." ".$hd->list_filter." in ".$vm->type); $domain->add_host_device($hd); if ($hd->list_devices) { @@ -220,6 +219,20 @@ sub test_hd_in_domain($vm , $hd) { } +sub _select_clone_up($base) { + + my $sth = connector->dbh->prepare("SELECT id FROM host_devices_domain_locked "); + $sth->execute(); + while ( my ($id) = $sth->fetchrow) { + my $clone = Ravada::Domain->open($id); + next if !$clone->is_active; + + return $clone; + } + + die "Error: no clone active with host devices locked"; +} + sub test_grab_free_device($base) { wait_request(); rvd_back->_cmd_refresh_vms(); @@ -228,10 +241,9 @@ sub test_grab_free_device($base) { die "Error: I need 3 clones . I can't try to grab only ".scalar(@clones) if scalar(@clones)<3; - my ($up) = grep { $_->{status} eq 'active' } @clones; + my $up = _select_clone_up($base); my ($down) = grep { $_->{status} ne 'active' } @clones; ok($down && exists $down->{id}) or die Dumper(\@clones); - $up = Ravada::Domain->open($up->{id}); $down = Ravada::Domain->open($down->{id}); my ($up_dev) = $up->list_host_devices_attached(); @@ -246,6 +258,10 @@ sub test_grab_free_device($base) { test_hostdev_in_domain_config($up, $expect_feat_kvm); test_hostdev_not_in_domain_config($down); + $up->shutdown_now(user_admin); + wait_request(); + sleep 3; + is($up->is_active,0); $up->shutdown_now(user_admin); ($up_dev) = $up->list_host_devices_attached(); is($up_dev->{is_locked},0); @@ -495,7 +511,6 @@ sub test_templates_gone_usb_2($vm) { ,id_host_device => $hd->id ); wait_request(); - diag("try to start again, it should fail"); my $req = Ravada::Request->start_domain(uid => user_admin->id ,id_domain => $domain->id ); @@ -551,7 +566,6 @@ sub test_templates_gone_usb($vm) { _mangle_dom_hd($domain); $hd->_data('list_filter',"no match"); - diag("try to start again, it should fail"); my $req = Ravada::Request->start_domain(uid => user_admin->id ,id_domain => $domain->id ); @@ -798,7 +812,6 @@ sub test_templates_change_filter($vm) { ok(@$templates); for my $first (@$templates) { - diag("Testing $first->{name} Hostdev on ".$vm->type); $vm->add_host_device(template => $first->{name}); my @list_hostdev = $vm->list_host_devices(); my ($hd) = $list_hostdev[-1]; @@ -923,7 +936,6 @@ sub test_templates($vm) { my $n = $vm->list_host_devices; for my $hd ( $vm->list_host_devices ) { _fix_host_device($hd) unless $vm->type eq 'KVM' && !config_host_devices($hd->{name},0); - diag("remove ".$hd->name); test_hd_remove($vm, $hd); is($vm->list_host_devices,--$n, $hd->name) or die Dumper([$vm->list_host_devices]); } @@ -1026,6 +1038,8 @@ for my $vm_name (vm_names()) { diag("Testing host devices in $vm_name"); + test_templates($vm); + test_frontend_list($vm); test_templates_gone_usb_2($vm); @@ -1036,7 +1050,6 @@ for my $vm_name (vm_names()) { test_templates_start_nohd($vm); test_templates_change_filter($vm); - test_templates($vm); test_templates_change_devices($vm); } diff --git a/t/device/50_nodes.t b/t/device/50_nodes.t index 8a68e1ca6..b16a48140 100644 --- a/t/device/50_nodes.t +++ b/t/device/50_nodes.t @@ -96,7 +96,6 @@ sub _create_host_devices($node,$number, $type=undef) { ($first) = grep { $_->{name} =~ /$type/i } @$templates; die "Error no template $type found in ".Dumper($templates) if !$first; } - diag($first->{name}); $vm->add_host_device(template => $first->{name}); @@ -207,7 +206,7 @@ sub test_assign_v2($hd, $node, $number, $volatile=0) { $base->set_base_vm(id_vm => $curr_node->id, user => user_admin); } - wait_request(); + wait_request(debug=>0); my %found; my %dupe; my $n_expected = 0; From 50137a7c6bf985ed25a293de8a94dd64a7b0fac8 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 27 May 2024 14:10:15 +0200 Subject: [PATCH 46/50] wip: testing host devs --- t/device/20_misc.t | 4 ++++ t/device/50_nodes.t | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/t/device/20_misc.t b/t/device/20_misc.t index 84c572b4c..979df3c78 100644 --- a/t/device/20_misc.t +++ b/t/device/20_misc.t @@ -216,6 +216,10 @@ EOT my $domain = create_domain($vm); my $xml_config = XML::LibXML->load_xml(string => $config); + + my ($name) = $xml_config->findnodes("/domain/name/text()"); + + $name->setData($domain->name); $domain->reload_config($xml_config); $domain->remove_config_node("/domain/devices/hostdev", $content, $xml_config); diff --git a/t/device/50_nodes.t b/t/device/50_nodes.t index b16a48140..22253fd25 100644 --- a/t/device/50_nodes.t +++ b/t/device/50_nodes.t @@ -94,6 +94,7 @@ sub _create_host_devices($node,$number, $type=undef) { my ($first) = $templates->[0]; if ($type) { ($first) = grep { $_->{name} =~ /$type/i } @$templates; + return if !$first && $type && $vm->type eq 'Void'; die "Error no template $type found in ".Dumper($templates) if !$first; } @@ -614,7 +615,9 @@ for my $vm_name (vm_names() ) { # TODO: volatile clones # test_devices_v2([$vm,$node1,$node2],[1,1,1],1); - test_devices_v2([$vm,$node1,$node2],[1,1,1], undef, 'pci'); + if ($vm->type eq 'KVM') { + test_devices_v2([$vm,$node1,$node2],[1,1,1], undef, 'pci'); + } test_devices_v2([$vm,$node1,$node2],[1,1,1]); test_devices_v2([$vm,$node1,$node2],[1,3,1]); From 1b3dc7d29d98fdf8c02932053838eb16992be980 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 27 May 2024 16:17:42 +0200 Subject: [PATCH 47/50] wip: wait for hd released --- t/device/40_mediated_device.t | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/t/device/40_mediated_device.t b/t/device/40_mediated_device.t index 45e97b382..54cf46274 100644 --- a/t/device/40_mediated_device.t +++ b/t/device/40_mediated_device.t @@ -134,7 +134,13 @@ sub test_mdev($vm) { is($hd->list_available_devices(), $n_devices-1); test_config($domain); + sleep 1; _req_shutdown($domain); + for ( 1 .. 3 ) { + last if $hd->list_available_devices() <= $n_devices; + _req_shutdown($domain); + sleep 1; + } # $domain->_dettach_host_devices(); is($hd->list_available_devices(), $n_devices); From 47e1ce594fc12778b8ba6f82f4bd8af49418c957 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 27 May 2024 17:04:07 +0200 Subject: [PATCH 48/50] wip: debug SQL in github --- lib/Ravada.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index a0d5bfb7d..e7a240e3a 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -2106,6 +2106,7 @@ sub _create_table { my $sql = join " ",<$in>; close $in; + warn $sql; $CONNECTOR->dbh->do($sql); return 1; } From db504c9cdcc814e62a971232e96acc61dc74a6ad Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 27 May 2024 17:55:20 +0200 Subject: [PATCH 49/50] wip: removed extra comma --- lib/Ravada.pm | 1 - sql/mysql/vms.sql | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index e7a240e3a..a0d5bfb7d 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -2106,7 +2106,6 @@ sub _create_table { my $sql = join " ",<$in>; close $in; - warn $sql; $CONNECTOR->dbh->do($sql); return 1; } diff --git a/sql/mysql/vms.sql b/sql/mysql/vms.sql index e77bcec57..7e8385508 100644 --- a/sql/mysql/vms.sql +++ b/sql/mysql/vms.sql @@ -6,5 +6,5 @@ create table vms ( `default_storage` varchar(64) DEFAULT 'default', `security` varchar(20) default null, `is_active` int default 0, - PRIMARY KEY (`id`), + PRIMARY KEY (`id`) ); From 5c02c4bc2338402103ba1ae0b4151ceca5b8e2db Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 27 May 2024 17:59:33 +0200 Subject: [PATCH 50/50] doc: remove requests --- lib/Ravada/Request.pm | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/Ravada/Request.pm b/lib/Ravada/Request.pm index 72dda7366..e6252e79b 100644 --- a/lib/Ravada/Request.pm +++ b/lib/Ravada/Request.pm @@ -1822,6 +1822,12 @@ sub redo($self) { $sth->execute($self->id); } +=head2 remove + +Remove all requests that comply with the conditions + +=cut + sub remove($status, %args) { my $sth = _dbh->prepare( "SELECT * FROM requests where status = ? "