Commit c0a7d95d authored by Francesc Guasch's avatar Francesc Guasch
Browse files

Merge branch 'hotfix/0.4.4'

parents 13e8787f bf0b2b8f
......@@ -15,12 +15,6 @@
<apic/>
<vmport state='off'/>
</features>
<cpu mode='custom' match='exact' check='full'>
<model fallback='forbid'>Penryn</model>
<feature policy='require' name='vme'/>
<feature policy='require' name='x2apic'/>
<feature policy='require' name='hypervisor'/>
</cpu>
<clock offset='utc'>
<timer name='rtc' tickpolicy='catchup'/>
<timer name='pit' tickpolicy='delay'/>
......@@ -138,12 +132,4 @@
<address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/>
</memballoon>
</devices>
<seclabel type='dynamic' model='apparmor' relabel='yes'>
<label>libvirt-a289e5a7-77c8-4447-b21e-177105598ec6</label>
<imagelabel>libvirt-a289e5a7-77c8-4447-b21e-177105598ec6</imagelabel>
</seclabel>
<seclabel type='dynamic' model='dac' relabel='yes'>
<label>+64055:+130</label>
<imagelabel>+64055:+130</imagelabel>
</seclabel>
</domain>
......@@ -12,9 +12,6 @@
<apic/>
<vmport state='off'/>
</features>
<cpu mode='custom' match='exact' check='partial'>
<model fallback='allow'>Penryn</model>
</cpu>
<clock offset='utc'>
<timer name='rtc' tickpolicy='catchup'/>
<timer name='pit' tickpolicy='delay'/>
......
......@@ -12,9 +12,6 @@
<apic/>
<vmport state='off'/>
</features>
<cpu mode='custom' match='exact' check='partial'>
<model fallback='allow'>Penryn</model>
</cpu>
<clock offset='utc'>
<timer name='rtc' tickpolicy='catchup'/>
<timer name='pit' tickpolicy='delay'/>
......
......@@ -12,9 +12,6 @@
<apic/>
<vmport state='off'/>
</features>
<cpu mode='custom' match='exact' check='partial'>
<model fallback='allow'>Penryn</model>
</cpu>
<clock offset='utc'>
<timer name='rtc' tickpolicy='catchup'/>
<timer name='pit' tickpolicy='delay'/>
......
......@@ -12,9 +12,6 @@
<apic/>
<vmport state='off'/>
</features>
<cpu mode='custom' match='exact' check='partial'>
<model fallback='allow'>Penryn</model>
</cpu>
<clock offset='utc'>
<timer name='rtc' tickpolicy='catchup'/>
<timer name='pit' tickpolicy='delay'/>
......
......@@ -145,12 +145,4 @@
<address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/>
</memballoon>
</devices>
<seclabel type='dynamic' model='apparmor' relabel='yes'>
<label>libvirt-7358e9e4-7472-416e-a2d2-c831491814e9</label>
<imagelabel>libvirt-7358e9e4-7472-416e-a2d2-c831491814e9</imagelabel>
</seclabel>
<seclabel type='dynamic' model='dac' relabel='yes'>
<label>+123:+131</label>
<imagelabel>+123:+131</imagelabel>
</seclabel>
</domain>
......@@ -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;
......@@ -1017,11 +1017,13 @@ sub _add_grant($self, $grant, $allowed, $description) {
my $sth_insert = $CONNECTOR->dbh->prepare(
"INSERT INTO grants_user (id_user, id_grant, allowed) VALUES(?,?,?) ");
$sth = $CONNECTOR->dbh->prepare("SELECT id,name FROM users WHERE is_temporary = 0");
$sth = $CONNECTOR->dbh->prepare("SELECT id,name,is_admin FROM users WHERE is_temporary = 0");
$sth->execute;
while (my ($id_user, $name) = $sth->fetchrow ) {
eval { $sth_insert->execute($id_user, $id_grant, $allowed) };
while (my ($id_user, $name, $is_admin) = $sth->fetchrow ) {
my $allowed_current = $allowed;
$allowed_current = 1 if $is_admin;
eval { $sth_insert->execute($id_user, $id_grant, $allowed_current ) };
die $@ if $@ && $@ !~/Duplicate entry /;
}
}
......@@ -1122,8 +1124,8 @@ sub _upgrade_table {
&& $new_size
&& $new_size != $row->{COLUMN_SIZE}) {
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");
warn "INFO: changing $field $row->{COLUMN_SIZE} to $new_size in $table\n" if $0 !~ /\.t$/;
return;
}
......@@ -1277,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');
......@@ -3084,6 +3086,15 @@ sub _cmd_list_network_interfaces($self, $request) {
$request->output(encode_json(\@ifs));
}
sub _cmd_list_isos($self, $request){
my $vm_type = $request->args('vm_type');
my $vm = Ravada::VM->open( type => $vm_type );
my @isos = sort { "\L$a" cmp "\L$b" } $vm->search_volume_path_re(qr(.*\.iso$));
$request->output(encode_json(\@isos));
}
sub _clean_requests($self, $command, $request=undef) {
my $query = "DELETE FROM requests "
." WHERE command=? "
......@@ -3346,6 +3357,9 @@ sub _req_method {
#networks
,list_network_interfaces => \&_cmd_list_network_interfaces
#isos
,list_isos => \&_cmd_list_isos
);
return $methods{$cmd};
}
......
......@@ -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 ) {
......@@ -2090,6 +2092,20 @@ sub expose($self, @args) {
}
}
sub exposed_port($self, $search) {
confess "Error: you must supply a port number or name of exposed port"
if !defined $search || !length($search);
for my $port ($self->list_ports) {
if ( $search =~ /^\d+$/ ) {
return $port if $port->{internal_port} eq $search;
} else {
return $port if $port->{name} eq $search;
}
}
return;
}
sub _update_expose($self, %args) {
my $id = delete $args{id_port};
$args{internal_port} = delete $args{port}
......@@ -2325,6 +2341,7 @@ sub list_ports($self) {
$sth->execute($self->id);
my @list;
while (my $data = $sth->fetchrow_hashref) {
lock_hash(%$data);
push @list,($data);
}
return @list;
......@@ -2460,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;
}
......@@ -2498,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 );
......@@ -2614,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);
}
......@@ -3003,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) {
......@@ -3454,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';
......
......@@ -580,12 +580,16 @@ Returns a reference to a list of the ISOs known by the system
=cut
sub iso_file {
my $self = shift;
my $vm = $self->search_vm('KVM');
my @isos = sort { "\L$a" cmp "\L$b" } $vm->search_volume_path_re(qr(.*\.iso$));
#TODO remove path from device
return \@isos;
sub iso_file ($self, $vm_type) {
my $req = Ravada::Request->list_isos(
vm_type => $vm_type
);
$self->wait_request($req);
return [] if $req->status ne 'done';
my $isos = decode_json($req->output());
return $isos;
}
=head2 list_lxc_templates
......@@ -884,6 +888,7 @@ sub list_requests($self, $id_domain_req=undef, $seconds=60) {
|| $command eq 'connect_node'
|| $command eq 'post_login'
|| $command eq 'list_network_interfaces'
|| $command eq 'list_isos'
;
next if ( $command eq 'force_shutdown'
|| $command eq 'start'
......
......@@ -101,6 +101,9 @@ our %VALID_ARG = (
#networks
,list_network_interfaces => { uid => 1, vm_type => 1, type => 2 }
#isos
,list_isos => { vm_type => 1 }
,ping_backend => {}
);
......@@ -137,7 +140,7 @@ our %COMMAND = (
,important=> {
limit => 20
,priority => 1
,commands => ['clone','start','start_clones','create','open_iptables','list_network_interfaces']
,commands => ['clone','start','start_clones','create','open_iptables','list_network_interfaces','list_isos']
}
,secondary => {
limit => 50
......
......@@ -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');