From 25020f69c8a04b4848e0abb515000f8bb993d63e Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 27 Jan 2025 14:51:39 +0100 Subject: [PATCH 01/11] wip: cache command paths --- lib/Ravada.pm | 12 ++++++++++++ lib/Ravada/VM.pm | 50 ++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index cc95df39c..33f1a1c39 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -1665,6 +1665,10 @@ sub _add_indexes_generic($self) { "index(id_domain)" ,"unique (id_bundle, id_domain)" ] + ,vm_which => [ + "index(id_vm)" + ,"unique(id_vm,command,path)" + ] ); my $if_not_exists = ''; $if_not_exists = ' IF NOT EXISTS ' if $CONNECTOR->dbh->{Driver}{Name} =~ /sqlite|mariadb/i; @@ -2457,6 +2461,14 @@ sub _sql_create_tables($self) { ,date_changed => 'timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP' } ] + , + [vm_which => { + id => 'integer PRIMARY KEY AUTO_INCREMENT', + ,id_vm => 'integer NOT NULL references `vms` (`id`) ON DELETE CASCADE', + ,command => 'char(40)' + ,path => 'varchar(255)' + } + ] ); for my $new_table (@tables ) { diff --git a/lib/Ravada/VM.pm b/lib/Ravada/VM.pm index 65e02f158..763af4b4a 100644 --- a/lib/Ravada/VM.pm +++ b/lib/Ravada/VM.pm @@ -318,6 +318,7 @@ sub BUILD { } $self->id; + $self->_which_cache_fetch(); } sub _open_type { @@ -1776,6 +1777,7 @@ sub _do_is_active($self, $force=undef) { } sub _cached_active($self, $value=undef) { + $self->_which_cache_flush() if defined $value && $value && !$self->_data('is_active'); return $self->_data('is_active', $value); } @@ -2740,8 +2742,50 @@ sub _list_qemu_bridges($self) { return keys %bridge; } -sub _which($self, $command) { +sub _which_cache_fetch($self) { + my $sth = $self->_dbh->prepare( + "SELECT command,path FROM vm_which " + ." WHERE id_vm=?" + ); + $sth->execute($self->id); + while (my ($command, $path)) { + $self->{_which}->{$command} = $path; + } + $sth->finish; +} + + +sub _which_cache_get($self, $command) { return $self->{_which}->{$command} if exists $self->{_which} && exists $self->{_which}->{$command}; +} + +sub _which_cache_set($self, $command, $path) { + $self->{_which}->{$command} = $path; + + eval { + my $sth = $self->_dbh->prepare( + "INSERT INTO vm_which (id_vm, command, path)" + ." VALUES (?,?,?) " + ); + $sth->execute($self->id, $command, $path); + }; + warn("Warning: $@ vm_which = ( ".$self->id.", $command, $path )") + if $@ && $@ !~ /Duplicate entry/i; + +} + +sub _which_cache_flush($self) { + warn $self->id." flush which cache"; + my $sth = $self->_dbh->prepare( + "DELETE FROM vm_which where id_vm=?" + ); + $sth->execute($self->id); +} + +sub _which($self, $command) { + + my $cached = $self->_which_cache_get($command); + return $cached if $cached; my $bin_which = $self->{_which}->{which}; if (!$bin_which) { @@ -2757,7 +2801,9 @@ sub _which($self, $command) { my @cmd = ( $bin_which,$command); my ($out,$err) = $self->run_command(@cmd); chomp $out; - $self->{_which}->{$command} = $out; + + $self->_which_cache_set($command,$out); + return $out; } From 1869c50617b4a6b546b4cd84dc4a4992e8ad5ec0 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Wed, 29 Jan 2025 12:44:05 +0100 Subject: [PATCH 02/11] wip: do not check base in nodes all the time --- lib/Ravada/Domain.pm | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index eb712a677..a290897c6 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -329,7 +329,7 @@ sub _around_start($orig, $self, @arg) { $enable_host_devices = 1 if !defined $enable_host_devices; my $is_volatile = ($self->is_volatile || $arg{is_volatile}); - for (1 .. 5) { + for (1 .. 2) { eval { $self->_start_checks(%arg, enable_host_devices => $enable_host_devices) }; my $error = $@; if ($error) { @@ -579,16 +579,11 @@ sub _start_checks($self, @args) { my $id_base = $self->id_base; if ($id_base && !$is_volatile) { $self->_check_tmp_volumes(); -# $self->_set_last_vm(1) - if ( !$self->is_local - && ( !$self->_vm->enabled || !base_in_vm($id_base,$self->_vm->id) - || !$self->_vm->ping) ) { - $self->_set_vm($vm_local, 1); - } if ( !$vm->is_alive ) { $vm->disconnect(); $vm->connect; $vm = $vm_local if !$vm->is_local && !$vm->is_alive; + die "Error: node ".$vm->name." is not alive" if !$vm->is_alive; }; if ($id_vm) { $self->_set_vm($vm); @@ -1185,10 +1180,9 @@ sub _check_free_vm_memory { sub _check_tmp_volumes($self) { confess "Error: only clones temporary volumes can be checked." if !$self->id_base; - my $vm_local = $self->_vm->new( host => 'localhost' ); + my $vm = $self->_vm; for my $vol ( $self->list_volumes_info) { next unless $vol->file && $vol->file =~ /\.(TMP|SWAP)\./; - next if $vm_local->file_exists($vol->file); $vol->delete(); my $base = Ravada::Domain->open($self->id_base); @@ -1200,7 +1194,7 @@ sub _check_tmp_volumes($self) { } my $vol_base = Ravada::Volume->new( file => $file_base->[0] , is_base => 1 - , vm => $vm_local + , vm => $vm ); $vol_base->clone(file => $vol->file); } From 9527b8b962d605a2988a86d17e46da74ee33cca3 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Wed, 29 Jan 2025 13:16:13 +0100 Subject: [PATCH 03/11] wip: remove warning for sqlite --- lib/Ravada/VM.pm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Ravada/VM.pm b/lib/Ravada/VM.pm index 763af4b4a..d542e6e9d 100644 --- a/lib/Ravada/VM.pm +++ b/lib/Ravada/VM.pm @@ -2770,12 +2770,12 @@ sub _which_cache_set($self, $command, $path) { $sth->execute($self->id, $command, $path); }; warn("Warning: $@ vm_which = ( ".$self->id.", $command, $path )") - if $@ && $@ !~ /Duplicate entry/i; - + if $@ && $@ !~ /Duplicate entry/i + && $@ !~ /UNIQUE constraint failed/i + ; } sub _which_cache_flush($self) { - warn $self->id." flush which cache"; my $sth = $self->_dbh->prepare( "DELETE FROM vm_which where id_vm=?" ); From 30416f03ae11232e01eb8f1ff613a5f9c9c89830 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Wed, 29 Jan 2025 13:20:06 +0100 Subject: [PATCH 04/11] wip: removed debug --- lib/Ravada/VM.pm | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Ravada/VM.pm b/lib/Ravada/VM.pm index d542e6e9d..f045ac12b 100644 --- a/lib/Ravada/VM.pm +++ b/lib/Ravada/VM.pm @@ -2769,7 +2769,6 @@ sub _which_cache_set($self, $command, $path) { ); $sth->execute($self->id, $command, $path); }; - warn("Warning: $@ vm_which = ( ".$self->id.", $command, $path )") if $@ && $@ !~ /Duplicate entry/i && $@ !~ /UNIQUE constraint failed/i ; From 2bc8fa193840b63db3e026356abd637ff5385372 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Wed, 29 Jan 2025 13:23:06 +0100 Subject: [PATCH 05/11] wip: fixed wrong line rm --- lib/Ravada/VM.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Ravada/VM.pm b/lib/Ravada/VM.pm index f045ac12b..d542e6e9d 100644 --- a/lib/Ravada/VM.pm +++ b/lib/Ravada/VM.pm @@ -2769,6 +2769,7 @@ sub _which_cache_set($self, $command, $path) { ); $sth->execute($self->id, $command, $path); }; + warn("Warning: $@ vm_which = ( ".$self->id.", $command, $path )") if $@ && $@ !~ /Duplicate entry/i && $@ !~ /UNIQUE constraint failed/i ; From 1d06f41f68a6c27aafbd7b3789ca2bf896462d6d Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Thu, 30 Jan 2025 10:50:46 +0100 Subject: [PATCH 06/11] wip: do not migrate if already done. Auto balance --- lib/Ravada/Domain.pm | 11 +++++++++-- lib/Ravada/VM.pm | 9 +++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index a290897c6..8437b50fc 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -213,8 +213,7 @@ after 'screenshot' => \&_post_screenshot; after '_select_domain_db' => \&_post_select_domain_db; -before 'migrate' => \&_pre_migrate; -after 'migrate' => \&_post_migrate; +around 'migrate' => \&_around_migrate; around 'get_info' => \&_around_get_info; around 'set_max_mem' => \&_around_set_max_mem; @@ -5331,6 +5330,14 @@ sub _post_migrate($self, $node, $request = undef) { } +sub _around_migrate($orig, $self, $node, $request=undef) { + return if $self->_vm->id == $node->id; + + $self->_pre_migrate($node, $request); + $self->$orig($node, $request); + $self->_post_migrate($node, $request); +} + sub _id_base_in_vm($self, $id_vm) { my $sth = $$CONNECTOR->dbh->prepare( "SELECT id FROM bases_vm " diff --git a/lib/Ravada/VM.pm b/lib/Ravada/VM.pm index d542e6e9d..9cc5627d4 100644 --- a/lib/Ravada/VM.pm +++ b/lib/Ravada/VM.pm @@ -553,15 +553,20 @@ sub _around_create_domain { return $base->_search_pool_clone($owner) if $from_pool; - if ($self->is_local && $base && $base->is_base && $args_create{volatile} && !$base->list_host_devices) { + if ($self->is_local && $base && $base->is_base ) { $request->status("balancing") if $request; my $vm = $self->balance_vm($owner->id, $base); if (!$vm) { die "Error: No free nodes available.\n"; } + if (!$vm->is_local) { + if ( $base->_base_files_in_vm($vm) + && $base->_check_all_parents_in_node($vm)) { + $self = $vm; + } + } $request->status("creating machine on ".$vm->name) if $request; - $self = $vm; $args_create{listen_ip} = $self->listen_ip($remote_ip); } From 93886a9aabcfa089365b7bc270d862825768d02e Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Thu, 30 Jan 2025 10:51:20 +0100 Subject: [PATCH 07/11] wip: properly clean only cloned volumes --- lib/Ravada/Domain.pm | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index a290897c6..5ed5392f5 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -1181,17 +1181,15 @@ sub _check_tmp_volumes($self) { confess "Error: only clones temporary volumes can be checked." if !$self->id_base; my $vm = $self->_vm; - for my $vol ( $self->list_volumes_info) { - next unless $vol->file && $vol->file =~ /\.(TMP|SWAP)\./; - $vol->delete(); - my $base = Ravada::Domain->open($self->id_base); - my @volumes = $base->list_files_base_target; + my $base = Ravada::Domain->open($self->id_base); + my @volumes = $base->list_files_base_target; + for my $vol ( $self->list_volumes_info) { + next unless $vol->file && $vol->file =~ /\.(TMP|SWAP)\.\w+$/; my ($file_base) = grep { $_->[1] eq $vol->info->{target} } @volumes; - if (!$file_base) { - warn "Error: I can't find base volume for target ".$vol->info->{target} - .Dumper(\@volumes); - } + next if !$file_base; + + $vol->delete(); my $vol_base = Ravada::Volume->new( file => $file_base->[0] , is_base => 1 , vm => $vm From 8f165f4d6bdfab563e10a3a8d434d4d3509eeb09 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Thu, 30 Jan 2025 10:51:36 +0100 Subject: [PATCH 08/11] wip: missing cs translation --- t/mojo/30_settings.t | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/t/mojo/30_settings.t b/t/mojo/30_settings.t index 21c399ca8..eb86d5bd5 100644 --- a/t/mojo/30_settings.t +++ b/t/mojo/30_settings.t @@ -27,7 +27,7 @@ my %FILES; my %HREFS; my %MISSING_LANG = map {$_ => 1 } - qw(ca-valencia he ko); + qw(ca-valencia cs he ko); my $ID_DOMAIN; @@ -503,7 +503,7 @@ sub test_languages() { while (my $file = readdir $ls) { next if $file !~ /(.*)\.po$/; next if $MISSING_LANG{$1}; - ok($lang->{$1},"Expecting $1 in select"); + ok($lang->{$1},"Expecting $1 in language select"); } } From 59d52deadbda133c4b0c598e9014b3c1c102ce49 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Thu, 30 Jan 2025 11:25:06 +0100 Subject: [PATCH 09/11] fix: warn and continue if rtc reinsertion error --- lib/Ravada/Domain/KVM.pm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Ravada/Domain/KVM.pm b/lib/Ravada/Domain/KVM.pm index 96fd9045e..80d5ba05b 100644 --- a/lib/Ravada/Domain/KVM.pm +++ b/lib/Ravada/Domain/KVM.pm @@ -694,7 +694,8 @@ sub post_resume_aux($self, %args) { # 55: domain is not running # 74: not configured # 86: no agent - die "$@\n" if $@ && $@ !~ /libvirt error code: (55|74|86),/; + # 84: qemu doesn't support rtc-reset-reinjection command + die "$@\n" if $@ && $@ !~ /libvirt error code: (55|74|84|86),/; } sub set_time($self) { From 5e8dd39580a499d83c4deeccde188792c34cf0cd Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 3 Feb 2025 12:09:00 +0100 Subject: [PATCH 10/11] wip: dettach hd when failed to start --- lib/Ravada/Domain.pm | 8 +++---- t/device/40_mediated_device.t | 39 +++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index 8239cea23..446521a0b 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -374,6 +374,8 @@ sub _around_start($orig, $self, @arg) { warn $error if $error; last if !$error; + $self->_dettach_host_devices() if $enable_host_devices && !$self->is_active(); + my $vm_name = Ravada::VM::_get_name_by_id($self->_data('id_vm')); die "Error: starting ".$self->name." on ".$vm_name." $error" @@ -388,12 +390,10 @@ sub _around_start($orig, $self, @arg) { && $error !~ /Could not run .*swtpm/i && $error !~ /virtiofs/ && $error !~ /child process/i + && $error !~ /host doesn.t support/ + && $error !~ /device not found/ ; - if ($error && $self->is_known && $self->id_base && !$self->is_local && $self->_vm->enabled) { - $self->_request_set_base(); - next; - } die $error; } $self->_post_start(%arg); diff --git a/t/device/40_mediated_device.t b/t/device/40_mediated_device.t index 39c16370a..887d83f7a 100644 --- a/t/device/40_mediated_device.t +++ b/t/device/40_mediated_device.t @@ -447,6 +447,9 @@ sub test_mdev_kvm_state($vm) { test_old_in_locked($domain); test_timer($domain,'present' => 'yes'); + # TODO: + # test_locked_hd($domain); + _req_shutdown($domain); $domain->_dettach_host_devices(); @@ -459,6 +462,20 @@ sub test_mdev_kvm_state($vm) { } +sub test_locked_hd($domain) { + my $sth = connector->dbh->prepare("DELETE FROM host_devices_domain_locked WHERE id_domain=?"); + $sth->execute($domain->id); + + my @domain_hds = $domain->list_host_devices_attached(); + Ravada::Request->refresh_machine( + uid => user_admin->id + ,id_domain => $domain->id + ); + wait_request(); + @domain_hds = $domain->list_host_devices_attached(); + is($domain_hds[0]->{is_locked},1); +} + sub test_old_in_locked($domain) { my $sth = connector->dbh->prepare("SELECT config_no_hd FROM domains d " ." WHERE id=?" @@ -883,6 +900,28 @@ sub test_volatile_clones($vm, $domain, $host_device) { is($host_device->list_available_devices(), $max_n_device) or exit; } +sub test_start_failed($vm) { + return if $vm->type eq 'Void'; # Void HDs will not fail + my $hd = _create_mdev($vm); + + my $dir = _prepare_dir_mdev(); + $hd->_data('list_command' => "ls $dir"); + + my $domain = create_domain($vm); + $domain->add_host_device($hd->id); + + my $req = Ravada::Request->start_domain(uid => user_admin->id + ,id_domain => $domain->id + ); + wait_request( check_error => 0); + + test_xml_no_hd($domain); + + remove_domain($domain); + $hd->remove(); +} + + #################################################################### clean(); From 16e3ade504f895be98a4abfb095ea49059637a00 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 3 Feb 2025 12:11:37 +0100 Subject: [PATCH 11/11] wip: faster remote file type and exists check --- lib/Ravada/VM.pm | 7 ++++--- lib/Ravada/VM/KVM.pm | 1 + lib/Ravada/Volume.pm | 2 +- lib/Ravada/Volume/QCOW2.pm | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/Ravada/VM.pm b/lib/Ravada/VM.pm index 9cc5627d4..44cea7ca4 100644 --- a/lib/Ravada/VM.pm +++ b/lib/Ravada/VM.pm @@ -553,7 +553,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} && !$base->list_host_devices ) { $request->status("balancing") if $request; my $vm = $self->balance_vm($owner->id, $base); @@ -1411,8 +1411,9 @@ sub is_locked($self) { next if defined $at && $at < time + 2; next if !$args; my $args_d = decode_json($args); - if ( exists $args_d->{id_vm} && $args_d->{id_vm} == $self->id ) { - warn "locked by $command\n"; + if ( exists $args_d->{id_vm} + && $args_d->{id_vm} =~ /^\d+$/ + && $args_d->{id_vm} == $self->id ) { return 1; } } diff --git a/lib/Ravada/VM/KVM.pm b/lib/Ravada/VM/KVM.pm index 919ed7dbb..df27f539a 100644 --- a/lib/Ravada/VM/KVM.pm +++ b/lib/Ravada/VM/KVM.pm @@ -615,6 +615,7 @@ sub file_exists($self, $file) { } sub _file_exists_remote($self, $file) { + return 1 if $self->search_volume($file); $file = $self->_follow_link($file) unless $file =~ /which$/; return if !$self->vm; for my $pool ($self->vm->list_all_storage_pools ) { diff --git a/lib/Ravada/Volume.pm b/lib/Ravada/Volume.pm index 9105ca61d..bc9fc44ad 100644 --- a/lib/Ravada/Volume.pm +++ b/lib/Ravada/Volume.pm @@ -95,7 +95,7 @@ sub _type_from_extension($file) { } sub _type($file,$vm = undef) { - return _type_from_file($file,$vm) if $vm; + return _type_from_file($file,$vm) if $vm && $vm->is_local; return (_type_from_extension($file) or 'QCOW2'); } diff --git a/lib/Ravada/Volume/QCOW2.pm b/lib/Ravada/Volume/QCOW2.pm index a7a929120..9d718039c 100644 --- a/lib/Ravada/Volume/QCOW2.pm +++ b/lib/Ravada/Volume/QCOW2.pm @@ -17,7 +17,7 @@ has 'capacity' => ( ,builder => '_get_capacity' ); -our $QEMU_IMG = "/usr/bin/qemu-img"; +our $QEMU_IMG = "qemu-img"; sub prepare_base($self) {