diff --git a/etc/xml/windows_11.xml b/etc/xml/windows_11.xml
index 8e4db065d..12e575d3f 100644
--- a/etc/xml/windows_11.xml
+++ b/etc/xml/windows_11.xml
@@ -23,7 +23,7 @@
-
+
diff --git a/lib/Ravada.pm b/lib/Ravada.pm
index d64765f01..bf8e8cc01 100644
--- a/lib/Ravada.pm
+++ b/lib/Ravada.pm
@@ -494,7 +494,8 @@ sub _update_isos {
,file_re => 'alpine-standard-3.16.*-x86_64.iso'
,sha256_url => '$url/alpine-standard-3.16.*.iso.sha256'
,min_disk_size => '2'
- ,options => { machine => 'pc-q35', bios => 'UEFI' }
+ ,options => { machine => 'pc-q35', bios => 'UEFI'
+ }
}
,alpine381_32 => {
name => 'Alpine 3.16 32 bits'
@@ -866,7 +867,9 @@ sub _update_isos {
,min_ram => 4
,arch => 'x86_64'
,extra_iso => 'https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/archive-virtio/virtio-win-0.1.2\d+-\d+/virtio-win-0.1.2\d+.iso'
- ,options => { machine => 'pc-q35', bios => 'UEFI' }
+ ,options => { machine => 'pc-q35', bios => 'UEFI'
+ ,hardware => { cpu => { cpu => { topology => { threads => 2, cores => 2}}}}
+ }
}
,empty_32bits => {
name => 'Empty Machine 32 bits'
@@ -5157,6 +5160,51 @@ sub _cmd_reboot {
}
+sub _cmd_shutdown_start($self, $request) {
+ my $uid = $request->args('uid');
+ my $id_domain = $request->args('id_domain');
+ my $id_vm = $request->defined_arg('id_vm');
+
+ my $domain;
+ if ($id_vm) {
+ my $vm = Ravada::VM->open($id_vm);
+ $domain = $vm->search_domain_by_id($id_domain);
+ } else {
+ $domain = $self->search_domain_by_id($id_domain);
+ }
+ die "Unknown domain '$id_domain'\n" if !$domain;
+
+ my $user = Ravada::Auth::SQL->search_by_id( $uid);
+
+ die "USER $uid not authorized to restart machine ".$domain->name
+ unless $domain->_data('id_owner') == $user->id || $user->is_operator;
+
+ my $timeout = ($request->defined_arg('timeout') or $domain->_timeout_shutdown() or 60);
+
+ for my $try ( 0 .. 1 ) {
+ $domain->shutdown(timeout => $timeout, user => $user
+ , request => $request);
+
+ for ( 0 .. $timeout+1 ) {
+ last if !$domain->is_active;
+ sleep 1;
+ }
+ last if !$domain->is_active;
+ }
+
+ my $req_shutdown = Ravada::Request->force_shutdown_domain(
+ uid => $user->id
+ ,id_domain => $domain->id
+ ,after_request => $request->id
+ );
+
+ Ravada::Request->start_domain(
+ uid => $user->id
+ ,id_domain => $domain->id
+ ,after_request => $req_shutdown->id
+ );
+}
+
sub _cmd_force_reboot {
my $self = shift;
my $request = shift;
@@ -6098,6 +6146,7 @@ sub _req_method {
,enforce_limits => \&_cmd_enforce_limits
,force_shutdown => \&_cmd_force_shutdown
,force_reboot => \&_cmd_force_reboot
+,shutdown_start => \&_cmd_shutdown_start
,rebase => \&_cmd_rebase
,refresh_storage => \&_cmd_refresh_storage
@@ -6468,6 +6517,8 @@ sub _cmd_close_exposed_ports($self, $request) {
my $user = Ravada::Auth::SQL->search_by_id( $uid ) or die "Error: user $uid not found";
my $domain = Ravada::Domain->open($request->id_domain);
+ return if !$domain;
+
die "Error: user ".$user->name." not authorized to delete iptables rule"
unless $user->is_admin || $domain->_data('id_owner') == $uid;
diff --git a/lib/Ravada/Auth.pm b/lib/Ravada/Auth.pm
index 10109c4f0..6fcb8a071 100644
--- a/lib/Ravada/Auth.pm
+++ b/lib/Ravada/Auth.pm
@@ -6,6 +6,7 @@ use strict;
our $LDAP_OK;
our $SSO_OK;
+use Data::Dumper;
use Ravada::Auth::SQL;
=head1 NAME
diff --git a/lib/Ravada/Auth/SSO.pm b/lib/Ravada/Auth/SSO.pm
index 240cd1aa4..c01043ed6 100644
--- a/lib/Ravada/Auth/SSO.pm
+++ b/lib/Ravada/Auth/SSO.pm
@@ -72,6 +72,7 @@ sub _get_session_userid_by_ticket
my ($cookie) = @_;
my $result;
die 'Can\'t read pubkey file (sso->cookie->pub_key value at ravada.conf file)' if (! -r $$CONFIG->{sso}->{cookie}->{pub_key});
+
eval { $result = Authen::ModAuthPubTkt::pubtkt_verify(publickey => $$CONFIG->{sso}->{cookie}->{pub_key}, keytype => $$CONFIG->{sso}->{cookie}->{type}, ticket => $cookie); };
die $@ ? $@ : 'Cannot validate ticket' if ((! $result) || ($@));
my %data = Authen::ModAuthPubTkt::pubtkt_parse($cookie);
@@ -123,6 +124,11 @@ sub init {
return 0;
}
}
+ if (!$$CONFIG->{sso}->{cookie}->{type}) {
+ $ERR = "Error: missing sso / cookie / type in config file\n";
+ warn $ERR unless $warn++;
+ return 0;
+ }
for my $field (qw(priv_key pub_key)) {
if ( !exists $$CONFIG->{sso}->{cookie}->{$field}
|| ! $$CONFIG->{sso}->{cookie}->{$field}) {
diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm
index f3ab5d9ac..eb3f35758 100644
--- a/lib/Ravada/Domain.pm
+++ b/lib/Ravada/Domain.pm
@@ -303,6 +303,8 @@ sub _around_start($orig, $self, @arg) {
$self->_start_preconditions(@arg);
+ $self->_pre_start_internal();
+
$self->_data( 'post_shutdown' => 0);
$self->_data( 'post_hibernated' => 0);
@@ -702,7 +704,9 @@ sub _around_add_volume {
($name) = $file =~ m{.*/(.*)} if !$name && $file;
$name = $self->name if !$name;
- $name .= "-".$args{target}."-".Ravada::Utils::random_name(4);
+ $name .= "-".$args{target}."-".Ravada::Utils::random_name(4)
+ if $name !~ /\.iso$/;
+
$args{name} = $name;
}
@@ -715,10 +719,12 @@ sub _around_add_volume {
$args{allocation} = Ravada::Utils::size_to_number($args{allocation})
if exists $args{allocation} && defined $args{allocation};
- my $free = $self->_vm->free_disk();
+ my $storage = $args{storage};
+
+ my $free = $self->_vm->free_disk($storage);
my $free_out = int($free / 1024 / 1024 / 1024 ) * 1024 *1024 *1024;
- confess "Error creating volume, out of space $size . Disk free: "
+ die "Error creating volume, out of space $size . Disk free: "
.Ravada::Utils::number_to_size($free_out)
."\n"
if exists $args{size} && $args{size} && $args{size} >= $free;
@@ -1661,6 +1667,10 @@ sub _data($self, $field, $value=undef, $table='domains') {
sub _data_extra($self, $field, $value=undef) {
$self->_insert_db_extra() if !$self->is_known_extra();
+ if (defined $value) {
+ my $old = $self->_data_extra($field);
+ return if defined $old && $old eq $value;
+ }
return $self->_data($field, $value, "domains_".lc($self->type));
}
@@ -1941,9 +1951,6 @@ sub display($self, $user) {
my ($display_info) = grep { $_->{driver} !~ /-tls$/ } @display_info;
- confess "Error: I can't find builtin display info for ".$self->name." ".ref($self)."\n".Dumper($display_info)
- if !exists $display_info->{port};
-
return '' if !$display_info->{driver} || !$display_info->{ip}
|| !$display_info->{port};
@@ -3042,9 +3049,17 @@ sub _remove_start_requests($self) {
}
}
+# it may be superceeded in child class
+sub _post_shutdown_internal {}
+
+# it may be superceeded in child class
+sub _pre_start_internal {}
+
sub _post_shutdown {
my $self = shift;
+ $self->_post_shutdown_internal();
+
my %arg = @_;
my $timeout = delete $arg{timeout};
if (!defined $timeout) {
@@ -4023,7 +4038,7 @@ sub _post_resume {
return $self->_post_start(@_);
}
-sub _timeout_shutdown($self, $value) {
+sub _timeout_shutdown($self, $value=undef) {
$TIMEOUT_SHUTDOWN = $value if defined $value;
return $TIMEOUT_SHUTDOWN;
}
@@ -4519,6 +4534,7 @@ Check if the domain has swap volumes defined, and clean them
sub clean_swap_volumes {
my $self = shift;
+ return if $self->is_active();
for my $vol ( $self->list_volumes_info) {
confess if !$vol->domain;
if ($vol->file && $vol->file =~ /\.SWAP\.\w+$/) {
@@ -5667,6 +5683,7 @@ hardware change can be applied.
=cut
sub needs_restart($self, $value=undef) {
+ return $self->_data('needs_restart') if !defined $value;
return $self->_data('needs_restart',$value);
}
@@ -5690,7 +5707,7 @@ sub _post_change_hardware($self, $hardware, $index, $data=undef) {
}
$self->info(Ravada::Utils->user_daemon) if $self->is_known();
- $self->needs_restart(1) if $self->is_known && $self->_data('status') eq 'active' && $hardware ne 'memory';
+ $self->needs_restart(1) if $self->is_known && $self->_data('status') eq 'active' && $hardware ne 'memory' && $hardware !~ /cpu/;
$self->post_prepare_base() if $self->is_base();
}
diff --git a/lib/Ravada/Domain/KVM.pm b/lib/Ravada/Domain/KVM.pm
index d42c912a4..aeb5e05b2 100644
--- a/lib/Ravada/Domain/KVM.pm
+++ b/lib/Ravada/Domain/KVM.pm
@@ -153,7 +153,10 @@ sub list_disks {
for my $child ($disk->childNodes) {
if ($child->nodeName eq 'source') {
my $file = $child->getAttribute('file');
- next if $file =~ /\.iso$/;
+ if (!$file) {
+ $file = $child->getAttribute('name');
+ }
+ next if !$file || $file =~ /\.iso$/;
push @disks,($file);
}
}
@@ -376,7 +379,9 @@ sub _disk_device($self, $with_info=undef, $attribute=undef, $value=undef) {
for my $disk ($doc->findnodes('/domain/devices/disk')) {
my ($source_node) = $disk->findnodes('source');
my $file;
- $file = $source_node->getAttribute('file') if $source_node;
+ if ( $source_node ) {
+ $file = $self->_get_volume_file($source_node);
+ }
my ($target_node) = $disk->findnodes('target');
my ($driver_node) = $disk->findnodes('driver');
@@ -571,7 +576,7 @@ sub _set_volumes_backing_store($self) {
for my $disk ($doc->findnodes('/domain/devices/disk')) {
next if $disk->getAttribute('device') ne 'disk';
for my $source( $disk->findnodes('source')) {
- my $file = $source->getAttribute('file');
+ my $file = $self->_get_volume_file($source);
my $backing_file = $vol{$file}->backing_file();
$self->_set_backing_store($disk, $backing_file);
@@ -581,6 +586,18 @@ sub _set_volumes_backing_store($self) {
$self->reload_config($doc);
}
+sub _get_volume_file($self, $source) {
+ return $source->getAttribute('file') if $source->getAttribute('file');
+
+ my $pool_name = $source->getAttribute('pool') or die "Error: I need pool or file in ".$source->toString();
+ my $volume = $source->getAttribute('volume') or die "Error: I need pool or file in ".$source->toString();
+ my $pool = $self->_vm->vm->get_storage_pool_by_name($pool_name)
+ or die "Error: no pool $pool_name";
+ my $vol = $pool->get_volume_by_name($volume);
+ return $vol->get_path;
+
+}
+
sub _store_xml($self) {
my $xml = $self->domain->get_xml_description(Sys::Virt::Domain::XML_INACTIVE);
@@ -646,6 +663,7 @@ sub _detect_disks_driver($self) {
my ( $source ) = $disk->findnodes('source');
my $file = $source->getAttribute('file');
+ next if !$file;
next if $file =~ /iso$/;
next unless $self->_vm->file_exists($file);
@@ -878,7 +896,7 @@ sub start {
$self->status('starting');
my $error;
- for ( ;; ) {
+ for ( 1 .. 60 ) {
eval { $self->domain->create() };
$error = $@;
next if $error && $error =~ /libvirt error code: 1, .* pool .* asynchronous/;
@@ -970,6 +988,33 @@ sub shutdown {
}
+sub _pre_start_internal($self,@args) {
+ # remove current CPU before start because we want max cpu the next start
+ $self->_remove_current_cpu();
+}
+
+sub _post_shutdown_internal($self,@args) {
+ # remove current CPU after shutdown because we want max cpu the next start
+ $self->_remove_current_cpu();
+}
+
+sub _remove_current_cpu($self) {
+ my ($is_active,$doc);
+ eval {
+ $is_active = $self->is_active if $self->domain;
+ $doc = XML::LibXML->load_xml(string => $self->domain->get_xml_description(Sys::Virt::Domain::XML_INACTIVE)) if $self->domain;
+ };
+ warn $@ if $@;
+ return if $is_active || !$doc;
+
+ $doc = XML::LibXML->load_xml(string => $self->domain->get_xml_description(Sys::Virt::Domain::XML_INACTIVE));
+ my ($cpu_node) = $doc->findnodes('/domain/vcpu');
+ $cpu_node->removeAttribute('current');
+
+ $self->reload_config($doc);
+}
+
+
sub _do_shutdown {
my $self = shift;
return if !$self->domain->is_active;
@@ -1644,8 +1689,16 @@ sub get_info {
$info->{max_mem} = $mem_xml if $mem_xml ne $info->{max_mem};
$info->{cpu_time} = $info->{cpuTime};
- $info->{n_virt_cpu} = $info->{nrVirtCpu};
- confess Dumper($info) if !$info->{n_virt_cpu};
+
+ my ($cpu_text) = $doc->findnodes('/domain/vcpu/text()');
+ $info->{max_virt_cpu} = 0+$cpu_text->getData();
+ my ($cpu_node) = $doc->findnodes('/domain/vcpu');
+
+ if ($cpu_node->getAttribute('current')) {
+ $info->{n_virt_cpu} = 0+$cpu_node->getAttribute('current')
+ } else {
+ $info->{n_virt_cpu} = $info->{max_virt_cpu};
+ }
if ( $self->is_active() ) {
$info->{ip} = $self->ip();
@@ -1655,6 +1708,13 @@ sub get_info {
eval { @interfaces2 = $self->domain->get_interface_addresses(Sys::Virt::Domain::INTERFACE_ADDRESSES_SRC_AGENT) };
@interfaces = @interfaces2 if !scalar(@interfaces);
$info->{interfaces} = \@interfaces;
+
+ eval {
+ $info->{n_virt_cpu}
+ = $self->domain->get_vcpus(Sys::Virt::Domain::VCPU_GUEST);
+ };
+ # warn error unless it is agent not responding
+ warn $@ if $@ && $@ !~ / error code: 86, /
}
lock_keys(%$info);
@@ -2976,29 +3036,48 @@ sub _change_hardware_display($self, $index, $data) {
sub _change_hardware_vcpus($self, $index, $data) {
+
confess "Error: I don't understand vcpus index = '$index' , only 0"
if defined $index && $index != 0;
- my $n_virt_cpu = delete $data->{n_virt_cpu};
+ my $req_max = delete $data->{max_virt_cpu};
+ my $req_current = delete $data->{n_virt_cpu};
+
confess "Error: Unkown args ".Dumper($data) if keys %$data;
- if ($self->domain->is_active) {
+ my $doc = XML::LibXML->load_xml(string => $self->xml_description);
+ my $changed =0;
+
+ if ($req_current) {
eval {
- $self->domain->set_vcpus($n_virt_cpu, Sys::Virt::Domain::VCPU_GUEST);
+ $self->domain->set_vcpus($req_current, Sys::Virt::Domain::VCPU_GUEST) if $self->is_active;
+
};
if ($@) {
warn $@;
- $self->_data('needs_restart' => 1);
+ $self->_data('needs_restart' => 1) if $self->is_active;
+ }
+ my ($vcpus) = $doc->findnodes('/domain/vcpu');
+ if (!defined $vcpus->getAttribute('current')
+ || $vcpus->getAttribute('current') != $req_current) {
+ $vcpus->setAttribute(current => $req_current);
+ $changed++;
}
}
- my $doc = XML::LibXML->load_xml(string => $self->xml_description);
my ($cpu) = $doc->findnodes('/domain/cpu');
my ($topology) = $cpu->findnodes('topology');
$cpu->removeChild($topology) if $topology;
- my ($vcpus) = ($doc->findnodes('/domain/vcpu/text()'));
- $vcpus->setData($n_virt_cpu);
- $self->reload_config($doc);
+ if ($req_max) {
+ my ($vcpus_max) = ($doc->findnodes('/domain/vcpu/text()'));
+ if ( $vcpus_max ne $req_max ) {
+ $vcpus_max->setData($req_max);
+ $self->needs_restart(1) if $self->is_active;
+ $changed++;
+ }
+ }
+
+ $self->reload_config($doc) if $changed;
}
@@ -3157,6 +3236,9 @@ sub _default_cpu($self) {
}
sub _fix_vcpu_from_topology($self, $data) {
+
+ $data->{vcpu} = {} if !exists $data->{vcpu};
+
if (!exists $data->{cpu}->{topology}
|| !defined($data->{cpu}->{topology})) {
@@ -3182,6 +3264,7 @@ sub _fix_vcpu_from_topology($self, $data) {
}
sub _change_hardware_cpu($self, $index, $data) {
+
$data = $self->_default_cpu()
if !keys %$data;
@@ -3189,36 +3272,76 @@ sub _change_hardware_cpu($self, $index, $data) {
if !$data->{cpu}->{'model'}->{'#text'};
delete $data->{cpu}->{model}->{'$$hashKey'};
-
- my $doc = XML::LibXML->load_xml(string => $self->xml_description);
+ my @flags = (Sys::Virt::Domain::XML_INACTIVE);
+ my $doc = XML::LibXML->load_xml( string => $self->domain->get_xml_description( @flags ));
my $count = 0;
my $changed = 0;
my ($n_vcpu) = $doc->findnodes('/domain/vcpu/text()');
+ my ($cpu0) = $doc->findnodes('/domain/cpu');
$self->_fix_vcpu_from_topology($data);
+
lock_hash(%$data);
+ my ($data_n_cpus, $data_current_cpus);
+ $data_n_cpus = delete $data->{vcpu}->{'#text'} if exists $data->{vcpu}->{'#text'};
+
+ $data_current_cpus = delete $data->{vcpu}->{'current'} if exists $data->{vcpu}->{'current'};
+ $data_n_cpus = $data_current_cpus if !defined $data_n_cpus && defined $data_current_cpus;
+
my ($vcpu) = $doc->findnodes('/domain/vcpu');
- if (exists $data->{vcpu} && $n_vcpu ne $data->{vcpu}->{'#text'}) {
+ if (defined $data_n_cpus && exists $data->{vcpu} && $n_vcpu ne $data_n_cpus) {
$vcpu->removeChildNodes();
- $vcpu->appendText($data->{vcpu}->{'#text'});
+ $vcpu->appendText($data_n_cpus);
+ $changed++;
}
+ for my $key ( keys %{$data->{vcpu}} ) {
+ next if $vcpu->getAttribute($key)
+ && exists $data->{vcpu}->{$key}
+ && defined $data->{vcpu}->{$key}
+ && $vcpu->getAttribute($key) eq $data->{vcpu}->{$key};
+
+ $vcpu->setAttribute($key => $data->{vcpu}->{$key});
+ $changed++ if $key ne 'current';
+ }
+ for my $attrib ($vcpu->attributes) {
+ next if exists $data->{vcpu}->{$attrib->name};
+ $vcpu->removeAttribute($attrib->name);
+ $changed++ if $attrib->name ne 'current';
+ }
+
my ($domain) = $doc->findnodes('/domain');
my ($cpu) = $doc->findnodes('/domain/cpu');
+ my $cpu_string = '';
+ $cpu_string = $cpu->toString();
+ $cpu_string = join("",split(/\n/,$cpu->toString)) if $cpu;
if (!$cpu) {
$cpu = $domain->addNewChild(undef,'cpu');
}
- my $feature = delete $data->{cpu}->{feature};
+ my $feature = $data->{cpu}->{feature};
- $changed += _change_xml($domain, 'cpu', $data->{cpu});
+ _change_xml($domain, 'cpu', $data->{cpu});
if ( $feature ) {
_change_xml_list($cpu, 'feature', $feature, 'name');
- $changed++;
+ }
+ $cpu_string =~ s/\s\s+/ /g;
+ my $cpu_string2 = join("",grep(/./,split(/\n/,$cpu->toString)));
+ $cpu_string2 =~ s/\s\s+/ /g;
+
+ if ( $cpu_string ne $cpu_string2 || $changed ) {
+ $self->needs_restart(1) if $self->is_active;
+ $self->reload_config($doc);
+ }
+ if ($self->is_active && $data_current_cpus) {
+ eval {
+ $self->domain->set_vcpus($data_current_cpus
+ , Sys::Virt::Domain::VCPU_GUEST);
+ };
+ warn $@ if $@;
}
- $self->reload_config($doc) if $changed;
}
@@ -3366,9 +3489,10 @@ sub _change_xml_list($xml,$name, $data, $field='name') {
$node = $curr if $curr->getAttribute($field) eq $entry->{$field};
}
$node = $xml->addNewChild(undef, $name) if !$node;
- for my $field (keys %$entry) {
- next if $field eq '$$hashKey';
- $node->setAttribute($field, $entry->{$field});
+ $node->setAttribute($field, $entry->{$field});
+ for my $field2 (keys %$entry) {
+ next if $field2 eq '$$hashKey' || $field2 eq $field;
+ $node->setAttribute($field2, $entry->{$field2});
}
}
@@ -3379,8 +3503,10 @@ sub _change_xml_list($xml,$name, $data, $field='name') {
}
sub _change_xml($xml, $name, $data) {
+ return 0 if ref($data) eq 'ARRAY';
+
confess Dumper([$name, $data])
- if !ref($data) || ( ref($data) ne 'HASH' && ref($data) ne 'ARRAY');
+ if !ref($data) || ( ref($data) ne 'HASH' );
my $changed = 0;
@@ -3420,12 +3546,13 @@ sub _change_xml($xml, $name, $data) {
&& $node->getAttribute($field) eq $data->{$field};
$node->setAttribute($field, $data->{$field});
+
$changed++;
}
}
for my $child ( $node->childNodes() ) {
my $name = $child->nodeName();
- if (!exists $data->{$name} || !defined $data->{$name} ) {
+ if ($name ne '#text' && (!exists $data->{$name} || !defined $data->{$name}) ) {
$node->removeChild($child);
$changed++;
}
@@ -3520,8 +3647,19 @@ sub _validate_xml($self, $doc) {
sub reload_config($self, $doc) {
$self->_validate_xml($doc) if $self->_vm->vm->get_major_version >= 4;
- my $new_domain = $self->_vm->vm->define_domain($doc->toString);
+
+ my $new_domain;
+
+ eval {
+ $new_domain = $self->_vm->vm->define_domain($doc->toString);
+ };
+
+ cluck ''.$@ if $@;
+
$self->domain($new_domain);
+
+ $self->_data_extra('xml', $doc->toString) if $self->is_known && $self->is_local;
+
}
sub _save_xml_tmp($self,$doc) {
diff --git a/lib/Ravada/Domain/Void.pm b/lib/Ravada/Domain/Void.pm
index f29f4aee2..24ad2532e 100644
--- a/lib/Ravada/Domain/Void.pm
+++ b/lib/Ravada/Domain/Void.pm
@@ -1034,11 +1034,15 @@ sub _change_hardware_disk($self, $index, $data_new) {
sub _change_hardware_vcpus($self, $index, $data) {
my $n = delete $data->{n_virt_cpu};
+ my $max = delete $data->{max_virt_cpu};
confess "Error: unknown args ".Dumper($data) if keys %$data;
my $info = $self->_value('info');
- $info->{n_virt_cpu} = $n;
+ $info->{n_virt_cpu} = $n if defined $n;
+ $info->{max_virt_cpu} = $max if defined $max;
$self->_store(info => $info);
+
+ $self->needs_restart(1);
}
sub _change_hardware_memory($self, $index, $data) {
diff --git a/lib/Ravada/Front/Domain/KVM.pm b/lib/Ravada/Front/Domain/KVM.pm
index 5093ff533..ea290bace 100644
--- a/lib/Ravada/Front/Domain/KVM.pm
+++ b/lib/Ravada/Front/Domain/KVM.pm
@@ -176,7 +176,9 @@ sub _get_controller_generic($self,$type) {
}
sub _get_controller_cpu($self) {
- my $doc = XML::LibXML->load_xml(string => $self->_data_extra('xml'));
+ my $xml = $self->_data_extra('xml');
+ return if !$xml;
+ my $doc = XML::LibXML->load_xml(string => $xml);
my $item = {
_name => 'cpu'
,_order => 0
@@ -192,6 +194,15 @@ sub _get_controller_cpu($self) {
my ($xml_vcpu) = $doc->findnodes("/domain/vcpu");
_xml_elements($xml_vcpu, $item->{vcpu});
+ $item->{vcpu}->{current} = $item->{vcpu}->{'#text'}
+ if exists $item->{vcpu}->{'#text'} && ! defined $item->{vcpu}->{'current'};
+
+ if ($self->is_active) {
+ my $info = $self->get_info();
+ $item->{vcpu}->{current} = $info->{n_virt_cpu}
+ if exists $info->{n_virt_cpu};
+
+ }
if (exists $item->{cpu}->{feature} && ref($item->{cpu}->{feature}) ne 'ARRAY') {
$item->{cpu}->{feature} = [ $item->{cpu}->{feature} ];
}
diff --git a/lib/Ravada/Request.pm b/lib/Ravada/Request.pm
index 890b63fc8..4b4ecef6b 100644
--- a/lib/Ravada/Request.pm
+++ b/lib/Ravada/Request.pm
@@ -74,6 +74,8 @@ our %VALID_ARG = (
,reboot_domain => { name => 2, id_domain => 2, uid => 1, timeout => 2, at => 2
, id_vm => 2 }
,force_reboot_domain => { id_domain => 1, uid => 1, at => 2, id_vm => 2 }
+ ,shutdown_start =>{ name => 2, id_domain => 2, uid => 1, timeout => 2
+ , at => 2 , id_vm => 2 }
,screenshot => { id_domain => 1 }
,domain_autostart => { id_domain => 1 , uid => 1, value => 2 }
,copy_screenshot => { id_domain => 1 }
diff --git a/lib/Ravada/VM.pm b/lib/Ravada/VM.pm
index 82c2b52a2..8e44ec4a0 100644
--- a/lib/Ravada/VM.pm
+++ b/lib/Ravada/VM.pm
@@ -410,6 +410,7 @@ sub _around_create_domain {
my %args = @_;
my $remote_ip = delete $args{remote_ip};
my $add_to_pool = delete $args{add_to_pool};
+ my $hardware = delete $args{options}->{hardware};
my %args_create = %args;
my $id_owner = delete $args{id_owner} or confess "ERROR: Missing id_owner";
@@ -496,6 +497,7 @@ sub _around_create_domain {
$domain->_data('is_compacted' => 1);
$domain->_data('alias' => $alias) if $alias;
$domain->_data('date_status_change', Ravada::Utils::now());
+ $self->_change_hardware_install($domain,$hardware) if $hardware;
if ($id_base) {
$domain->run_timeout($base->run_timeout)
@@ -539,6 +541,19 @@ sub _around_create_domain {
return $domain;
}
+sub _change_hardware_install($self, $domain, $hardware) {
+
+ for my $item (sort keys %$hardware) {
+ Ravada::Request->change_hardware(
+ uid => Ravada::Utils::user_daemon->id
+ ,id_domain=> $domain->id
+ ,hardware => $item
+ ,data => $hardware->{$item}
+ );
+ }
+
+}
+
sub _set_ascii_name($self, $name) {
my $length = length($name);
$name =~ tr/.âêîôûáéíóúàèìòùäëïöüçñ'/-aeiouaeiouaeiouaeioucn_/;
@@ -931,7 +946,7 @@ sub _check_require_base {
delete $args{start};
delete $args{remote_ip};
- delete @args{'_vm','name','vm', 'memory','description','id_iso','listen_ip','spice_password','from_pool', 'volatile', 'alias','storage'};
+ delete @args{'_vm','name','vm', 'memory','description','id_iso','listen_ip','spice_password','from_pool', 'volatile', 'alias','storage', 'options'};
confess "ERROR: Unknown arguments ".join(",",keys %args)
if keys %args;
diff --git a/lib/Ravada/VM/KVM.pm b/lib/Ravada/VM/KVM.pm
index def125b01..01a03033d 100644
--- a/lib/Ravada/VM/KVM.pm
+++ b/lib/Ravada/VM/KVM.pm
@@ -1078,6 +1078,8 @@ sub _domain_create_from_iso {
if $spice_password;
$domain->xml_description();
+ $self->_change_hardware_install($domain,$iso->{options}->{hardware});
+
return $domain;
}
@@ -1831,12 +1833,14 @@ sub _xml_modify_options($self, $doc, $options=undef) {
my $machine = delete $options->{machine};
my $arch = delete $options->{arch};
my $bios = delete $options->{'bios'};
+
confess "Error: bios=$bios and uefi=$uefi clash"
if defined $uefi && defined $bios
&& ($bios !~/uefi/i && $uefi || $bios =~/uefi/i && !$uefi);
$uefi = 1 if $bios && $bios =~ /uefi/i;
+
confess "Arguments unknown ".Dumper($options) if keys %$options;
if ($machine) {
$self->_xml_set_machine($doc, $machine);
diff --git a/lib/Ravada/Volume.pm b/lib/Ravada/Volume.pm
index 6a414cbe0..d0d450c3c 100644
--- a/lib/Ravada/Volume.pm
+++ b/lib/Ravada/Volume.pm
@@ -76,12 +76,13 @@ sub _type_from_file($file, $vm) {
my ($out, $err) = $vm->run_command("file","-L",$file);
return 'QCOW2' if $out =~ /QEMU QCOW/;
+ return 'Void' if $out =~ /ASCII text/;
return 'RAW';
}
sub _type_from_extension($file) {
my ($ext) = $file =~ m{.*\.(.*)};
- confess if !defined $ext;
+ return if !defined $ext;
confess if $ext =~ /-/;
my %type = (
void => 'Void'
@@ -95,7 +96,7 @@ sub _type_from_extension($file) {
sub _type($file,$vm = undef) {
return _type_from_file($file,$vm) if $vm;
- return _type_from_extension($file);
+ return (_type_from_extension($file) or 'QCOW2');
}
sub BUILD($self, $arg) {
@@ -111,6 +112,10 @@ sub BUILD($self, $arg) {
} elsif (exists $arg->{info}) {
if (exists $arg->{info}->{device} && $arg->{info}->{device} eq 'cdrom') {
$class = "Ravada::Volume::ISO";
+ } elsif(exists $arg->{info}->{driver} && exists $arg->{info}->{driver}->{type}) {
+ my $name = 'unknown';
+ $name = $arg->{info}->{name} if exists $arg->{info}->{name};
+ $class = "Ravada::Volume::"._type_from_extension("$name.".$arg->{info}->{driver}->{type});
} else {
confess "I can't guess class from ".Dumper($arg);
}
diff --git a/lib/Ravada/Volume/Void.pm b/lib/Ravada/Volume/Void.pm
index 83cd1fb55..33a688ed0 100644
--- a/lib/Ravada/Volume/Void.pm
+++ b/lib/Ravada/Volume/Void.pm
@@ -33,7 +33,9 @@ sub prepare_base($self) {
}
sub capacity($self) {
- my $info = $self->_load();
+ my $info = {};
+ eval { $info = $self->_load(); };
+ warn $@ if $@;
confess "Unknown capacity for ".$self->file.Dumper($info)
if !exists $info->{capacity};
diff --git a/public/js/admin.js b/public/js/admin.js
index 62d6a6a72..13181bdf7 100644
--- a/public/js/admin.js
+++ b/public/js/admin.js
@@ -215,9 +215,14 @@ ravadaApp.directive("solShowMachine", swMach)
}
}
}
- if ($scope.id_iso && $scope.id_iso.options
- && $scope.id_iso.options['bios']) {
- $scope.bios = $scope.id_iso.options['bios'];
+ if ($scope.id_iso && $scope.id_iso.options) {
+ if( $scope.id_iso.options['bios']) {
+ $scope.bios = $scope.id_iso.options['bios'];
+ }
+ if( $scope.id_iso.options['hardware']) {
+ $scope.hardware = $scope.id_iso.options['hardware'];
+ }
+
}
};
diff --git a/public/js/ravada.js b/public/js/ravada.js
index 7133a2bb1..b37a25c85 100644
--- a/public/js/ravada.js
+++ b/public/js/ravada.js
@@ -363,6 +363,7 @@
* item.cores
* item.threads;
+ cpu.vcpu['current']=undefined;
$scope.topology = true;
};
@@ -393,7 +394,9 @@
}
$scope.$apply(function () {
if ($scope.lock_info) {
- $scope.showmachine.requests = data.requests;
+ if(data.requests) {
+ $scope.showmachine.requests = data.requests;
+ }
return;
}
$scope.hardware = Object.keys(data.hardware);
@@ -420,13 +423,14 @@
if ($scope.showmachine.is_base) {
$scope.copy_is_volatile = $scope.showmachine.volatile_clones;
}
+ $scope.topology_changed();
if (!subscribed_extra) {
subscribed_extra = true;
subscribe_nodes(url,data.type);
//subscribe_bases(url);
}
if ($scope.edit) { $scope.lock_info = true }
- $scope.topology_changed();
+ update_info_settings();
});
_select_new_base();
}
@@ -545,6 +549,14 @@
};
var url_ws;
+
+ var update_info_settings = function() {
+ $scope.new_n_virt_cpu= 0+$scope.showmachine.n_virt_cpu;
+ $scope.new_max_virt_cpu= 0+$scope.showmachine.max_virt_cpu;
+ $scope.new_memory = ($scope.showmachine.memory / 1024);
+ $scope.new_max_mem = ($scope.showmachine.max_mem / 1024);
+ };
+
$scope.init = function(id, url,is_admin) {
url_ws = url;
$scope.showmachineId=id;
@@ -561,10 +573,7 @@
if (typeof $scope.new_name == 'undefined' ) {
$scope.new_name=$scope.showmachine.alias+"-2";
$scope.validate_new_name($scope.showmachine.alias);
- $scope.new_n_virt_cpu= $scope.showmachine.n_virt_cpu;
- $scope.new_memory = ($scope.showmachine.memory / 1024);
- $scope.new_max_mem = ($scope.showmachine.max_mem / 1024);
-
+ update_info_settings();
$scope.new_run_timeout = ($scope.showmachine.run_timeout / 60);
if (!$scope.new_run_timeout) $scope.new_run_timeout = undefined;
@@ -844,6 +853,9 @@
}
};
$scope.check_access = function() {
+ if (!$scope.user_name) {
+ return;
+ }
$http.get('/machine/check_access/'+$scope.showmachine.id+"/"+$scope.user_name).then(function(response) {
$scope.check_allowed=response.data.ok;
});
@@ -1060,7 +1072,6 @@
var _host_device_in_machine = function(id_hd) {
for ( var i=0;i<$scope.showmachine.host_devices.length; i++ ) {
var hd = $scope.showmachine.host_devices[i];
- console.log(hd.id+ " "+id_hd);
if (hd.id_host_device == id_hd) {
return true;
}
@@ -1099,10 +1110,19 @@
});
});
};
-
+ $scope.req_change_current = function(n_cpu) {
+ if (!$scope.topology && $scope.showmachine.is_active) {
+ request('change_hardware',{
+ 'id_domain': $scope.showmachine.id
+ ,'hardware': 'vcpus'
+ ,'data': { 'n_virt_cpu': n_cpu}
+ })
+ }
+ };
$scope.request = function(request, args) {
$scope.showmachine.requests++;
$scope.pending_request = undefined;
+ $scope.lock_info = false;
$http.post('/request/'+request+'/'
,JSON.stringify(args)
).then(function(response) {
@@ -1151,8 +1171,11 @@
});
};
- $scope.reboot = function() {
- $http.get("/machine/stop_start/"+$scope.showmachine.id+".json")
+ $scope.shutdown_start = function() {
+ $scope.set_edit();
+ $scope.lock_info=false;
+ $scope.showmachine.needs_restart = 0;
+ $http.get("/machine/shutdown_start/"+$scope.showmachine.id+".json")
.then(function(response) {
});
};
@@ -1235,10 +1258,8 @@
try {
var successful = document.execCommand('copy');
var msg = successful ? 'successful' : 'unsuccessful';
- console.log('Copying text command was ' + msg);
$scope.password_clipboard=successful;
} catch (err) {
- console.log('Oops, unable to copy');
}
}
diff --git a/script/rvd_front b/script/rvd_front
index 8a6df0d5c..998ea11b0 100644
--- a/script/rvd_front
+++ b/script/rvd_front
@@ -897,9 +897,9 @@ get '/machine/reboot/(:id).(:type)' => sub {
return reboot_machine($c);
};
-get '/machine/stop_start/(:id).(:type)' => sub($c) {
+get '/machine/shutdown_start/(:id).(:type)' => sub($c) {
return access_denied($c) if !$USER ->can_reboot($c->stash('id'));
- return stop_start($c);
+ return shutdown_start($c);
};
@@ -2836,6 +2836,7 @@ sub req_new_domain {
my $vm = ( $c->param('backend') or 'KVM');
$swap = int($swap * 1024*1024*1024);
my $bios = $c->param('bios');
+ my $hardware = $c->param('hardware');
my $machine = ($c->param('machine') or '');
$machine =~ s/^string://;
$machine = '' if $machine eq '?';
@@ -3375,20 +3376,15 @@ sub reboot_machine {
return $c->render(json => { req => $req->id });
}
-sub stop_start($c) {
+sub shutdown_start($c) {
return login($c) if !_logged_in($c);
my ($domain, $type) = _search_requested_machine($c);
- my $req = Ravada::Request->shutdown_domain(name => $domain->name
+ my $req = Ravada::Request->shutdown_start(id_domain => $domain->id
,uid => $USER->id
);
- my $req2 = Ravada::Request->start_domain(id_domain => $domain->id
- ,uid => $USER->id
- ,after_request => $req->id
- );
-
- return $c->render(json => { req => $req2->id });
+ return $c->render(json => { req => $req->id });
}
sub force_shutdown_machine {
diff --git a/t/20_volumes.t b/t/20_volumes.t
index 779d671ca..76daaed9d 100644
--- a/t/20_volumes.t
+++ b/t/20_volumes.t
@@ -363,11 +363,83 @@ sub test_defaults($vm, $volume_type=undef) {
$domain->remove(user_admin);
}
+sub remove_extension($domain, $vol) {
+ if ($domain->type eq 'KVM') {
+ _remove_extension_kvm($domain,$vol);
+ } elsif ($domain->type eq 'Void') {
+ _remove_extension_void($domain,$vol);
+ } else {
+ die "I don't know how to rename ".$domain->type;
+ }
+
+}
+
+sub _remove_extension_void($domain, $vol) {
+ my $yml = $domain->_load();
+ my $hw = $yml->{hardware};
+ for my $dev ( @{$yml->{hardware}->{device}}) {
+ if ($dev->{file} eq $vol) {
+ $dev->{file} =~ s/(.*)\.\w+/$1/;
+ $dev->{file} =~ s/\./_/g;
+ $dev->{name} =~ s/(.*)\.\w+/$1/;
+ $domain->_store(hardware => $yml->{hardware});
+ rename($vol, $dev->{file})
+ or die "$! $vol -> $dev->{file}";
+ }
+ }
+}
+
+sub _remove_extension_kvm($domain, $vol) {
+ my $doc = XML::LibXML->load_xml(string => $domain->domain->get_xml_description);
+ for my $disk ($doc->findnodes('/domain/devices/disk')) {
+ my ($source_node) = $disk->findnodes('source');
+ my $file;
+ if ( $source_node ) {
+ my $file = $source_node->getAttribute('file');
+ if ($file && $file eq $vol) {
+ $file =~ s/(.*)\.\w+/$1/;
+ $file =~ s/\./_/g;
+
+ $source_node->setAttribute('file' => $file);
+ rename($vol, $file)
+ or die "$! $vol -> $file";
+ }
+ }
+ }
+ $domain->reload_config($doc);
+}
+
+sub test_no_extension($vm) {
+ my $base = create_domain($vm);
+ $base->add_volume(type => 'swap', size => 1024*1024);
+ $base->add_volume(type => 'data', size => 1024*1024);
+
+ my @vols = $base->list_volumes();
+
+ for my $vol ( @vols ) {
+ next if $vol =~ /\.iso$/;
+ remove_extension($base, $vol);
+ }
+ my $sth = connector->dbh->prepare(
+ "DELETE FROM volumes WHERE id_domain=?"
+ );
+ $sth->execute($base->id);
+
+ my $info = $base->info(user_admin);
+ my @vols2 = $base->list_volumes_info();
+
+ for my $vol (@vols2) {
+ my $ref = ref($vol);
+ unlike($ref,qr/HASH|RAW$/);
+ }
+}
+
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);
+ wait_request();
my $clone = $base->clone(
name => new_domain_name
@@ -440,6 +512,8 @@ for my $vm_name (reverse vm_names() ) {
diag("Testing volumes in $vm_name");
init_vm($vm);
+ test_no_extension($vm);
+
test_qcow_format($vm);
test_raw($vm);
diff --git a/t/kvm/40_import.t b/t/kvm/40_import.t
index 36ec2d4de..37a1910fe 100644
--- a/t/kvm/40_import.t
+++ b/t/kvm/40_import.t
@@ -2,6 +2,7 @@ use warnings;
use strict;
use Data::Dumper;
+use IPC::Run3 qw(run3);
use Test::More;
use lib 't/lib';
@@ -151,6 +152,165 @@ sub test_import_spinoff {
}
+sub _create_vol($vm, $name) {
+ my $sp = $vm->vm->get_storage_pool_by_name('default');
+
+ my $old_vol = $sp->get_volume_by_name($name);
+ $old_vol->delete() if $old_vol;
+
+ my $xml = <
+ $name
+ /var/lib/libvirt/images/$name
+ 21474836480
+ 3485696
+ 21478375424
+
+ /var/lib/libvirt/images/$name
+
+
+ 0644
+ 0
+ 0
+
+
+ 1538407038.168298505
+ 1538406915.308849295
+ 1538407050.036621775
+
+ 1.1
+
+
+
+
+
+EOT
+
+ $sp->create_volume($xml);
+}
+
+sub test_volume($vm) {
+
+ return if $vm->type ne 'KVM';
+ my $dom_name = new_domain_name();
+ my $vol_name = new_domain_name();
+ _create_vol($vm, $vol_name);
+ $vm->refresh_storage_pools();
+ return if $vm->type ne 'KVM';
+my $xml =<
+ $dom_name
+ 6f6c9b78-3ce4-4a4e-a025-b1c7ae1965e0
+
+
+
+
+
+ 1273856
+ 1273856
+ 1
+
+ hvm
+
+
+
+
+
+
+
+ qemu64
+
+
+ destroy
+ restart
+ restart
+
+ /usr/bin/qemu-system-x86_64
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+EOT
+
+ $vm->vm->define_domain($xml);
+
+ my $domain;
+ eval {
+ $domain = $RVD_BACK->import_domain(
+ vm => $vm->type
+ ,name => $dom_name
+ ,user => $USER->name
+ );
+ };
+ diag($@) if $@;
+ is(''.$@,'') or exit;
+ ok($domain,"Importing domain $dom_name") or exit;
+
+ my $domain_f = Ravada::Front::Domain->open($domain->id);
+ $domain_f->info(user_admin);
+
+ $domain->remove(user_admin);
+
+}
+
############################################################################
clean();
@@ -169,6 +329,8 @@ for my $vm_name (@VMS) {
diag("Testing import in $vm_name");
test_wrong_args($vm_name, $vm);
+ test_volume($vm);
+
my $domain = test_already_there($vm_name, $vm);
test_import($vm_name, $vm, $domain) if $domain;
diff --git a/t/kvm/uefi.t b/t/kvm/uefi.t
index 101e5f2af..5236d09a5 100644
--- a/t/kvm/uefi.t
+++ b/t/kvm/uefi.t
@@ -16,6 +16,32 @@ use feature qw(signatures);
########################################################################
+sub test_threads($vm) {
+ my $name = new_domain_name();
+ my $id_iso = search_id_iso('Alpine');
+
+ my $n_threads = 4;
+ my $req = Ravada::Request->create_domain(
+ name => $name
+ ,vm => $vm->type
+ ,id_iso => $id_iso
+ ,id_owner => user_admin->id
+ ,memory => 512 * 1024
+ ,disk => 1024 * 1024
+ ,options => { hardware => { cpu => { cpu => { topology => { threads => $n_threads }}}} }
+ );
+ wait_request( debug => 0);
+ my $domain = $vm->search_domain($name);
+ ok($domain);
+ my $config = $domain->xml_description();
+
+ my $doc = XML::LibXML->load_xml(string => $config);
+ my ($topology) = $doc->findnodes("/domain/cpu/topology");
+ ok($topology) or die $domain->name;
+ is($topology->getAttribute('threads'), $n_threads);
+
+}
+
sub test_uefi($vm) {
my $name = new_domain_name();
my $id_iso = search_id_iso('Alpine');
@@ -322,6 +348,8 @@ for my $vm_name ( 'KVM' ) {
diag($msg) if !$vm;
skip $msg,10 if !$vm;
+ test_threads($vm);
+
test_isos($vm);
test_req_machine_types($vm);
test_req_machine_types2($vm);
diff --git a/t/lib/Test/Ravada.pm b/t/lib/Test/Ravada.pm
index b0be1e64d..d693d7232 100644
--- a/t/lib/Test/Ravada.pm
+++ b/t/lib/Test/Ravada.pm
@@ -86,6 +86,8 @@ create_domain
remove_old_users
remove_old_users_ldap
+ remove_qemu_pools
+
mangle_volume
test_volume_contents
test_volume_format
@@ -273,6 +275,7 @@ sub create_domain_v2(%args) {
my $user = (delete $args{user} or $USER_ADMIN);
my $iso_name = delete $args{iso_name};
my $id_iso = delete $args{id_iso};
+ my $disk = delete $args{disk};
croak "Error: supply either iso_name or id_iso ".Dumper(\%args)
if $iso_name && $id_iso;
@@ -298,13 +301,12 @@ sub create_domain_v2(%args) {
if keys %args;
my $iso;
- my $disk;
if ($vm->type eq 'KVM' && (!$iso_name || $iso_name !~ /Alpine/i)) {
$iso = $vm->_search_iso($id_iso);
- $disk = ($iso->{min_disk_size} or 2 );
+ $disk = ($iso->{min_disk_size} or 2 ) if !$disk;
diag("Creating [$id_iso] $iso->{name}");
} else {
- $disk = 2;
+ $disk = 2 if !$disk;
}
if ($vm->type eq 'KVM' && !$options ) {
@@ -1510,6 +1512,7 @@ sub remove_old_storage_pools_void() {
}
sub remove_old_storage_pools_kvm() {
+ remove_qemu_pools();
}
sub remove_old_storage_pools() {
diff --git a/t/mojo/10_login.t b/t/mojo/10_login.t
index d0fe3012a..a13ccf7eb 100644
--- a/t/mojo/10_login.t
+++ b/t/mojo/10_login.t
@@ -300,8 +300,8 @@ sub test_list_ldap_attributes($t, $expected_code=200) {
sub test_login_non_admin_req($t, $base, $clone){
mojo_check_login($t, $USERNAME, $PASSWORD);
+ my $clone2;
for ( 1 .. 3 ) {
- my $clone2;
if (!$clone->is_base) {
mojo_request($t,"shutdown", {id_domain => $clone->id, timeout => 1 });
@@ -310,7 +310,7 @@ sub test_login_non_admin_req($t, $base, $clone){
.$t->tx->res->body()
if $t->tx->res->code() != 200;
- for ( 1 .. 10 ) {
+ for ( 1 .. 60 ) {
$clone2 = rvd_front->search_domain($clone->name);
last if $clone2->is_base || !$clone2->list_requests;
_wait_request(debug => 1, background => 1, check_error => 1);
@@ -318,8 +318,8 @@ sub test_login_non_admin_req($t, $base, $clone){
}
last if $clone2->is_base;
}
- is($clone2->is_base,1,"Expecting ".$clone2->name." is base") or exit;
}
+ is($clone2->is_base,1,"Expecting ".$clone2->name." is base") or exit;
$clone->is_public(1);
my $name = new_domain_name();
@@ -550,9 +550,12 @@ sub test_logout_ldap {
sub _add_displays($t, $domain) {
# mojo_request($t, "add_hardware", { id_domain => $base->id, name => 'network' });
+ mojo_request($t, "refresh_machine", { id_domain => $domain->id });
my $info = $domain->info(user_admin);
my $options = $info->{drivers}->{display};
for my $driver (@$options) {
+ wait_request(background => 1);
+ $info = $domain->info(user_admin);
next if grep { $_->{driver} eq $driver } @{$info->{hardware}->{display}};
my $req = Ravada::Request->add_hardware(
@@ -574,8 +577,13 @@ sub _clone_and_base($vm_name, $t, $base0) {
die "Error: test base $BASE_NAME not found" if !$base;
my $name = new_domain_name()."-".$vm_name."-$$";
mojo_request_url_post($t,"/machine/copy",{id_base => $base->id, new_name => $name, copy_ram => 0.128, copy_number => 1});
- $base1 = rvd_front->search_domain($name);
- ok($base1, "Expecting domain $name create") or exit;
+
+ for ( 1 .. 60 ) {
+ $base1 = rvd_front->search_domain($name);
+ last if $base1;
+ wait_request();
+ }
+ ok($base1, "Expecting domain $name created") or exit;
}
mojo_check_login($t);
@@ -592,8 +600,15 @@ sub _clone_and_base($vm_name, $t, $base0) {
}
sub test_clone($base1) {
- mojo_request($t,"prepare_base", {id_domain => $base1->id })
- if !$base1->is_base();
+ if (!$base1->is_base()) {
+ mojo_request($t,"force_shutdown_domain", {id_domain => $base1->id });
+ mojo_request($t,"prepare_base", {id_domain => $base1->id });
+ }
+
+ for ( 1 .. 10 ) {
+ last if $base1->is_base;
+ sleep 1;
+ }
$t->get_ok("/machine/clone/".$base1->id.".html")->status_is(200);
my $body = $t->tx->res->body;
@@ -617,7 +632,7 @@ sub test_clone($base1) {
like($clone->name, qr/^$clone_name/);
ok($clone->name);
is($clone->is_volatile,0) or exit;
- is(scalar($clone->list_ports),2);
+ is(scalar($clone->list_ports),scalar($base1->list_ports));
return $clone;
}
@@ -727,8 +742,13 @@ sub test_new_machine_default($t, $vm_name, $empty_iso_file=undef) {
my ($swap ) = grep { $_->{file} =~ /SWAP/ } @$disks;
ok($swap,"Expecting a swap disk volume");
- my ($data) = grep { $_->{file} =~ /DATA/ } @$disks;
- ok($data,"Expecting a data disk volume");
+ my $data;
+ for ( 1 .. 10 ) {
+ ($data) = grep { $_->{file} =~ /DATA/ } @$disks;
+ last if $data;
+ sleep 1;
+ }
+ ok($data,"Expecting a data disk volume") or exit;
my ($iso) = grep { $_->{file} =~ /iso$/ } @$disks;
ok($iso,"Expecting an ISO cdrom disk volume");
@@ -757,6 +777,9 @@ sub test_new_machine_advanced_options($t, $vm_name, $swap=undef ,$data=undef) {
}
)->status_is(302);
+ wait_request();
+ my $domain0 = rvd_front->search_domain($name);
+ mojo_request($t,"refresh_machine", {id_domain => $domain0->id });
wait_request();
my $domain = rvd_front->search_domain($name);
@@ -951,6 +974,7 @@ sub test_clone_same_name($t, $base) {
last if $cloneb->is_base;
sleep 1;
}
+ mojo_check_login($t);
$t->get_ok("/machine/clone/".$base->id.".html")
->status_is(200);
diff --git a/t/request/33_hw_cpu.t b/t/request/33_hw_cpu.t
index 2cf02e61b..01f357701 100644
--- a/t/request/33_hw_cpu.t
+++ b/t/request/33_hw_cpu.t
@@ -11,6 +11,9 @@ use Test::Ravada;
no warnings "experimental::signatures";
use feature qw(signatures);
+my $BASE_NAME = "zz-test-base-ubuntu";
+my $BASE;
+
###############################################################################
sub test_add_vcpu($domain) {
my $info = $domain->info(user_admin);
@@ -20,7 +23,7 @@ sub test_add_vcpu($domain) {
my $req = Ravada::Request->change_hardware(
uid => user_admin->id
,id_domain => $domain->id
- ,data => { n_virt_cpu => $n+2 }
+ ,data => { n_virt_cpu => $n+2, max_virt_cpu => $n+3 }
,hardware => 'vcpus'
);
wait_request();
@@ -31,7 +34,9 @@ sub test_add_vcpu($domain) {
return if ($domain->type eq 'Void');
- is($info2->{hardware}->{cpu}->[0]->{vcpu}->{'#text'},$n+2)
+ is($info2->{hardware}->{cpu}->[0]->{vcpu}->{'current'},$n+2);
+
+ is($info2->{hardware}->{cpu}->[0]->{vcpu}->{'#text'},$n+3)
or die $domain->name."\n".Dumper($info2->{hardware}->{cpu});
$domain->start(user_admin);
@@ -40,7 +45,7 @@ sub test_add_vcpu($domain) {
my $req2 = Ravada::Request->change_hardware(
uid => user_admin->id
,id_domain => $domain->id
- ,data => { n_virt_cpu => $n+2 }
+ ,data => { n_virt_cpu => $n+2 , max_virt_cpu => $n+3 }
,hardware => 'vcpus'
);
wait_request();
@@ -49,7 +54,7 @@ sub test_add_vcpu($domain) {
is($domain2->_data('needs_restart'),1) or exit;
my $info3 = $domain->info(user_admin);
- is($info3->{n_virt_cpu},$n);
+ is($info3->{n_virt_cpu},$n+2);
}
@@ -70,7 +75,7 @@ sub test_less_vcpu_down($domain) {
my $domain2 = Ravada::Domain->open($domain->id);
my $info2 = $domain2->info(user_admin);
if ($domain->type eq 'KVM') {
- is($info2->{hardware}->{cpu}->[0]->{vcpu}->{'#text'},$n-1)
+ is($info2->{hardware}->{cpu}->[0]->{vcpu}->{'current'},$n-1)
or die Dumper($info2->{hardware}->{cpu});
}
is($info2->{n_virt_cpu},$n-1) or die $domain->name."\n"
@@ -80,10 +85,14 @@ sub test_less_vcpu_down($domain) {
}
sub test_less_vcpu_up($domain) {
- $domain->start(user_admin);
+ $domain->shutdown(user => user_admin) if $domain->is_active;
+ wait_request();
+
my $info = $domain->info(user_admin);
my $cpu = $info->{hardware}->{cpu};
- my $n = $info->{n_virt_cpu};
+ my $n = $info->{max_virt_cpu};
+
+ $domain->start(user_admin);
my $req = Ravada::Request->change_hardware(
uid => user_admin->id
@@ -96,13 +105,13 @@ sub test_less_vcpu_up($domain) {
my $domain2 = Ravada::Domain->open($domain->id);
my $info2 = $domain2->info(user_admin);
- is($info2->{hardware}->{cpu}->[0]->{vcpu}->{'#text'},$n-1)
+ is($info2->{hardware}->{cpu}->[0]->{vcpu}->{'current'},$n-1)
if $domain->type eq 'KVM';
if ( $domain->type eq 'Void') {
is($info2->{n_virt_cpu},$n-1);
} elsif ($domain->type eq 'KVM') {
- is($info2->{n_virt_cpu},$n);
+ is($info2->{n_virt_cpu},$n-1) or die $domain->name;
}
is($domain2->_data('needs_restart'),1) or exit;
@@ -113,10 +122,11 @@ sub test_less_vcpu_up($domain) {
my $domain3 = Ravada::Domain->open($domain->id);
my $info3 = $domain3->info(user_admin);
- is($info3->{hardware}->{cpu}->[0]->{vcpu}->{'#text'},$n-1)
+ is($info3->{hardware}->{cpu}->[0]->{vcpu}->{'current'}
+ ,$info3->{hardware}->{cpu}->[0]->{vcpu}->{'#text'})
if $domain->type eq 'KVM';
- is($info3->{n_virt_cpu},$n-1) or die $domain->name;
+ is($info3->{n_virt_cpu},$n) if $domain->type eq 'KVM';
$domain->shutdown_now(user_admin);
}
@@ -191,13 +201,307 @@ sub test_add_threads($domain) {
my $req2 = Ravada::Request->change_hardware(
hardware => 'vcpus'
- ,data => { n_virt_cpu => 3 }
+ ,data => { n_virt_cpu => 1 }
,id_domain => $domain->id
,uid => user_admin->id
);
wait_request();
}
+sub _custom_cpu_susana($domain) {
+
+ my $xml = XML::LibXML->load_xml(string => $domain->domain->get_xml_description);
+ my ($vcpu) = $xml->findnodes("/domain/vcpu");
+ $vcpu->setAttribute('placement' => 'static');
+ $vcpu->setAttribute('current' => '2');
+
+ my ($vcpu_max) = $xml->findnodes("/domain/vcpu/text()");
+ $vcpu_max->setData('4');
+
+ my ($cpu) = $xml->findnodes("/domain/cpu");
+ my %data = ( mode => 'custom' , match => 'exact', check => 'partial');
+ for my $field( keys %data) {
+ $cpu->setAttribute( $field => $data{$field});
+ }
+ $cpu->removeChildNodes();
+ my $model = $cpu->addNewChild(undef,'model');
+ $model->setAttribute('fallback' => 'allow');
+ $model->appendText('qemu64');
+
+ $domain->reload_config($xml);
+
+}
+
+sub test_change_vcpu_feature($vm) {
+ return if $vm->type ne 'KVM';
+
+ my $domain = $BASE->clone(name => new_domain_name, user => user_admin);
+ $domain->start(user_admin);
+ _wait_ip($domain);
+ my $info0 = $domain->info(user_admin);
+ my $cpu = $info0->{hardware}->{cpu}->[0];
+ $cpu->{vcpu}->{current}=1;
+ my $req = Ravada::Request->change_hardware(
+ hardware => 'cpu'
+ ,id_domain => $domain->id
+ ,uid => user_admin->id
+ ,data => $cpu
+ );
+ wait_request();
+ my $domain2=Ravada::Front::Domain->open($domain->id);
+ is($domain2->needs_restart(),0) or exit;
+
+ $domain->remove(user_admin);
+}
+
+sub test_current_max_live($vm) {
+ return if $vm->type ne 'KVM';
+
+ my $domain = $BASE->clone(name => new_domain_name, user => user_admin);
+ my $info0 = $domain->info(user_admin);
+
+ my $req = Ravada::Request->change_hardware(
+ hardware => 'vcpus'
+ ,id_domain => $domain->id
+ ,uid => user_admin->id
+ ,data => { n_virt_cpu => 2 , max_virt_cpu => 5 }
+ );
+ wait_request();
+ _wait_ip($domain);
+ is($domain->needs_restart,0);
+ $domain->remove(user_admin);
+}
+
+sub test_current_max($vm) {
+ return if $vm->type ne 'KVM';
+
+ my $domain = $BASE->clone(name => new_domain_name, user => user_admin);
+ my $info0 = $domain->info(user_admin);
+
+ my $max_cpu = 8;
+
+ my $req = Ravada::Request->change_hardware(
+ hardware => 'cpu'
+ ,id_domain => $domain->id
+ ,uid => user_admin->id
+ ,'data' => {
+ '_can_edit' => 1,
+ 'vcpu' => {
+ 'placement' => 'static',
+ '#text' => $max_cpu,
+ },
+ 'cpu'=> $info0->{hardware}->{cpu}->[0]->{cpu}
+ },
+ );
+ wait_request(debug => 0);
+ my $domain2 = Ravada::Front::Domain->open($domain->id);
+ my $info = $domain2->info(user_admin);
+
+ is($info->{hardware}->{cpu}->[0]->{vcpu}->{'#text'},$max_cpu);
+ is($info->{hardware}->{cpu}->[0]->{vcpu}->{'placement'},'static');
+
+ $domain->start(user_admin);
+
+ _wait_ip($domain);
+
+ $req->status('requested');
+ wait_request();
+ is($domain->needs_restart,0);
+
+ my $domain3 = Ravada::Front::Domain->open($domain->id);
+ my $info3 = $domain3->info(user_admin);
+
+ # change current cpu to 2
+ my $req2 = Ravada::Request->change_hardware(
+ hardware => 'cpu'
+ ,id_domain => $domain->id
+ ,uid => user_admin->id
+ ,'data' => {
+ '_can_edit' => 1,
+ 'vcpu' => {
+ 'placement' => 'static',
+ '#text' => $max_cpu,
+ ,'current' => 2
+ },
+ 'cpu'=> $info3->{hardware}->{cpu}->[0]->{cpu}
+ },
+ );
+ wait_request( debug => 0 );
+
+ my $domain22 = Ravada::Domain->open($domain->id);
+ is($domain22->needs_restart,0, $domain22->name) or exit;
+ my $info22 = $domain22->info(user_admin);
+ is($info22->{hardware}->{cpu}->[0]->{vcpu}->{'current'},2)
+ or die $domain->name;
+
+ my $domain22_f = Ravada::Domain->open($domain->id);
+ is($domain22_f->needs_restart,0) or exit;
+ my $info22_f = $domain22_f->info(user_admin);
+ is($info22_f->{hardware}->{cpu}->[0]->{vcpu}->{'current'},2)
+ or die $domain->name;
+
+ # now we req increase up , it should need restart and info increases too
+ my $req3 = Ravada::Request->change_hardware(
+ hardware => 'cpu'
+ ,id_domain => $domain->id
+ ,uid => user_admin->id
+ ,'data' => {
+ '_can_edit' => 1,
+ 'vcpu' => {
+ 'placement' => 'static',
+ '#text' => $max_cpu+1,
+ ,'current' => 2
+ },
+ 'cpu'=> $info0->{hardware}->{cpu}->[0]->{cpu}
+ },
+ );
+ wait_request(debug => 0);
+
+ my $domain4 = Ravada::Front::Domain->open($domain->id);
+ is($domain4->needs_restart,1) or exit;
+
+ my $info4 = $domain4->info(user_admin);
+ is($info4->{hardware}->{cpu}->[0]->{vcpu}->{current},2) or die $domain4->name;
+ is($info4->{hardware}->{cpu}->[0]->{vcpu}->{'#text'}, $max_cpu+1) or die $domain3->name;
+
+ $domain->remove(user_admin);
+}
+
+sub test_needs_restart($vm) {
+ return if $vm->type ne 'KVM';
+
+ my $domain = $BASE->clone(name => new_domain_name, user => user_admin);
+ my $info = $domain->info(user_admin);
+ my $cpu = $info->{hardware}->{cpu}->[0];
+ $cpu->{vcpu}->{'#text'} = 6;
+ $cpu->{vcpu}->{'current'} = 4;
+
+ my $req0 = Ravada::Request->change_hardware(
+ hardware => 'cpu'
+ ,id_domain => $domain->id
+ ,uid => user_admin->id
+ ,'data' => $cpu
+ );
+
+ _wait_ip($domain);
+ is($domain->needs_restart(),0);
+ my $req = Ravada::Request->change_hardware(
+ hardware => 'cpu'
+ ,id_domain => $domain->id
+ ,uid => user_admin->id
+ ,'data' => $cpu
+ );
+ wait_request(debug => 0);
+ is($domain->needs_restart(),0);
+
+ $cpu->{vcpu}->{current} = 1;
+
+ my $req2 = Ravada::Request->change_hardware(
+ hardware => 'cpu'
+ ,id_domain => $domain->id
+ ,uid => user_admin->id
+ ,'data' => $cpu
+ );
+ wait_request(debug => 0);
+ is($domain->needs_restart(),0);
+
+ $domain->remove(user_admin);
+}
+
+sub _wait_ip($domain) {
+ $domain->start(user_admin) if !$domain->is_active;
+ for my $n ( 1 .. 120 ) {
+ return if $domain->ip;
+ sleep 1;
+ diag("Waiting for ip in ".$domain->name) if ! $n%10;
+ }
+}
+
+sub test_change_vcpu_topo($vm) {
+ return if $vm->type ne 'KVM';
+
+ my $domain = create_domain($vm);
+ _custom_cpu_susana($domain);
+
+ my %data = (
+ 'hardware' => 'cpu',
+ 'id_domain' => $domain->id,
+ 'uid' => user_admin->id,
+ 'index' => 0,
+ 'data' => {
+ '_can_edit' => 1,
+ 'vcpu' => {
+ 'placement' => 'static',
+ '#text' =>undef
+ },
+ '_cat_remove' => 0,
+ '_order' => 0,
+ 'cpu' => {
+ 'model' => {
+ '#text' => 'qemu64',
+ 'fallback' => 'allow'
+ },
+ 'check' => 'partial',
+ 'match' => 'exact',
+ 'mode' => 'custom',
+ 'topology' => {
+ 'sockets' => 2
+ },
+ 'feature' => [
+ {
+ 'name' => 'svm',
+ '$$hashKey' => 'object:64',
+ 'policy' => 'optional'
+ }
+ ]
+ }
+ }
+ );
+ my $req = Ravada::Request->change_hardware(%data);
+ wait_request();
+
+ my $domain2 = Ravada::Front::Domain->open($domain->id);
+ my $info = $domain2->info(user_admin);
+ is($info->{n_virt_cpu},2);
+ is($info->{hardware}->{cpu}->[0]->{vcpu}->{'#text'},2);
+
+ _custom_cpu_susana($domain);
+ delete $data{data}->{cpu}->{topology};
+ $data{data}->{vcpu}->{'#text'}=3;
+
+ Ravada::Request->change_hardware(%data);
+ wait_request(debug => 0);
+
+ my $domain3 = Ravada::Front::Domain->open($domain->id);
+ my $info3 = $domain3->info(user_admin);
+ is($info3->{n_virt_cpu},3) or die $domain3->name;
+
+ is($info3->{hardware}->{cpu}->[0]->{vcpu}->{'#text'},3);
+
+ _custom_cpu_susana($domain);
+
+ $data{data}->{max_virt_cpu} = 6;
+ $data{data}->{n_virt_cpu} = 5;
+ $data{hardware} = 'vcpus';
+ delete $data{data}->{vcpu};
+ delete $data{data}->{cpu};
+
+ my $req3 = Ravada::Request->change_hardware(%data);
+ wait_request(debug => 0);
+
+ my $domain4 = Ravada::Front::Domain->open($domain->id);
+ my $info4 = $domain4->info(user_admin);
+ is($info4->{n_virt_cpu},5) or die $domain->name;
+ is($info4->{max_virt_cpu},6);
+ is($info4->{hardware}->{cpu}->[0]->{vcpu}->{'#text'},6);
+ is($info4->{hardware}->{cpu}->[0]->{vcpu}->{'current'},5);
+
+ my $xml = XML::LibXML->load_xml(string => $domain->domain->get_xml_description);
+ my ($cpu) = $xml->findnodes("/domain/vcpu");
+
+ $domain->remove(user_admin);
+}
+
###############################################################################
clean();
@@ -217,6 +521,20 @@ for my $vm_name ( vm_names() ) {
}
skip($msg,10) if !$vm;
+
+ if ($vm_name eq 'KVM') {
+ $BASE = import_domain($vm, $BASE_NAME, 1);
+ }
+
+ test_current_max($vm);
+
+ test_change_vcpu_feature($vm);
+ test_change_vcpu_topo($vm);
+
+ test_current_max_live($vm);
+
+ test_needs_restart($vm);
+
my $domain = create_domain($vm);
test_add_vcpu($domain);
test_less_vcpu_down($domain);
diff --git a/t/vm/10_domain.t b/t/vm/10_domain.t
index 94f56efdb..f32451d0c 100644
--- a/t/vm/10_domain.t
+++ b/t/vm/10_domain.t
@@ -223,6 +223,23 @@ sub test_open {
is($domain2->vm, $domain->vm);
}
+sub test_shutdown_start($domain) {
+ $domain->start(user_admin);
+ $domain->needs_restart(1);
+ is($domain->needs_restart,1);
+ my $req = Ravada::Request->shutdown_start(
+ uid => user_admin->id
+ ,id_domain => $domain->id
+ ,timeout => 10
+ );
+ wait_request(debug => 0);
+ wait_request(debug => 0);
+ my $domain2 = Ravada::Front::Domain->open($domain->id);
+ is($domain2->needs_restart,0);
+ is($domain2->is_active,1);
+ $domain->shutdown_now(user_admin);
+}
+
sub test_manage_domain {
my $vm_name = shift;
my $domain = shift;
@@ -605,8 +622,8 @@ sub test_permissions {
my ($stat) = @_;
for my $vol ( keys %$stat ) {
my @stat_new = stat($vol);
- my $mode = sprintf('%o',$stat_new[2] & 07777);
- my $mode_expected = sprintf('%o',$stat->{$vol}->[2] & 07777);
+ my $mode = sprintf('%o',$stat_new[2] & oct(7777));
+ my $mode_expected = sprintf('%o',$stat->{$vol}->[2] & oct(7777));
is($mode, $mode_expected);
is($stat_new[4],$stat->{$vol}->[4]);
is($stat_new[5],$stat->{$vol}->[5]);
@@ -672,6 +689,8 @@ for my $vm_name ( vm_names() ) {
my $domain = test_create_domain($vm_name, $host);
test_open($vm_name, $domain);
+ test_shutdown_start($domain);
+
test_description($vm_name, $domain);
test_change_interface($vm_name,$domain);
ok($domain->has_clones==0,"[$vm_name] has_clones expecting 0, got ".$domain->has_clones);
diff --git a/t/vm/20_base.t b/t/vm/20_base.t
index a87be9541..fe9d12073 100644
--- a/t/vm/20_base.t
+++ b/t/vm/20_base.t
@@ -300,17 +300,15 @@ sub test_displays_added_on_refresh($domain, $n_expected, $delete=1) {
_wait_ip($domain);
+ my $domain_f0 = Ravada::Front::Domain->open($domain->id);
+ my $display0_raw = $domain_f0->info(user_admin)->{hardware}->{display};
+
+ my @display0 = grep { $_->{is_secondary} == 0 } @$display0_raw;
+ my $n_expected2 = scalar(@display0);
+
if ($delete) {
my $sth = connector->dbh->prepare("DELETE FROM domain_displays WHERE id_domain=?");
$sth->execute($domain->id);
- }else {
- my $sth = connector->dbh->prepare("SELECT count(*) FROM domain_displays WHERE id_domain=?");
- $sth->execute($domain->id);
- my ($n_exp2) = $sth->fetchrow;
- if ($n_exp2 && $n_expected != $n_exp2) {
- cluck "n_expected was $n_expected, but it should be $n_exp2";
- $n_expected = $n_exp2;
- }
}
my $req;
for ( 1 .. 3 ) {
@@ -333,7 +331,7 @@ sub test_displays_added_on_refresh($domain, $n_expected, $delete=1) {
my $count;
for ( 1 .. 10 ) {
my $sth_count = connector->dbh->prepare(
- "SELECT count(*) FROM domain_displays WHERE id_domain=?");
+ "SELECT count(*) FROM domain_displays WHERE id_domain=? AND is_secondary=0");
$sth_count->execute($domain->id);
($count) = $sth_count->fetchrow;
last if $count;
@@ -344,11 +342,13 @@ sub test_displays_added_on_refresh($domain, $n_expected, $delete=1) {
);
wait_request();
}
- ok($count>=$n_expected,"Got $count, expecting >$n_expected displays on table domain_displays for ".$domain->name) or confess;
+ ok($count>=$n_expected || $count >=$n_expected2,"Got $count, expecting >$n_expected or $n_expected2 displays on table domain_displays for ".$domain->name) or confess;
my $domain_f = Ravada::Front::Domain->open($domain->id);
- my $display = $domain_f->info(user_admin)->{hardware}->{display};
- is(scalar(@$display), $n_expected,"Expecting $n_expected displays on info->{hardware}->{display} in ".$domain->name) or confess Dumper($display);
+ my $display_raw = $domain_f->info(user_admin)->{hardware}->{display};
+ my @display = grep { $_->{is_secondary} == 0 } @$display_raw;
+ ok(scalar(@display) == $n_expected || scalar(@display) == $n_expected2
+ ,"Expecting $n_expected or $n_expected2 displays on info->{hardware}->{display} in ".$domain->name) or confess Dumper(@display);
}
@@ -2007,7 +2007,7 @@ sub test_already_requested_working($vm) {
wait_request( request => [ $req->id, $req2->id]);
is($req->status, 'done');
is($req2->status, 'done');
- is($req->error, '');
+ like($req->error,qr/waiting/) if $req->error;
is($req2->error, '');
$domain->remove(user_admin);
diff --git a/t/vm/40_volumes.t b/t/vm/40_volumes.t
index 4b4666b2b..d12527180 100644
--- a/t/vm/40_volumes.t
+++ b/t/vm/40_volumes.t
@@ -5,6 +5,7 @@ use Carp qw(confess croak);
use Data::Dumper;
use File::Copy;
use Test::More;
+use YAML qw(DumpFile);
use v5.22; use feature qw(signatures);
no warnings "experimental::signatures";
@@ -300,9 +301,7 @@ sub test_add_volume_path {
my $file_path = $vm->dir_img."/mock.img";
- open my $out,'>',$file_path or die "$! $file_path";
- print $out "hi\n";
- close $out;
+ DumpFile($file_path, {capacity => 100, data => 'hi'});
$domain->add_volume(file => $file_path);
diff --git a/t/vm/92_ports.t b/t/vm/92_ports.t
index 398919cbc..2c630f617 100644
--- a/t/vm/92_ports.t
+++ b/t/vm/92_ports.t
@@ -243,8 +243,7 @@ sub test_one_port($vm) {
, 'to-destination' => $domain_ip.":".$internal_port
);
last if $n_rule;
- $domain->start(user => user_admin, remote_ip => $remote_ip
- ,_force => 1);
+ $domain->start(user => user_admin, remote_ip => $remote_ip);
wait_request();
}
@@ -1265,7 +1264,7 @@ sub test_open_port_duplicated($vm) {
sleep 1;
}
is($req->status,'done');
- is($req->error, '') or exit;
+ like($req->error,qr/checking /) if $req->error;
my (@out3,@open3);
for ( 1 .. 5 ) {
diff --git a/t/vm/s30_storage.t b/t/vm/s30_storage.t
index 2ea8e4b46..a13f2de23 100644
--- a/t/vm/s30_storage.t
+++ b/t/vm/s30_storage.t
@@ -85,6 +85,48 @@ sub _clean_local {
my $file = "$dir/check_storage";# or die "$!";
unlink $file or die "$! $file" if -e $file;
}
+
+sub test_storage_full($vm) {
+
+ my $dir = "/run";
+ $dir.="/user/".$< if $<;
+ my $storage_name = new_domain_name();
+
+ $dir.= "/".$storage_name;
+
+ mkdir $dir or die "$! $dir" if ! -e $dir;
+
+ if (! grep { $_ eq $storage_name} $vm->list_storage_pools) {
+ $vm->create_storage_pool($storage_name,$dir);
+ }
+
+ my($out,$err) = $vm->run_command("df");
+
+ my ($available) = $out =~ m{(\d+)\s+\d+\% /run}ms;
+
+ $available = $available*10;
+
+ $vm->default_storage_pool_name($storage_name);
+
+ my $name = new_domain_name;
+ my $req = Ravada::Request->create_domain(
+ vm => $vm->type
+ ,id_owner => user_admin->id
+ ,name => $name
+ ,disk => $available
+ ,storage => 'default'
+ ,id_iso => search_id_iso('Alpine%64')
+ );
+ wait_request(debug => 0);
+
+ my $domain = $vm->search_domain($name);
+ for my $vol ($domain->list_volumes) {
+ next if $vol =~ /iso$/;
+ unlike($vol,qr{^/run});
+ }
+
+}
+
###########################################################
_clean_local();
@@ -101,6 +143,12 @@ for my $vm_name (vm_names() ) {
}
skip($msg,10) if !$vm;
+
+ if ($vm->type eq 'KVM') {
+ remove_qemu_pools($vm);
+ }
+ test_storage_full($vm);
+
test_storage_pools($vm);
test_storage_pools_fail($vm);
}
diff --git a/templates/bootstrap/navigation.html.ep b/templates/bootstrap/navigation.html.ep
index dbf6ceeef..c43e2f38e 100644
--- a/templates/bootstrap/navigation.html.ep
+++ b/templates/bootstrap/navigation.html.ep
@@ -58,7 +58,7 @@ navbar-dark bg-dark fixed-top navbar-expand-lg navbar-inverse">
% if ( !$_anonymous ) {
<%=l 'Help' %>
-