Commit 4fd098fa authored by Francesc Guasch's avatar Francesc Guasch
Browse files

Merge tag '0.4.4' into develop

fixed networking for volatile machines

issue #1099
parents 5c5be9a7 c0a7d95d
......@@ -3,7 +3,7 @@ package Ravada;
use warnings;
use strict;
our $VERSION = '0.4.3';
our $VERSION = '0.4.4';
use Carp qw(carp croak);
use Data::Dumper;
......@@ -454,16 +454,47 @@ sub _update_isos {
,xml_volume => 'jessie-volume.xml'
,min_disk_size => '10'
}
,debian_stretch => {
,debian_stretch_32 => {
name =>'Debian Stretch 32 bits'
,description => 'Debian 9 Stretch 32 bits (XFCE desktop)'
,url => 'https://cdimage.debian.org/cdimage/archive/^9\..*/i386/iso-cd/'
,file_re => 'debian-9.[\d\.]+-i386-xfce-CD-1.iso'
,md5_url => '$url/MD5SUMS'
,xml => 'jessie-i386.xml'
,xml_volume => 'jessie-volume.xml'
,min_disk_size => '10'
}
,debian_stretch_64 => {
name =>'Debian Stretch 64 bits'
,description => 'Debian 9 Stretch 64 bits (XFCE desktop)'
,url => 'https://cdimage.debian.org/debian-cd/^9\..*/amd64/iso-cd/'
,url => 'https://cdimage.debian.org/cdimage/archive/^9\..*/amd64/iso-cd/'
,file_re => 'debian-9.[\d\.]+-amd64-xfce-CD-1.iso'
,md5_url => '$url/MD5SUMS'
,xml => 'jessie-amd64.xml'
,xml_volume => 'jessie-volume.xml'
,min_disk_size => '10'
}
,debian_buster_64=> {
name =>'Debian Buster 64 bits'
,description => 'Debian 10 Buster 64 bits (XFCE desktop)'
,url => 'https://cdimage.debian.org/debian-cd/^10\..*/amd64/iso-cd/'
,file_re => 'debian-10.[\d\.]+-amd64-xfce-CD-1.iso'
,md5_url => '$url/MD5SUMS'
,xml => 'jessie-amd64.xml'
,xml_volume => 'jessie-volume.xml'
,min_disk_size => '10'
}
,debian_buster_32=> {
name =>'Debian Buster 32 bits'
,description => 'Debian 10 Buster 32 bits (XFCE desktop)'
,url => 'https://cdimage.debian.org/debian-cd/^10\..*/i386/iso-cd/'
,file_re => 'debian-10.[\d\.]+-i386-xfce-CD-1.iso'
,md5_url => '$url/MD5SUMS'
,xml => 'jessie-i386.xml'
,xml_volume => 'jessie-volume.xml'
,min_disk_size => '10'
}
,windows_7 => {
name => 'Windows 7'
,description => 'Windows 7 64 bits. Requires an user provided ISO image.'
......@@ -1093,7 +1124,7 @@ sub _upgrade_table {
&& $new_size
&& $new_size != $row->{COLUMN_SIZE}) {
warn "INFO: changing $field $row->{COLUMN_SIZE} to $new_size in $table\n" if $0 !~ /\.t$/;
warn "INFO: changing $field $row->{COLUMN_SIZE} to $new_size in $table\n$definition\n" if $0 !~ /\.t$/;
$dbh->do("alter table $table change $field $field $definition");
return;
}
......@@ -1248,7 +1279,7 @@ sub _upgrade_tables {
$self->_upgrade_table('domains','autostart','int NOT NULL DEFAULT 0');
$self->_upgrade_table('domains','status','varchar(32) DEFAULT "shutdown"');
$self->_upgrade_table('domains','display','varchar(250) DEFAULT NULL');
$self->_upgrade_table('domains','display','text');
$self->_upgrade_table('domains','display_file','text DEFAULT NULL');
$self->_upgrade_table('domains','info','varchar(255) DEFAULT NULL');
$self->_upgrade_table('domains','internal_id','varchar(64) DEFAULT NULL');
......
......@@ -1787,6 +1787,7 @@ sub clone {
return $self->_copy_clone(@_) if $self->id_base();
my $remote_ip = delete $args{remote_ip};
my $request = delete $args{request};
my $memory = delete $args{memory};
......@@ -1803,6 +1804,7 @@ sub clone {
my @args_copy = ();
push @args_copy, ( memory => $memory ) if $memory;
push @args_copy, ( request => $request ) if $request;
push @args_copy, ( remote_ip => $remote_ip) if $remote_ip;
my $vm = $self->_vm;
if ($self->volatile_clones ) {
......@@ -2475,8 +2477,8 @@ sub _post_start {
$self->display($arg{user});
$self->display_file($arg{user});
$self->info($arg{user});
$self->open_exposed_ports();
}
$self->open_exposed_ports();
Ravada::Request->enforce_limits(at => time + 60);
$self->post_resume_aux;
}
......@@ -2513,7 +2515,7 @@ sub _add_iptable {
my $display_info = $self->display_info($user);
$self->display_file($user) if !$self->_data('display_file');
my $local_ip = $display_info->{listen_ip};
my $local_ip = (delete $args{local_ip} or $display_info->{listen_ip});
my $local_port = $display_info->{port};
$self->_remove_iptables( port => $local_port );
......@@ -2629,6 +2631,14 @@ sub open_iptables {
}
$self->_add_iptable(%args);
my $remote_ip = $args{remote_ip};
if ($remote_ip && $remote_ip =~ /^127\./) {
my %args2 = %args;
$args2{local_ip} = $self->_vm->ip;
$self->_add_iptable(%args2);
}
$self->info($user);
}
......@@ -3018,10 +3028,8 @@ sub set_driver_id {
}
sub _listen_ip($self, $remote_ip) {
return ( Ravada::display_ip()
or $self->_vm->public_ip
or $self->_vm->_interface_ip($remote_ip));
}
return $self->_vm->listen_ip($remote_ip);
}
sub remote_ip($self) {
......@@ -3469,6 +3477,7 @@ sub _pre_clone($self,%args) {
my $user = delete $args{user};
my $memory = delete $args{memory};
delete $args{request};
delete $args{remote_ip};
confess "ERROR: Missing clone name " if !$name;
confess "ERROR: Invalid name '$name'" if $name !~ /^[a-z0-9_-]+$/i;
......
......@@ -1604,8 +1604,6 @@ sub _set_spice_ip($self, $set_password, $ip=undef) {
=> $self->domain->get_xml_description);
my @graphics = $doc->findnodes('/domain/devices/graphics');
$ip = $self->_vm->ip() if !defined $ip;
for my $graphics ( $doc->findnodes('/domain/devices/graphics') ) {
next if $self->is_hibernated() || $self->domain->is_active;
......@@ -1619,14 +1617,14 @@ sub _set_spice_ip($self, $set_password, $ip=undef) {
}
$self->_set_spice_password($password);
$graphics->setAttribute('listen' => $ip);
$graphics->setAttribute('listen' => ($ip or $self->_vm->listen_ip));
my $listen;
for my $child ( $graphics->childNodes()) {
$listen = $child if $child->getName() eq 'listen';
}
# we should consider in the future add a new listen if it ain't one
next if !$listen;
$listen->setAttribute('address' => $ip);
$listen->setAttribute('address' => ($ip or $self->_vm->listen_ip));
$self->domain->update_device($graphics);
}
}
......
......@@ -49,11 +49,11 @@ sub display_info {
return $display_data;
}
sub _set_display($self, $remote_ip=undef) {
sub _set_display($self, $listen_ip=$self->_vm->listen_ip) {
$listen_ip=$self->_vm->listen_ip if !$listen_ip;
# my $ip = ($self->_vm->nat_ip or $self->_vm->ip());
my $ip = ( $self->_listen_ip($remote_ip) or $self->_vm->ip );
my $display="void://$ip:5990/";
my $display_data = { display => $display , type => 'void', ip => $ip, port => 5990 };
my $display="void://$listen_ip:5990/";
my $display_data = { display => $display , type => 'void', ip => $listen_ip, port => 5990 };
$self->_store( display => $display_data );
return $display_data;
}
......@@ -215,9 +215,16 @@ sub shutdown_now {
sub start($self, @args) {
my %args;
%args = @args if scalar(@args) % 2 == 0;
my $listen_ip = delete $args{listen_ip};
my $remote_ip = delete $args{remote_ip};
my $user = delete $args{user};
delete $args{'id_vm'};
confess "Error: unknown args ".Dumper(\%args) if keys %args;
$listen_ip = $self->_vm->listen_ip($remote_ip) if !$listen_ip;
$self->_store(is_active => 1);
$self->_set_display( $remote_ip );
$self->_set_display( $listen_ip );
}
sub prepare_base {
......@@ -481,8 +488,7 @@ sub get_info {
return $info;
}
sub _set_default_info {
my $self = shift;
sub _set_default_info($self, $listen_ip=undef) {
my $info = {
max_mem => 512*1024
,memory => 512*1024,
......@@ -492,6 +498,7 @@ sub _set_default_info {
,ip =>'1.1.1.'.int(rand(254)+1)
};
$self->_store(info => $info);
$self->_set_display($listen_ip);
my %controllers = $self->list_controllers;
for my $name ( sort keys %controllers) {
next if $name eq 'disk';
......
......@@ -77,11 +77,6 @@ has 'host' => (
, default => 'localhost'
);
has 'public_ip' => (
isa => 'Str'
, is => 'rw'
);
has 'default_dir_img' => (
isa => 'String'
, is => 'ro'
......@@ -177,7 +172,10 @@ sub open {
lock_hash(%$row);
confess "ERROR: I can't find VM id=$args{id}" if !$row || !keys %$row;
return $VM{$args{id}} if $VM{$args{id}} && $VM{$args{id}}->name eq $row->{name};
if ( $VM{$args{id}} && $VM{$args{id}}->name eq $row->{name} ) {
my $vm = $VM{$args{id}};
return _clean($vm);
}
my $type = $row->{vm_type};
$type = 'KVM' if $type eq 'qemu';
......@@ -207,10 +205,10 @@ sub BUILD {
my $name = delete $args->{name};
my $store = delete $args->{store};
$store = 1 if !defined $store;
my $public_ip = delete $args->{public_ip};
delete $args->{readonly};
delete $args->{security};
delete $args->{public_ip};
# TODO check if this is needed
delete $args->{connector};
......@@ -227,16 +225,11 @@ sub BUILD {
,vm_type => $self->type
);
$query{name} = $name if $name;
$query{public_ip} = $public_ip if defined $public_ip;
$self->_select_vm_db(%query);
}
$self->id;
$self->public_ip($self->_data('public_ip'))
if defined $self->_data('public_ip')
&& (!defined $self->public_ip
|| $self->public_ip ne $self->_data('public_ip')
);
}
sub _open_type {
......@@ -364,12 +357,13 @@ sub _around_create_domain {
my $orig = shift;
my $self = shift;
my %args = @_;
my $remote_ip = delete $args{remote_ip};
my %args_create = %args;
my $id_owner = delete $args{id_owner} or confess "ERROR: Missing id_owner";
my $owner = Ravada::Auth::SQL->search_by_id($id_owner) or confess "Unknown user id: $id_owner";
my $base;
my $remote_ip = delete $args{remote_ip};
my $volatile = delete $args{volatile};
my $id_base = delete $args{id_base};
my $id_iso = delete $args{id_iso};
......@@ -377,7 +371,7 @@ sub _around_create_domain {
my $name = delete $args{name};
my $swap = delete $args{swap};
# args get deleted but kept on @_ so when we call $self->$orig below are passed
# args get deleted but kept on %args_create so when we call $self->$orig below are passed
delete $args{disk};
delete $args{memory};
delete $args{request};
......@@ -402,9 +396,11 @@ sub _around_create_domain {
confess "ERROR: Base ".$base->name." is private"
if !$owner->is_admin && $base && !$base->is_public();
$self->_pre_create_domain(@_);
$args_create{listen_ip} = $self->listen_ip($remote_ip);
$args_create{spice_password} = $self->_define_spice_password($remote_ip);
$self->_pre_create_domain(%args_create);
my $domain = $self->$orig(@_, volatile => $volatile);
my $domain = $self->$orig(%args_create, volatile => $volatile);
$domain->add_volume_swap( size => $swap ) if $swap;
if ($id_base) {
......@@ -435,6 +431,15 @@ sub _around_create_domain {
return $domain;
}
sub _define_spice_password($self, $remote_ip) {
my $spice_password = Ravada::Utils::random_name(4);
if ($remote_ip) {
my $network = Ravada::Network->new(address => $remote_ip);
$spice_password = undef if !$network->requires_password;
}
return $spice_password;
}
sub _check_duplicate_name($self, $name) {
my $sth = $$CONNECTOR->dbh->prepare("SELECT id,name,vm FROM domains where name=?");
$sth->execute($name);
......@@ -601,6 +606,17 @@ sub _interface_ip($self, $remote_ip=undef) {
return $default_ip;
}
sub listen_ip($self, $remote_ip=undef) {
return Ravada::display_ip() if Ravada::display_ip();
return $self->public_ip if $self->public_ip;
return $self->_interface_ip($remote_ip) if $remote_ip;
return (
$self->ip()
);
}
sub _check_memory {
my $self = shift;
my %args = @_;
......@@ -644,7 +660,7 @@ sub _check_require_base {
delete $args{start};
delete $args{remote_ip};
delete @args{'_vm','name','vm', 'memory','description','id_iso'};
delete @args{'_vm','name','vm', 'memory','description','id_iso','listen_ip','spice_password'};
confess "ERROR: Unknown arguments ".join(",",keys %args)
if keys %args;
......@@ -693,6 +709,7 @@ sub _data($self, $field, $value=undef) {
);
$sth->execute($value, $self->id);
$sth->finish;
return $value;
}
......@@ -712,12 +729,17 @@ sub _data($self, $field, $value=undef) {
sub _timed_data_cache($self) {
return if !$self->{$FIELD_TIMEOUT} || time - $self->{$FIELD_TIMEOUT} < $CACHE_TIMEOUT;
return _clean($self);
}
sub _clean($self) {
my $name = $self->{_data}->{name};
my $id = $self->{_data}->{id};
delete $self->{_data};
delete $self->{$FIELD_TIMEOUT};
$self->{_data}->{name} = $name if $name;
$self->{_data}->{id} = $id if $id;
return $self;
}
sub _do_select_vm_db {
......@@ -768,11 +790,12 @@ sub _insert_vm_db {
my %args = @_;
my $name = ( delete $args{name} or $self->name);
my $host = ( delete $args{hostname} or $self->host );
my $public_ip = ( delete $args{public_ip} or '' );
delete $args{vm_type};
confess "Unknown args ".Dumper(\%args) if keys %args;
eval { $sth->execute($name,$self->type,$host, $self->public_ip) };
eval { $sth->execute($name,$self->type,$host, $public_ip) };
confess $@ if $@;
$sth->finish;
......@@ -1057,6 +1080,10 @@ sub is_enabled($self, $value=undef) {
return $self->enabled($value);
}
sub public_ip($self, $value=undef) {
return $self->_data('public_ip', $value);
}
=head2 remove
Remove the virtual machine manager.
......
......@@ -725,7 +725,8 @@ sub _domain_create_from_iso {
if !$args{$_};
}
my $remove_cpu = delete $args2{remove_cpu};
for (qw(disk swap active request vm memory iso_file id_template volatile)) {
for (qw(disk swap active request vm memory iso_file id_template volatile spice_password
listen_ip)) {
delete $args2{$_};
}
......@@ -807,20 +808,16 @@ sub _domain_create_common {
my $id_owner = delete $args{id_owner} or confess "ERROR: The id_owner is mandatory";
my $is_volatile = delete $args{is_volatile};
my $remote_ip = delete $args{remote_ip};
my $listen_ip = delete $args{listen_ip};
my $spice_password = delete $args{spice_password};
my $user = Ravada::Auth::SQL->search_by_id($id_owner)
or confess "ERROR: User id $id_owner doesn't exist";
my $spice_password = Ravada::Utils::random_name(4);
if ($remote_ip) {
my $network = Ravada::Network->new(address => $remote_ip);
$spice_password = undef if !$network->requires_password;
}
$self->_xml_modify_memory($xml,$args{memory}) if $args{memory};
$self->_xml_modify_network($xml , $args{network}) if $args{network};
$self->_xml_modify_mac($xml);
my $uuid = $self->_xml_modify_uuid($xml);
$self->_xml_modify_spice_port($xml, $spice_password);
$self->_xml_modify_spice_port($xml, $spice_password, $listen_ip);
$self->_fix_pci_slots($xml);
$self->_xml_add_guest_agent($xml);
$self->_xml_clean_machine_type($xml) if !$self->is_local;
......@@ -1514,16 +1511,14 @@ sub _xml_modify_video {
}
sub _xml_modify_spice_port {
my $self = shift;
my $doc = shift or confess "Missing XML doc";
my $password = shift;
sub _xml_modify_spice_port($self, $doc, $password=undef, $listen_ip=$self->listen_ip) {
$listen_ip = $self->listen_ip if !defined $listen_ip;
my ($graph) = $doc->findnodes('/domain/devices/graphics')
or die "ERROR: I can't find graphic";
$graph->setAttribute(type => 'spice');
$graph->setAttribute(autoport => 'yes');
$graph->setAttribute(listen=> $self->ip() );
$graph->setAttribute(listen=> $listen_ip );
$graph->setAttribute(passwd => $password) if $password;
my ($listen) = $doc->findnodes('/domain/devices/graphics/listen');
......@@ -1533,8 +1528,7 @@ sub _xml_modify_spice_port {
}
$listen->setAttribute(type => 'address');
$listen->setAttribute(address => $self->ip());
$listen->setAttribute(address => $listen_ip);
}
sub _xml_modify_uuid {
......
......@@ -80,6 +80,8 @@ sub create_domain {
my $volatile = delete $args{volatile};
my $active = ( delete $args{active} or $volatile or $user->is_temporary or 0);
my $listen_ip = delete $args{listen_ip};
confess if $args{name} eq 'tst_vm_v20_volatile_clones_02' && !$listen_ip;
my $domain = Ravada::Domain::Void->new(
%args
, domain => $args{name}
......@@ -89,7 +91,7 @@ sub create_domain {
"-e ".$domain->_config_file." && echo 1" );
chomp $out;
die "Error: Domain $args{name} already exists " if $out;
$domain->_set_default_info();
$domain->_set_default_info($listen_ip);
$domain->_store( autostart => 0 );
$domain->_store( is_active => $active );
$domain->set_memory($args{memory}) if $args{memory};
......@@ -136,7 +138,7 @@ sub create_domain {
, target => 'hdc'
);
$domain->_set_default_drivers();
$domain->_set_default_info();
$domain->_set_default_info($listen_ip);
$domain->_store( is_active => 0 );
$domain->_store( is_active => 1 ) if $volatile || $user->is_temporary;
......
......@@ -23,16 +23,6 @@ VALUES('Ubuntu Xenial Xerus 64 bits','Ubuntu 16.04 LTS Xenial Xerus 64 bits'
);
INSERT INTO iso_images
(name,description,arch,xml,xml_volume,url, file_re, md5_url)
VALUES('Ubuntu Yakkety Yak 64 bits',' Ubuntu 16.10 Yakkety Yak 64 bits'
,'amd64'
,'yakkety64-amd64.xml'
,'yakkety64-volume.xml'
,'http://old-releases.ubuntu.com/releases/16.10/'
,'ubuntu-16.10.*desktop-amd64.iso'
,'http://old-releases.ubuntu.com/releases/16.10/MD5SUMS'
);
INSERT INTO iso_images
(name,description,arch,xml,xml_volume,url,file_re,md5_url)
VALUES('Debian Jessie 64 bits'
,'Debian 8.5.0 Jessie 64 bits (netsinst)'
......
......@@ -191,7 +191,7 @@ sub test_iptables($node, $remote_ip, $local_ip, $local_port) {
, local_ip => $local_ip
, local_port => $local_port
);
ok(scalar @line,$node->type." No iptables found $remote_ip -> $local_ip:$local_port");
ok(scalar @line,$node->type." No iptables found $remote_ip -> $local_ip:$local_port") or confess;
ok(scalar @line == 1,$node->type." iptables should found only 1 found $remote_ip -> $local_ip:$local_port ".Dumper(\@line));
}
......@@ -1162,6 +1162,9 @@ SKIP: {
is($node->is_local,0,"Expecting ".$node->name." ".$node->ip." is remote" ) or BAIL_OUT();
is($vm->shared_storage($node,'/var/tmp/'),0) or exit;
test_domain($vm_name, $node);
test_migrate_back($node);
test_remove_base_main($node);
......
use warnings;
use strict;
use Carp qw(confess);
use Data::Dumper;
use JSON::XS;
use Test::More;
......@@ -53,9 +54,7 @@ sub test_create_domain {
return $domain->name;
}
sub test_fw_domain{
my ($vm_name, $domain_name) = @_;
my $remote_ip = '99.88.77.66';
sub test_fw_domain($vm_name, $domain_name, $remote_ip='99.88.77.66') {
my $local_ip;
my $local_port;
......@@ -65,11 +64,13 @@ sub test_fw_domain{
my $vm = rvd_back->search_vm($vm_name);
my $domain = $vm->search_domain($domain_name);
ok($domain,"Searching for domain $domain_name") or return;
$domain->shutdown_now($USER) if $domain->is_active;
$domain->start( user => $USER, remote_ip => $remote_ip);
my $display = $domain->display($USER);
($local_port) = $display =~ m{\d+\.\d+\.\d+\.\d+\:(\d+)};
$local_ip = $vm->ip;
$local_ip = $vm->public_ip if $vm->public_ip;
ok(defined $local_port, "Expecting a port in display '$display'") or return;
......@@ -99,6 +100,14 @@ sub test_fw_domain{
}
sub test_fw_domain_public_ip($vm_name, $domain_name, $remote_ip='1.2.3.4') {
my $vm = rvd_back->search_vm($vm_name);
$vm->public_ip('127.0.0.2');
test_fw_domain($vm_name, $domain_name, $remote_ip);
$vm->public_ip('');
}
sub test_fw_domain_pause {
my ($vm_name, $domain_name) = @_;
my $remote_ip = '99.88.77.66';
......@@ -141,7 +150,7 @@ sub test_fw_domain_pause {
is($req->status,'done');
is($req->error,'');
ok(search_rule($local_ip,$local_port, $remote_ip ),"Expecting rule for $local_ip:$local_port <- $remote_ip") or return;
ok(search_rule($local_ip,$local_port, $remote_ip ),"Expecting rule for $local_ip:$local_port <- $remote_ip") or confess;
my @messages2 = $USER->messages();
is(scalar @messages2, scalar @messages
,"Expecting no new messages ");
......@@ -166,7 +175,8 @@ sub test_chain {
my $rule_num = search_rule(@_);
ok($rule_num,"[$vm_name] Expecting rule for $remote_ip -> $local_ip: $local_port")
ok($rule_num,"[$vm_name] Expecting rule for $remote_ip -> $local_ip: $local_port")
or confess
if $enabled;
ok(!$rule_num,"[$vm_name] Expecting no rule for $remote_ip "
."-> $local_ip: $local_port"
......@@ -238,8 +248,10 @@ for my $vm_name (qw( Void KVM )) {
flush_rules();
my $domain_name = test_create_domain($vm_name);
test_fw_domain($vm_name, $domain_name, '127.0.0.1');
test_fw_domain($vm_name, $domain_name);
test_fw_domain_pause($vm_name, $domain_name);
test_fw_domain_public_ip($vm_name, $domain_name);
test_fw_domain_down($vm_name);
};
......
......@@ -427,8 +427,7 @@ sub test_host_down {
$domain->start(user => user_admin, remote_ip => $remote_ip);
_wait_ip($vm_name, $domain);
rvd_back->_process_requests_dont_fork();
_wait_requests($domain);
my $domain_ip = $domain->ip;
<