Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor: faster launch machine in node #2140

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions lib/Ravada.pm
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 ) {
Expand Down
49 changes: 24 additions & 25 deletions lib/Ravada/Domain.pm
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -329,7 +328,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) {
Expand Down Expand Up @@ -375,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"
Expand All @@ -389,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);
Expand Down Expand Up @@ -579,16 +578,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);
Expand Down Expand Up @@ -1185,22 +1179,19 @@ 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' );
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 $vm = $self->_vm;

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_local
, vm => $vm
);
$vol_base->clone(file => $vol->file);
}
Expand Down Expand Up @@ -5337,6 +5328,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 "
Expand Down
3 changes: 2 additions & 1 deletion lib/Ravada/Domain/KVM.pm
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
64 changes: 58 additions & 6 deletions lib/Ravada/VM.pm
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ sub BUILD {
}
$self->id;

$self->_which_cache_fetch();
}

sub _open_type {
Expand Down Expand Up @@ -552,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 && $args_create{volatile} && !$base->list_host_devices ) {
$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);
}

Expand Down Expand Up @@ -1405,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;
}
}
Expand Down Expand Up @@ -1776,6 +1783,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);
}

Expand Down Expand Up @@ -2740,8 +2748,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
&& $@ !~ /UNIQUE constraint failed/i
;
}

sub _which_cache_flush($self) {
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) {
Expand All @@ -2757,7 +2807,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;
}

Expand Down
1 change: 1 addition & 0 deletions lib/Ravada/VM/KVM.pm
Original file line number Diff line number Diff line change
Expand Up @@ -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 ) {
Expand Down
2 changes: 1 addition & 1 deletion lib/Ravada/Volume.pm
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}

Expand Down
2 changes: 1 addition & 1 deletion lib/Ravada/Volume/QCOW2.pm
Original file line number Diff line number Diff line change
Expand Up @@ -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) {

Expand Down
39 changes: 39 additions & 0 deletions t/device/40_mediated_device.t
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -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=?"
Expand Down Expand Up @@ -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();
Expand Down
4 changes: 2 additions & 2 deletions t/mojo/30_settings.t
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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");
}
}

Expand Down