Unverified Commit ec4d3f8e authored by Francesc Guasch's avatar Francesc Guasch Committed by GitHub
Browse files

Feature support focal fossa server (#1374)

feature(backend): run Ravada on Focal Fossa Ubuntu 20.04

* fix(backend): fixed qcow and iptables path
* fix(volumes): fixing qcow2 backing files
* refactor(nodes): find command properly when has args
* test(nodes): improved some tests on focal

issue #1324
parent ce713fea
......@@ -3201,7 +3201,7 @@ sub _delete_ip_rule ($self, $iptables, $vm = $self->_vm) {
&& ( $args{dport} eq $extra->{d_port}))
{
$vm->run_command("/sbin/iptables", "-t", $filter, "-D", $chain, $count)
$vm->run_command("iptables", "-t", $filter, "-D", $chain, $count)
if $vm->is_active;
$removed++;
$count--;
......
......@@ -685,6 +685,7 @@ sub start {
my $set_password = delete $arg{set_password};
$self->_set_spice_ip($set_password, $listen_ip);
$self->_check_qcow_format($request);
$self->status('starting');
my $error;
......@@ -712,6 +713,22 @@ sub start {
}
}
sub _check_qcow_format($self, $request) {
return if $self->is_active;
my $qemu_img = $Ravada::Volume::QCOW2::QEMU_IMG;
for my $vol ( $self->list_volumes_info ) {
next if !$vol->file || $vol->file =~ /iso$/;
next if !$vol->backing_file;
next if $vol->_qemu_info('backing file format') eq 'qcow2';
$request->status("rebasing","rebasing to release 0.8 "
.$vol->file."\n".$vol->backing_file) if $request;
$vol->rebase($vol->backing_file);
$self->remove_backingstore($vol->file);
}
}
sub _remove_smbios($self) {
my $doc = XML::LibXML->load_xml(string => $self->domain->get_xml_description(Sys::Virt::Domain::XML_INACTIVE));
......@@ -1087,7 +1104,6 @@ sub _xml_new_device($self , %arg) {
<disk type='file' device='$device'>
<driver name='qemu' type='$arg{type}' cache='$arg{cache}'/>
<source file='$file'/>
<backingStore/>
<target bus='$bus' dev='$arg{target}'/>
<address type=''/>
<boot/>
......@@ -2427,4 +2443,23 @@ sub dettach($self, $user) {
}
}
sub remove_backingstore($self, $file) {
my $doc = XML::LibXML->load_xml(string
=> $self->xml_description(Sys::Virt::Domain::XML_INACTIVE))
or die "ERROR: $!\n";
my $n_order = 0;
for my $disk ($doc->findnodes('/domain/devices/disk')) {
my ($source_node) = $disk->findnodes('source');
next if !$source_node;
my $file_found = $source_node->getAttribute('file');
next if !$file_found || $file_found ne $file;
my ($backingstore) = $disk->findnodes('backingStore');
$disk->removeChild($backingstore) if $backingstore;
}
$self->_post_change_hardware($doc);
}
1;
......@@ -1263,6 +1263,14 @@ Run a command on the node
sub run_command($self, @command) {
my ($exec) = $command[0];
if ($exec !~ m{^/}) {
my ($exec_command,$args) = $exec =~ /(.*?) (.*)/;
$exec_command = $exec if !defined $exec_command;
$exec = $self->_findbin($exec_command);
$command[0] = $exec;
$command[0] .= " $args" if $args;
}
return $self->_run_command_local(@command) if $self->is_local();
my $ssh = $self->ssh or confess "Error: Error connecting to ".$self->host;
......@@ -1271,8 +1279,9 @@ sub run_command($self, @command) {
chomp $err if $err;
$err = '' if !defined $err;
die "Error: Failed remote command on ".$self->host." @command : '$err'\n"
."'".$ssh->error."'"
confess "Error: Failed remote command on ".$self->host." err='$err'\n"
."ssh error: '".$ssh->error."'\n"
."command: ". Dumper(\@command)
if $ssh->error && $ssh->error !~ /^child exited with code/;
......@@ -1404,16 +1413,26 @@ Creates a new chain in the system iptables
=cut
sub create_iptables_chain($self, $chain, $jchain='INPUT') {
my ($out, $err) = $self->run_command("/sbin/iptables","-n","-L",$chain);
my ($out, $err) = $self->run_command("iptables","-n","-L",$chain);
$self->run_command("/sbin/iptables", '-N' => $chain)
$self->run_command('iptables', '-N' => $chain)
if $out !~ /^Chain $chain/;
($out, $err) = $self->run_command("/sbin/iptables","-n","-L",$jchain);
($out, $err) = $self->run_command("iptables","-n","-L",$jchain);
return if grep(/^$chain /, split(/\n/,$out));
$self->run_command("/sbin/iptables", '-I', $jchain, '-j' => $chain);
$self->run_command("iptables", '-I', $jchain, '-j' => $chain);
}
sub _findbin($self, $name) {
my $exec = "_exec_$name";
return $self->{$exec} if $self->{$exec};
my ($out, $err) = $self->run_command('/usr/bin/which', $name);
chomp $out;
$self->{$exec} = $out;
confess "Error: Command '$name' not found" if !$out;
return $out;
}
=head2 iptables
......@@ -1427,7 +1446,7 @@ Example:
=cut
sub iptables($self, @args) {
my @cmd = ('/sbin/iptables','-w');
my @cmd = ('iptables','-w');
for ( ;; ) {
my $key = shift @args or last;
my $field = "-$key";
......@@ -1491,7 +1510,7 @@ Returns the list of the system iptables
sub iptables_list($self) {
# Extracted from Rex::Commands::Iptables
# (c) Jan Gehring <jan.gehring@gmail.com>
my ($out,$err) = $self->run_command("/sbin/iptables-save");
my ($out,$err) = $self->run_command("iptables-save");
my ( %tables, $ret );
my ($current_table);
......@@ -1554,7 +1573,7 @@ sub balance_vm($self, $base=undef) {
confess "Error: we need a base to balance ";
@vms = $self->list_nodes();
}
return $vms[0] if scalar(@vms)<1;
return $vms[0] if scalar(@vms)<=1;
for my $vm (_random_list( @vms )) {
next if !$vm->enabled();
my $active = 0;
......
package Ravada::Volume::QCOW2;
use Data::Dumper;
use Hash::Util qw(lock_hash);
use Moose;
extends 'Ravada::Volume';
......@@ -58,6 +59,7 @@ sub clone($self, $file_clone) {
die "Error: ".$self->file." looks active" if $n-- <0;
}
my @cmd = ($QEMU_IMG,'create'
,'-F','qcow2'
,'-f','qcow2'
,'-F','qcow2'
,"-b", $self->file
......@@ -70,14 +72,9 @@ sub clone($self, $file_clone) {
}
sub _get_capacity($self) {
my @cmd = ($QEMU_IMG,"info", $self->file);
my ($out, $err) = $self->vm->run_command(@cmd);
confess $err if $err;
my ($size) = $out =~ /virtual size: .*\((\d+) /ms;
confess "I can't find size from $out" if !defined $size;
return $size;
my $size = $self->_qemu_info('virtual size');
my ($capacity) = $size =~ /\((\d+) /;
return $capacity;
}
sub _cmd_convert($base_img, $qcow_img) {
......@@ -98,18 +95,14 @@ sub _cmd_copy {
}
sub backing_file($self) {
my @cmd = ( $QEMU_IMG,'info',$self->file);
my ($out, $err) = $self->vm->run_command(@cmd);
die $err if $err;
my ($base) = $out =~ m{^backing file: (.*)}mi;
return $base;
return $self->_qemu_info('backing file');
}
sub rebase($self, $new_base) {
my @cmd = ($QEMU_IMG,'rebase','-b',$new_base,$self->file);
my @cmd = ($QEMU_IMG,'rebase'
,'-f','qcow2'
,'-F','qcow2'
,'-b',$new_base,$self->file);
my ($out, $err) = $self->vm->run_command(@cmd);
die $err if $err;
......@@ -146,4 +139,34 @@ sub block_commit($self) {
my ($out, $err) = $self->vm->run_command(@cmd, $self->file);
warn $err if $err;
}
sub _qemu_info($self, $field=undef) {
if ( exists $self->{_qemu_info} ) {
return $self->{_qemu_info} if !defined $field;
confess "Unknown field $field ".Dumper($self->{_qemu_info})
if !exists $self->{_qemu_info}->{$field};
return $self->{_qemu_info}->{$field};
}
my @cmd = ( $QEMU_IMG,'info',$self->file);
my ($out, $err) = $self->vm->run_command(@cmd);
die $err if $err;
my %info = (
'backing file'=> undef
,'backing file format' => ''
);
for my $line (split /\n/, $out) {
last if $line =~ /^Format/;
my ($field, $value) = $line =~ /(.*?):\s*(.*)/;
$info{$field} = $value;
}
lock_hash(%info);
$self->{_qemu_info} = \%info;
return $info{$field};
}
1;
......@@ -329,6 +329,61 @@ sub test_defaults($vm) {
$domain->remove(user_admin);
}
sub test_qcow_format($vm) {
return if $vm->type ne 'KVM';
my $base = create_domain($vm);
$base->add_volume(type => 'swap', size => 1024*1024);
$base->add_volume(type => 'data', size => 1024*1024);
my $clone = $base->clone(
name => new_domain_name
,user => user_admin
);
my $QEMU_IMG = `which qemu-img`;
chomp $QEMU_IMG;
for my $vol ( $clone->list_volumes_info ) {
next if $vol->file && $vol->file =~ /iso$/;
my @cmd = ($QEMU_IMG,'create'
,'-f','qcow2'
,"-b", $vol->backing_file
,$vol->file
);
$clone->_vm->run_command(@cmd);
my @cmd_info = ($QEMU_IMG , 'info', $vol->file);
my ($out, $err) = $clone->_vm->run_command(@cmd_info);
my ($bff) = $out =~ /^backing file format: (.*)/m;
is($bff, undef);
}
eval { $clone->start(user_admin) };
is(''.$@,'');
$clone->shutdown_now(user_admin);
for my $vol ( $clone->list_volumes_info ) {
next if !$vol->file || $vol->file =~ /iso$/;
my @cmd_info = ($QEMU_IMG , 'info', $vol->file);
my ($out, $err) = $clone->_vm->run_command(@cmd_info);
my ($bff) = $out =~ /^backing file format: (.*)/m;
is($bff, 'qcow2');
}
eval { $clone->start(user_admin) };
is(''.$@,'');
_remove_domains($base);
}
sub _remove_domains(@bases) {
for my $base (@bases) {
for my $clone ($base->clones) {
my $d_clone = Ravada::Domain->open($clone->{id});
$d_clone->remove(user_admin);
}
$base->remove(user_admin);
}
}
#########################################################
init();
......@@ -350,6 +405,9 @@ for my $vm_name (reverse vm_names() ) {
diag("Testing volumes in $vm_name");
init_vm($vm);
test_qcow_format($vm);
test_raw($vm);
test_raw_swap($vm);
......
......@@ -1063,7 +1063,7 @@ sub _shutdown_nicely($clone) {
sub _write_in_volumes($clone) {
for my $file ($clone->list_volumes) {
$clone->_vm->run_command("echo 'foo: hola' >> $file");
$clone->_vm->run_command("echo",'foo: hola',">>",$file);
}
}
......
......@@ -5,12 +5,13 @@ use warnings;
use Carp qw(carp confess);
use Data::Dumper;
use Fcntl qw(:flock SEEK_END);
use File::Path qw(make_path);
use File::Path qw(make_path remove_tree);
use YAML qw(DumpFile);
use Hash::Util qw(lock_hash unlock_hash);
use IPC::Run3 qw(run3);
use Mojo::File 'path';
use Test::More;
use XML::LibXML;
use YAML qw(Load LoadFile Dump DumpFile);
use feature qw(signatures);
......@@ -940,6 +941,7 @@ sub remove_qemu_pools {
my $name = $pool->get_name;
next if $name !~ qr/^$base/;
diag("Removing ".$pool->get_name." storage_pool");
_delete_qemu_pool($pool);
for my $vol ( $pool->list_volumes ) {
diag("Removing ".$pool->get_name." vol ".$vol->get_name);
$vol->delete();
......@@ -950,6 +952,21 @@ sub remove_qemu_pools {
ok(!$@ or $@ =~ /Storage pool not found/i);
}
opendir my $ls ,"/var/tmp" or die $!;
while (my $file = readdir($ls)) {
next if $file !~ qr/^$base/;
my $dir = "/var/tmp/$file";
remove_tree($dir,{ safe => 1, verbose => 1}) or die "$! $dir";
}
}
sub _delete_qemu_pool($pool) {
my $xml = XML::LibXML->load_xml(string => $pool->get_xml_description());
my ($path) = $xml->findnodes('/pool/target/path');
my $dir = $path->textContent();
rmdir($dir) or die "$! $dir";
}
sub remove_old_pools {
......@@ -1174,12 +1191,16 @@ sub _unlock_all {
sub flush_rules_node($node) {
_lock_fw();
$node->create_iptables_chain($CHAIN);
$node->run_command("/sbin/iptables","-F", $CHAIN);
$node->run_command("/sbin/iptables","-X", $CHAIN);
my ($out, $err) = $node->run_command("iptables","-F", $CHAIN);
is($err,'');
($out, $err) = $node->run_command("iptables","-D","INPUT","-j",$CHAIN);
is($err,'');
($out, $err) = $node->run_command("iptables","-X", $CHAIN);
is($err,'') or die `iptables-save`;
# flush forward too. this is only supposed to run on test servers
$node->run_command("/sbin/iptables","-F", 'FORWARD');
($out, $err) = $node->run_command("iptables","-F", 'FORWARD');
is($err,'');
}
sub flush_rules {
......@@ -1193,6 +1214,7 @@ sub flush_rules {
@cmd = ('iptables','-L','INPUT');
run3(\@cmd, \$in, \$out, \$err);
is($err,'');
my $count = -2;
my @found;
......@@ -1206,11 +1228,16 @@ sub flush_rules {
run3([@cmd, $n], \$in, \$out, \$err);
warn $err if $err;
}
run3(["/sbin/iptables","-F", $CHAIN], \$in, \$out, \$err);
run3(["/sbin/iptables","-X", $CHAIN], \$in, \$out, \$err);
run3(["iptables","-F", $CHAIN], \$in, \$out, \$err);
like($err,qr(^$|chain/target/match by that name));
($out, $err) = run3(["iptables","-D","INPUT","-j",$CHAIN],\$in, \$out, \$err);
like($err,qr(^$|chain/target/match by that name));
run3(["iptables","-X", $CHAIN], \$in, \$out, \$err);
like($err,qr(^$|chain/target/match by that name));
# flush forward too. this is only supposed to run on test servers
run3(["/sbin/iptables","-F","FORWARD" ], \$in, \$out, \$err);
run3(["iptables","-F","FORWARD" ], \$in, \$out, \$err);
is($err,'');
}
......@@ -1403,7 +1430,7 @@ sub hibernate_domain_internal($domain) {
sub _iptables_list {
my ($in, $out, $err);
run3(['/sbin/iptables-save'], \$in, \$out, \$err);
run3(['iptables-save'], \$in, \$out, \$err);
my ( %tables, $ret );
my ($current_table);
......
......@@ -1033,6 +1033,11 @@ sub test_fill_memory($vm, $node, $migrate) {
my $master_free_memory = $vm->free_memory;
my $node_free_memory = $node->free_memory;
my $memory = $master_free_memory/3;
if ($migrate && $node_free_memory < $master_free_memory ) {
$memory = $node_free_memory/3;
}
my $error;
my %nodes;
my @clones;
......@@ -1042,6 +1047,7 @@ sub test_fill_memory($vm, $node, $migrate) {
name => $clone_name
,id_owner => user_admin->id
,id_base => $base->id
,memory => int($memory)
);
wait_request(debug => 0);
is($req->error, '');
......@@ -1057,6 +1063,9 @@ sub test_fill_memory($vm, $node, $migrate) {
$nodes{$clone->_vm->name}++;
last if $migrate && exists $nodes{$vm->name} && $nodes{$vm->name} > 2;
if (keys(%nodes) > 1) {
$memory = int($memory*1.5);
}
}
ok(exists $nodes{$vm->name},"Expecting some clones to node ".$vm->name." ".$vm->id);
ok(exists $nodes{$node->name},"Expecting some clones to node ".$node->name." ".$node->id);
......
......@@ -28,11 +28,13 @@ sub test_no_dupe($vm) {
my ($internal_port, $name_port) = (22, 'ssh');
my ($in, $out, $err);
run3(['/sbin/iptables','-t','nat','-L','PREROUTING','-n'],\($in, $out, $err));
run3(['iptables','-t','nat','-L','PREROUTING','-n'],\($in, $out, $err));
die $err if $err;
my @out = split /\n/,$out;
is(grep(/^DNAT.*/,@out),0);
run3(['/sbin/iptables','-L','FORWARD','-n'],\($in, $out, $err));
run3(['iptables','-L','FORWARD','-n'],\($in, $out, $err));
die $err if $err;
@out = split /\n/,$out;
is(grep(m{^ACCEPT.*192.168.\d+\.0/24\sstate NEW},@out),0);
......@@ -59,16 +61,18 @@ sub test_no_dupe($vm) {
delete_request('enforce_limits');
wait_request(background => 0, debug => 0);
run3(['/sbin/iptables','-t','nat','-L','PREROUTING','-n'],\($in, $out, $err));
run3(['iptables','-t','nat','-L','PREROUTING','-n'],\($in, $out, $err));
@out = split /\n/,$out;
is(grep(/^DNAT.*$local_ip.*dpt:$public_port to:$internal_ip:$internal_port/,@out),1);
run3(['/sbin/iptables','-L','FORWARD','-n'],\($in, $out, $err));
run3(['iptables','-L','FORWARD','-n'],\($in, $out, $err));
die $err if $err;
@out = split /\n/,$out;
is(grep(m{^ACCEPT.*$internal_net\s+state NEW},@out),1,"Expecting rule for $internal_net")
or die $out;
run3(['/sbin/iptables','-L','FORWARD','-n'],\($in, $out, $err));
run3(['iptables','-L','FORWARD','-n'],\($in, $out, $err));
die $err if $err;
@out = split /\n/,$out;
is(grep(m{^ACCEPT.*$remote_ip\s+$internal_ip.*dpt:$internal_port},@out),1) or die $out;
is(grep(m{^DROP.*0.0.0.0.+$internal_ip.*dpt:$internal_port},@out),1) or die $out;
......@@ -79,15 +83,18 @@ sub test_no_dupe($vm) {
#
$domain->start(user => user_admin, remote_ip => $remote_ip);
is($domain->is_active,1);
run3(['/sbin/iptables','-t','nat','-L','PREROUTING','-n'],\($in, $out, $err));
run3(['iptables','-t','nat','-L','PREROUTING','-n'],\($in, $out, $err));
die $err if $err;
@out = split /\n/,$out;
is(grep(/^DNAT.*$local_ip.*dpt:$public_port to:$internal_ip:$internal_port/,@out),1);
run3(['/sbin/iptables','-L','FORWARD','-n'],\($in, $out, $err));
run3(['iptables','-L','FORWARD','-n'],\($in, $out, $err));
die $err if $err;
@out = split /\n/,$out;
is(grep(m{^ACCEPT.*$internal_net\s+state NEW},@out),1) or die $out;
run3(['/sbin/iptables','-L','FORWARD','-n'],\($in, $out, $err));
run3(['iptables','-L','FORWARD','-n'],\($in, $out, $err));
die $err if $err;
@out = split /\n/,$out;
is(grep(m{^ACCEPT.*$remote_ip\s+$internal_ip.*dpt:$internal_port},@out),1) or die $out;
is(grep(m{^DROP.*0.0.0.0.+$internal_ip.*dpt:$internal_port},@out),1) or die $out;
......@@ -105,15 +112,18 @@ sub test_hibernate($domain
is($domain->is_hibernated,1);
my ($in,$out,$err);
run3(['/sbin/iptables','-t','nat','-L','PREROUTING','-n'],\($in, $out, $err));
run3(['iptables','-t','nat','-L','PREROUTING','-n'],\($in, $out, $err));
die $err if $err;
my @out = split /\n/,$out;
is(grep(/^DNAT.*$local_ip.*dpt:$public_port to:$internal_ip:$internal_port/,@out),0);
run3(['/sbin/iptables','-L','FORWARD','-n'],\($in, $out, $err));
run3(['iptables','-L','FORWARD','-n'],\($in, $out, $err));
die $err if $err;
@out = split /\n/,$out;
is(grep(m{^ACCEPT.*192.168.\d+\.0/24\sstate NEW},@out),0);
run3(['/sbin/iptables','-L','FORWARD','-n'],\($in, $out, $err));
run3(['iptables','-L','FORWARD','-n'],\($in, $out, $err));
die $err if $err;
@out = split /\n/,$out;
is(grep(m{^ACCEPT.*$remote_ip\s+$internal_ip.*dpt:$internal_port},@out),0) or die $out;
is(grep(m{^DROP.*0.0.0.0.+$internal_ip.*dpt:$internal_port},@out),0) or die $out;
......@@ -132,15 +142,18 @@ sub test_start_after_hibernate($domain
wait_request(debug => 0, background => 0);
my ($in,$out,$err);
run3(['/sbin/iptables','-t','nat','-L','PREROUTING','-n'],\($in, $out, $err));
run3(['iptables','-t','nat','-L','PREROUTING','-n'],\($in, $out, $err));
die $err if $err;
my @out = split /\n/,$out;
is(grep(/^DNAT.*$local_ip.*dpt:$public_port to:$internal_ip:$internal_port/,@out),1);
run3(['/sbin/iptables','-L','FORWARD','-n'],\($in, $out, $err));
run3(['iptables','-L','FORWARD','-n'],\($in, $out, $err));
die $err if $err;
@out = split /\n/,$out;
is(grep(m{^ACCEPT.*$internal_net\s+state NEW},@out),1) or die $out;
run3(['/sbin/iptables','-L','FORWARD','-n'],\($in, $out, $err));
run3(['iptables','-L','FORWARD','-n'],\($in, $out, $err));
die $err if $err;
@out = split /\n/,$out;
is(grep(m{^ACCEPT.*$remote_ip\s+$internal_ip.*dpt:$internal_port},@out),1) or die $out;
is(grep(m{^DROP.*0.0.0.0.+$internal_ip.*dpt:$internal_port},@out),1) or die $out;
......@@ -852,7 +865,8 @@ sub test_change_expose_3($vm) {
$domain->expose(id_port => $port->{id}, restricted => $restricted);
wait_request(background => 0, debug => 0);
my ($in, $out, $err);
run3(['/sbin/iptables','-L','FORWARD','-n'],\($in, $out, $err));
run3(['iptables','-L','FORWARD','-n'],\($in, $out, $err));
die $err if $err;
my @out = split /\n/,$out;
if ($restricted) {
my $port_re = qr{^ACCEPT.*$remote_ip\s+$internal_ip.*dpt:$port->{internal_port}};
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment