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

Fix restore config without host devices #2056

Merged
merged 7 commits into from
May 17, 2024
Merged
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
3 changes: 2 additions & 1 deletion lib/Ravada.pm
Original file line number Diff line number Diff line change
Expand Up @@ -2952,6 +2952,7 @@ sub _upgrade_tables {
$self->_upgrade_table('domains','auto_compact','int default NULL');
$self->_upgrade_table('domains','date_status_change' , 'datetime');
$self->_upgrade_table('domains','show_clones' , 'int not null default 1');
$self->_upgrade_table('domains','config_no_hd' , 'text');

$self->_upgrade_table('domains_network','allowed','int not null default 1');

Expand Down Expand Up @@ -5647,7 +5648,7 @@ sub _cmd_refresh_machine($self, $request) {
,timeout => 60, retry => 10)
if $is_active && $domain->ip && $domain->list_ports;

$domain->_unlock_host_devices() if !$is_active;
$domain->_dettach_host_devices() if !$is_active;
}

sub _cmd_refresh_machine_ports($self, $request) {
Expand Down
72 changes: 45 additions & 27 deletions lib/Ravada/Domain.pm
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ sub _vm_disconnect {
sub _around_start($orig, $self, @arg) {

$self->_post_hibernate() if $self->is_hibernated && !$self->_data('post_hibernated');
$self->_dettach_host_devices() if !$self->is_active;

$self->_start_preconditions(@arg);

Expand Down Expand Up @@ -355,7 +356,7 @@ sub _around_start($orig, $self, @arg) {
$arg{listen_ip} = $display_ip;
}
if ($enable_host_devices) {
$self->_add_host_devices(@arg);
$self->_attach_host_devices(@arg);
} else {
$self->_dettach_host_devices();
}
Expand Down Expand Up @@ -913,7 +914,7 @@ sub _pre_prepare_base($self, $user, $request = undef ) {
sleep 1;
}
}
$self->_unlock_host_devices() if !$self->is_active;
$self->_dettach_host_devices() if !$self->is_active;

# $self->_post_remove_base();
if (!$self->is_local) {
Expand Down Expand Up @@ -3126,7 +3127,7 @@ sub _post_shutdown {
my $is_active = $self->is_active;

if ( $self->is_known && !$self->is_volatile && !$is_active ) {
$self->_unlock_host_devices();
$self->_dettach_host_devices();
if ($self->is_hibernated) {
$self->_data(status => 'hibernated');
} else {
Expand Down Expand Up @@ -7125,9 +7126,7 @@ sub add_host_device($self, $host_device) {

sub remove_host_device($self, $host_device) {
confess if !ref($host_device);

confess if $self->readonly;
$self->_dettach_host_device($host_device);

my $id_hd = $host_device->id;

Expand Down Expand Up @@ -7185,8 +7184,8 @@ sub list_host_devices_attached($self) {
$sth_locked->execute($self->id, $row->{name});
my ($is_locked) = $sth_locked->fetchrow();
$row->{is_locked} = 1 if $is_locked;
push @found,($row);
}
push @found,($row);
}

return @found;
Expand All @@ -7195,6 +7194,21 @@ sub list_host_devices_attached($self) {
# adds host devices to domain instance
# usually run right before startup
sub _add_host_devices($self, @args) {
$self->_attach_host_devices(@args);
}

sub _backup_config_no_hd($self) {
$self->_dettach_host_devices();
$self->_data('config_no_hd' => $self->get_config_txt);
}

sub _restore_config_no_hd($self) {
my $config_no_hd = $self->_data('config_no_hd');
return if !$config_no_hd;
$self->reload_config($config_no_hd);
}

sub _attach_host_devices($self, @args) {
my @host_devices = $self->list_host_devices();
return if !@host_devices;
return if $self->is_active();
Expand All @@ -7208,28 +7222,21 @@ sub _add_host_devices($self, @args) {
$request = delete $args{request} if exists $args{request};
}

$self->_backup_config_no_hd();
my $doc = $self->get_config();
for my $host_device ( @host_devices ) {
next if !$host_device->enabled();
my $device_configured = $self->_device_already_configured($host_device);

my $device;
if ( $device_configured ) {
if ( $host_device->enabled() && $host_device->is_device($device_configured) && $self->_lock_host_device($host_device) ) {
next;
$device = $device_configured;
} else {
$self->_dettach_host_device($host_device, $doc, $device_configured);
}
}
next if !$host_device->enabled();

my ($device) = $host_device->list_available_devices();
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";
}
}
$device = $self->_search_free_device($host_device) if !$device;

$self->_lock_host_device($host_device, $device);

Expand All @@ -7253,12 +7260,25 @@ sub _add_host_devices($self, @args) {

}

sub _search_free_device($self, $host_device) {
my ($device) = $host_device->list_available_devices();
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";
}
}
return $device;
}

sub _dettach_host_devices($self) {
my @host_devices = $self->list_host_devices();
for my $host_device ( @host_devices ) {
$self->_dettach_host_device($host_device);
}
$self->remove_host_devices();
$self->_restore_config_no_hd();
}

sub _dettach_host_device($self, $host_device, $doc=$self->get_config
Expand All @@ -7268,6 +7288,7 @@ sub _dettach_host_device($self, $host_device, $doc=$self->get_config
return if !defined $device or !length($device);

for my $entry( $host_device->render_template($device) ) {

if ($entry->{type} eq 'node') {
$self->remove_config_node($entry->{path}, $entry->{content}, $doc);
} elsif ($entry->{type} eq 'unique_node') {
Expand All @@ -7285,12 +7306,6 @@ sub _dettach_host_device($self, $host_device, $doc=$self->get_config
$self->reload_config($doc);

$self->_unlock_host_device($device);
my $sth = $$CONNECTOR->dbh->prepare(
"UPDATE host_devices_domain SET name=NULL "
." WHERE id_domain=? AND id_host_device=?"
);
$sth->execute($self->id, $host_device->id);

}

# marks a host device as being used by a domain
Expand All @@ -7314,7 +7329,10 @@ sub _lock_host_device($self, $host_device, $device=undef) {
my $query = "INSERT INTO host_devices_domain_locked (id_domain,id_vm,name) VALUES(?,?,?)";

my $sth = $$CONNECTOR->dbh->prepare($query);
eval { $sth->execute($self->id,$self->_vm->id, $device) };
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) };
if ($@) {
warn $@;
$self->_data(status => 'shutdown');
Expand Down Expand Up @@ -7350,7 +7368,7 @@ sub _check_host_device_already_used($self, $device) {
." WHERE id_vm=? AND name=?"
;
my $sth = $$CONNECTOR->dbh->prepare($query);
$sth->execute($self->_vm->id, $device);
$sth->execute($self->_data('id_vm'), $device);
my ($id_domain) = $sth->fetchrow;
# warn "\n".($id_domain or '<UNDEF>')." [".$self->id."] had locked $device\n";

Expand Down
48 changes: 44 additions & 4 deletions lib/Ravada/Domain/KVM.pm
Original file line number Diff line number Diff line change
Expand Up @@ -3651,6 +3651,9 @@ sub _validate_xml($self, $doc) {
}

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;

my $new_domain;
Expand Down Expand Up @@ -3693,6 +3696,10 @@ sub get_config($self) {
return XML::LibXML->load_xml( string => $self->xml_description());
}

sub get_config_txt($self) {
return $self->xml_description();
}

sub _change_xml_address($self, $doc, $address, $bus) {
my $type_def = $address->getAttribute('type');
return $self->_change_xml_address_ide($doc, $address, 1, 1) if $bus eq 'ide';
Expand Down Expand Up @@ -3905,6 +3912,8 @@ sub remove_config_node($self, $path, $content, $doc) {
if ( _xml_equal_hostdev($content, $element_s) ) {
$parent->removeChild($element);
} else {
=pod

my @lines_c = split /\n/,$content;
my @lines_e = split /\n/,$element_s;
warn $element->getName." ".(scalar(@lines_c)." ".scalar(@lines_e));
Expand All @@ -3914,13 +3923,19 @@ sub remove_config_node($self, $path, $content, $doc) {
}
warn $content;
die $self->name if $element->getName eq 'hostdev';
=cut
}
}
}
}

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{(</?)\w+:(\w+)}{$1$2}mg;
my $xml1 = $parser->parse_string($doc1);
Expand Down Expand Up @@ -3966,19 +3981,45 @@ sub add_config_node($self, $path, $content, $doc) {
die "Error: I found ".scalar(@parent)." nodes for $dir, expecting 1"
unless scalar(@parent)==1;

my $element;
my @element;
eval {
($element) = $parent[0]->findnodes($entry);
(@element) = $parent[0]->findnodes($entry);
};
die $@ if $@ && $@ !~ /Undefined namespace prefix/;
return if $element && $element->toString eq $content;
for my $element (@element) {
return if $element && $element->toString eq $content;
}

if ($content =~ /<qemu:commandline/) {
_add_xml_parse($parent[0], $content);
} else {
$self->_fix_pci_slot(\$content);
$parent[0]->appendWellBalancedChunk($content);
}
}

sub set_config_node($self, $path, $content, $doc) {

my ($dir,$entry) = $path =~ m{(.*)/(.*)};
confess "Error: missing entry in '$path'" if !$entry;

my @parent = $doc->findnodes($dir);
if (scalar(@parent)==0) {
@parent = $self->_xml_create_path($doc, $dir);
}

die "Error: I found ".scalar(@parent)." nodes for $dir, expecting 1"
unless scalar(@parent)==1;

my $parent = $parent[0];

for my $child ($parent->findnodes($entry)) {
$parent->removeChild($child);
}

for my $curr_entry (@$content) {
$parent->appendWellBalancedChunk($curr_entry);
}

}

Expand Down Expand Up @@ -4096,7 +4137,6 @@ sub remove_host_devices($self) {
my ($dev) = $doc->findnodes("/domain/devices");
for my $hostdev ( $dev->findnodes("hostdev") ) {
$dev->removeChild($hostdev);
warn $hostdev->toString();
}
$self->reload_config($doc);
}
Expand Down
Loading
Loading