diff --git a/etc/rvd_front.conf.example b/etc/rvd_front.conf.example index 369aa24dc..69d9f5d9b 100644 --- a/etc/rvd_front.conf.example +++ b/etc/rvd_front.conf.example @@ -37,10 +37,4 @@ ,file => '/var/log/ravada/rvd_front.log' ,level => 'debug' } -# Insert widget in /js/custom/insert_here_widget.js -# this widget embed js in templates/bootstrap/scripts.html.ep - ,widget => '' -# Content-Security-Policy HTTP response header helps you reduce XSS risks -# define custom directives. More info https://content-security-policy.com/ - ,security_policy => 'foo.bar.com' }; diff --git a/lib/Ravada.pm b/lib/Ravada.pm index cc0f18d64..56c699974 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -2572,6 +2572,7 @@ sub _sql_insert_defaults($self){ } ,{ id_parent => $id_frontend ,name => "widget" + ,value => $conf->{widget} } ,{ id_parent => $id_frontend @@ -3932,6 +3933,7 @@ sub _timeout_requests($self) { ." FROM requests " ." WHERE ( status = 'working' or status = 'stopping' )" ." AND date_changed >= ? " + ." AND command <> 'move_volume'" ." ORDER BY date_req " ); $sth->execute(_date_now(-30)); @@ -4067,6 +4069,7 @@ sub _kill_dead_process($self) { ." AND ( status like 'working%' OR status like 'downloading%'" ." OR status like 'start%' ) " ." AND pid IS NOT NULL " + ." AND command <> 'move_volume'" ); $sth->execute(time - 2); while (my ($id, $pid, $command, $start_time) = $sth->fetchrow) { @@ -4172,7 +4175,7 @@ sub _execute { return; } - $self->_wait_pids; + $self->_wait_pids(); return if !$self->_can_fork($request); my $pid = fork(); @@ -4527,7 +4530,9 @@ sub _wait_pids($self) { my @done; for my $type ( keys %{$self->{pids}} ) { for my $pid ( keys %{$self->{pids}->{$type}}) { + next if kill(0,$pid); my $kid = waitpid($pid , WNOHANG); + next if kill(0,$pid); push @done, ($pid) if $kid == $pid || $kid == -1; } } @@ -6311,6 +6316,7 @@ sub _req_method { ,import_domain => \&_cmd_import ,list_unused_volumes => \&_cmd_list_unused_volumes ,remove_files => \&_cmd_remove_files + ,move_volume => \&_cmd_move_volume ,update_iso_urls => \&_cmd_update_iso_urls ,list_networks => \&_cmd_list_virtual_networks @@ -6857,6 +6863,45 @@ sub _cmd_create_storage_pool($self, $request) { } +sub _cmd_move_volume($self, $request) { + + my $user = Ravada::Auth::SQL->search_by_id($request->args('uid')); + die "Error: ".$user->name." not authorized to move volumes" + if !$user->is_admin; + + my $domain = Ravada::Domain->open($request->args('id_domain')); + die "Error: I can not move volume while machine running ".$domain->name."\n" + if $domain->is_active; + + my $volume = $request->args('volume'); + my @volumes = $domain->list_volumes_info(); + my $found; + my $n_found = 0; + for my $vol (@volumes) { + if ($vol->file eq $volume ) { + $found = $vol; + last; + } + $n_found++; + } + die "Volume $volume not found in ".$domain->name."\n".Dumper([map { $_->file } @volumes]) if !$found; + + my $vm = $domain->_vm; + my $storage = $request->args('storage'); + my $dst_path = $vm->_storage_path($storage); + my ($filename) = $volume =~ m{.*/(.*)}; + my $dst_vol = "$dst_path/$filename"; + + die "Error: file '$dst_vol' already exists in ".$vm->name."\n" if $vm->file_exists($dst_vol); + + my $new_file = $vm->copy_file_storage($volume, $storage); + + $domain->change_hardware('disk', $n_found, { file => $new_file }); + if ($volume !~ /\.iso$/) { + $vm->remove_file($volume); + } +} + =head2 set_debug_value Sets debug global variable from setting diff --git a/lib/Ravada/Domain/KVM.pm b/lib/Ravada/Domain/KVM.pm index aeb5e05b2..b019b1b32 100644 --- a/lib/Ravada/Domain/KVM.pm +++ b/lib/Ravada/Domain/KVM.pm @@ -392,7 +392,8 @@ sub _disk_device($self, $with_info=undef, $attribute=undef, $value=undef) { my ($boot_node) = $disk->findnodes('boot'); my $info = {}; - eval { $info = $self->_volume_info($file) if $file && $device eq 'disk' }; + eval { $info = $self->_volume_info($file) + if $file && $device eq 'disk' or $device eq 'cdrom' }; die $@ if $@ && $@ !~ /not found/i; $info->{device} = $device; if (!$info->{name} ) { @@ -437,7 +438,7 @@ sub _pool_refresh($pool) { eval { $pool->refresh }; return if !$@; - return if ref($@) && $@->code == 1; + return if ref($@) && ($@->code == 1 || $@->code == 55 );#55: not active; warn "WARNING: on vol remove , pool refresh $@" if $@; sleep 1; @@ -450,11 +451,16 @@ sub _volume_info($self, $file, $refresh=0) { my ($name) = $file =~ m{.*/(.*)}; my $vol; + my $storage_pool; for my $pool ( $self->_vm->vm->list_storage_pools ) { _pool_refresh($pool) if $refresh; eval { $vol = $pool->get_volume_by_name($name) }; warn $@ if $@ && $@ !~ /^libvirt error code: 50,/; - last if $vol; + if ( $vol ) { + next if $vol->get_path ne $file; + $storage_pool = $pool->get_name(); + last; + } } if (!$vol && !$refresh) { return $self->_volume_info($file, ++$refresh); @@ -470,6 +476,7 @@ sub _volume_info($self, $file, $refresh=0) { warn "WARNING: $@" if $@ && $@ !~ /^libvirt error code: 50,/; $info->{file} = $file; $info->{name} = $name; + $info->{storage_pool} = $storage_pool; return $info; } diff --git a/lib/Ravada/Domain/Void.pm b/lib/Ravada/Domain/Void.pm index 82c48f146..7a07f59b2 100644 --- a/lib/Ravada/Domain/Void.pm +++ b/lib/Ravada/Domain/Void.pm @@ -553,7 +553,20 @@ sub _create_volume($self, $file, $format, $data=undef) { confess "Undefined format" if !defined $format; if ($format =~ /iso|raw|void/) { $data->{format} = $format; - $self->_vm->write_file($file, Dump($data)), + if ( $format eq 'raw' && $data->{capacity} && $self->is_local) { + my $capacity = Ravada::Utils::number_to_size($data->{capacity}); + my ($count,$unit) = $capacity =~ /^(\d+)(\w)$/; + die "Error, I can't find count and unit from $capacity" + if !$count || !$unit; + + my @cmd = ("dd","if=/dev/zero","of=$file","count=$count","bs=1$unit" + ,"status=none"); + my ($in, $out, $err); + run3(\@cmd, \$in, \$out, \$err); + warn "@cmd $err" if $err; + } else { + $self->_vm->write_file($file, Dump($data)), + } } elsif ($format eq 'qcow2') { my @cmd = ('qemu-img','create','-f','qcow2', $file, $data->{capacity}); my ($out, $err) = $self->_vm->run_command(@cmd); @@ -679,6 +692,7 @@ sub list_volumes_info($self, $attribute=undef, $value=undef) { } else { $dev->{driver}->{type} = 'void'; } + $dev->{storage_pool} = $self->_vm->_find_storage_pool($dev->{file}); my $vol = Ravada::Volume->new( file => $dev->{file} ,info => $dev diff --git a/lib/Ravada/Request.pm b/lib/Ravada/Request.pm index b18735093..4fc966c02 100644 --- a/lib/Ravada/Request.pm +++ b/lib/Ravada/Request.pm @@ -166,12 +166,14 @@ our %VALID_ARG = ( ,update_iso_urls => { uid => 1 } ,list_unused_volumes => {uid => 1, id_vm => 1, start => 2, limit => 2 } ,remove_files => { uid => 1, id_vm => 1, files => 1 } - + ,move_volume => { uid => 1, id_domain => 1, volume => 1, storage => 1 } + ,list_networks => { uid => 1, id_vm => 1} ,new_network => { uid => 1, id_vm => 1, name => 2 } ,create_network => { uid => 1, id_vm => 1, data => 1 } ,remove_network => { uid => 1, id => 1, id_vm => 2, name => 2 } ,change_network => { uid => 1, data => 1 } + ); $VALID_ARG{shutdown} = $VALID_ARG{shutdown_domain}; @@ -223,7 +225,7 @@ our %COMMAND = ( # list from low to high priority ,disk_low_priority => { limit => 2 - ,commands => ['rsync_back','check_storage', 'refresh_vms'] + ,commands => ['rsync_back','check_storage', 'refresh_vms','move_volume'] ,priority => 30 } ,disk => { diff --git a/lib/Ravada/VM.pm b/lib/Ravada/VM.pm index f8b93cecf..9abfce117 100644 --- a/lib/Ravada/VM.pm +++ b/lib/Ravada/VM.pm @@ -12,6 +12,7 @@ Ravada::VM - Virtual Managers library for Ravada use utf8; use Carp qw( carp confess croak cluck); use Data::Dumper; +use File::Copy qw(copy); use File::Path qw(make_path); use Hash::Util qw(lock_hash); use IPC::Run3 qw(run3); @@ -162,6 +163,8 @@ around 'remove_network' => \&_around_remove_network; around 'list_virtual_networks' => \&_around_list_networks; around 'change_network' => \&_around_change_network; +around 'copy_file_storage' => \&_around_copy_file_storage; + ############################################################# # # method modifiers @@ -2249,6 +2252,42 @@ sub shared_storage($self, $node, $dir) { return $shared; } + +=head2 copy_file_storage + +Copies a volume file to another storage + +Args: + +=over + +=item * file + +=item * storage + +=back + +=cut + +sub copy_file_storage($self, $file, $storage) { + die "Error: file '$file' does not exist" if !$self->file_exists($file); + + my ($pool) = grep { $_->{name} eq $storage } $self->list_storage_pools(1); + die "Error: storage pool $storage does not exist" if !$pool; + + my $path = $pool->{path}; + + die "TODO remote" if !$self->is_local; + + copy($file, $path) or die "$! $file -> $path"; + + my ($filename) = $file =~ m{.*/(.*)}; + die "Error: file '$file' not copied to '$path'" if ! -e "$path/$filename"; + + return "$path/$filename"; + +} + sub _fetch_tls_host_subject($self) { return '' if !$self->dir_cert(); @@ -2790,6 +2829,28 @@ sub list_unused_volumes($self) { return @vols; } +sub _around_copy_file_storage($orig, $self, $file, $storage) { + my $sth = $self->_dbh->prepare("SELECT id,info FROM volumes" + ." WHERE file=? " + ); + $sth->execute($file); + my ($id,$infoj) = $sth->fetchrow; + + my $new_file = $self->$orig($file, $storage); + + if ($id) { + my $info = decode_json($infoj); + $info->{file} = $new_file; + my $sth_update = $self->_dbh->prepare( + "UPDATE volumes set info=?,file=?" + ." WHERE id=?" + ); + $sth_update->execute(encode_json($info), $new_file, $id); + } + + return $new_file; +} + 1; diff --git a/lib/Ravada/VM/KVM.pm b/lib/Ravada/VM/KVM.pm index edfebba89..412e63bc4 100644 --- a/lib/Ravada/VM/KVM.pm +++ b/lib/Ravada/VM/KVM.pm @@ -15,6 +15,7 @@ use Data::Dumper; use Digest::MD5; use Encode; use Encode::Locale; +use File::Copy qw(copy); use File::Path qw(make_path); use Fcntl qw(:flock O_WRONLY O_EXCL O_CREAT); use Hash::Util qw(lock_hash); @@ -2916,6 +2917,46 @@ sub _is_ip_nat($self, $ip0) { return 0; } +sub copy_file_storage($self, $file, $storage) { + my $vol = $self->search_volume($file); + die "Error: volume $file not found" if !$vol; + + my $sp = $self->vm->get_storage_pool_by_name($storage); + die "Error: storage pool $storage not found" if !$sp; + + my ($name) = $vol->get_name(); + my $xml = $vol->get_xml_description(); + my $doc = XML::LibXML->load_xml(string => $xml); + + my $vol_capacity = $vol->get_info()->{capacity}; + + my $pool_capacity = $sp->get_info()->{capacity}; + + die "Error: '$file' too big to fit in $storage ".Ravada::Utils::number_to_size($vol_capacity)." > ".Ravada::Utils::number_to_size($pool_capacity)."\n" + if $vol_capacity>$pool_capacity; + + my ($format) = $doc->findnodes("/volume/target/format"); + if ($format ne 'qcow2') { + die "Error: I can't copy $format on remote nodes" + unless $self->is_local; + + my $dst_file = $self->_storage_path($storage)."/".$name; + copy($file,$dst_file); + $self->refresh_storage(); + return $dst_file; + } + + my $vol_dst; + eval { $vol_dst= $sp->get_volume_by_name($name) }; + die $@ if $@ && !(ref($@) && $@->code == 50); + + warn 1; + $vol_dst= $sp->clone_volume($vol->get_xml_description); + warn 2; + + return $vol_dst->get_path(); +} + sub get_library_version($self) { return $self->vm->get_library_version(); } diff --git a/lib/Ravada/VM/Void.pm b/lib/Ravada/VM/Void.pm index 046133624..b509c7d76 100644 --- a/lib/Ravada/VM/Void.pm +++ b/lib/Ravada/VM/Void.pm @@ -599,6 +599,25 @@ sub _init_storage_pool_default($self) { } +sub _find_storage_pool($self, $file) { + + my ($path) = $file =~ m{(.*)/}; + + return $self->{_storage_pool_path}->{$path} + if $self->{_storage_pool_path} && exists $self->{_storage_pool_path}->{$path}; + + my $found; + for my $sp ($self->list_storage_pools(1)) { + if ($sp->{path} eq $path) { + $found = $sp->{name}; + last; + } + } + return '' if !$found; + $self->{_storage_pool_path}->{$path} = $found; + return $found; +} + sub list_storage_pools($self, $info=0) { my @list; my $config_dir = Ravada::Front::Domain::Void::_config_dir(); diff --git a/public/js/ravada.js b/public/js/ravada.js index 55ea2a5a0..89120e38c 100644 --- a/public/js/ravada.js +++ b/public/js/ravada.js @@ -320,6 +320,7 @@ $scope.lock_info = false; $scope.topology = false; $scope.searching_ldap_attributes = true; + $scope.storage_pools=['default']; $scope.getUnixTimeFromDate = function(date) { date = (date instanceof Date) ? date : date ? new Date(date) : new Date(); @@ -589,6 +590,10 @@ $http.get('/list_storage_pools/'+$scope.showmachine.type+"?active=1") .then(function(response) { $scope.list_storage= response.data; + + for (var i = 0; i < response.data.length; i++) { + $scope.storage_pools[i]=response.data[i].name; + } }); } list_interfaces(); @@ -756,6 +761,17 @@ .then(function(response) { }); }; + $scope.move_file_storage = function() { + $http.post('/request/move_volume/' + , JSON.stringify({ 'id_domain': $scope.showmachine.id + ,'volume': $scope.sp_move.file + ,'storage': $scope.sp_move.storage_pool + }) + ).then(function(response) { + console.log(response.data); + }); + + } $scope.copy_machine = function() { $scope.copy_request= { 'status': 'requested' }; $http.post('/machine/copy/' @@ -1174,6 +1190,15 @@ }); }; + $scope.shutdown= function() { + $scope.set_edit(); + $scope.lock_info=false; + $http.get("/machine/shutdown/"+$scope.showmachine.id+".json") + .then(function(response) { + }); + }; + + $scope.shutdown_start = function() { $scope.set_edit(); $scope.lock_info=false; diff --git a/t/17_templates.t b/t/17_templates.t index b8528c6e7..bb6b42aa5 100644 --- a/t/17_templates.t +++ b/t/17_templates.t @@ -133,8 +133,22 @@ sub test_form_new_machine() { } +sub test_copyright() { + my @now = localtime(time); + my $current_year = $now[5]+1900; + my $file = "templates/bootstrap/footer.html.ep"; + open my $in,"<",$file or die "$! $file"; + while (my $line = <$in>) { + my ($year) = $line =~ /Copyright.*\d+\s+\-\s+(\d+)/; + next if !defined $year; + is($year, $current_year); + } + close $in; +} + ##################################################################3 +test_copyright(); test_form_new_machine(); test_validate_html_local("templates/bootstrap"); test_validate_html_local("templates/main"); diff --git a/t/lib/Test/Ravada.pm b/t/lib/Test/Ravada.pm index 60b83ccb6..b7af19e6f 100644 --- a/t/lib/Test/Ravada.pm +++ b/t/lib/Test/Ravada.pm @@ -60,6 +60,7 @@ create_domain connector init_ldap_config + create_ram_fs create_storage_pool local_ips @@ -1489,13 +1490,12 @@ sub remove_qemu_pools($vm=undef) { } } - my $base = base_pool_name(); + my $base_pool = base_pool_name(); + my $base = base_domain_name(); $vm->connect(); for my $pool ( $vm->vm->list_all_storage_pools) { my $name = $pool->get_name; - next if $name !~ qr/^$base/; - diag($name); - + next if $name !~ /^($base_pool|$base)/; eval {$pool->build(Sys::Virt::StoragePool::BUILD_NEW); $pool->create() }; warn $@ if $@ && $@ !~ /already active/; if ($pool->is_active) { @@ -1590,6 +1590,7 @@ sub clean($ldap=undef) { _remove_old_groups_ldap(); remove_old_user_ldap(); remove_old_storage_pools(); + remove_old_ram_fs(); if ($file_remote_config) { my $config; @@ -2973,4 +2974,83 @@ sub ping_backend() { return rvd_front->ping_backend(); } +sub _dir_findmnt($dir) { + my @cmd = ("findmnt"); + my ($in, $out, $err); + run3(\@cmd, \$in, \$out, \$err); + + for my $line ( split /\n/,$out) { + next if $line !~ /var.tmp/; + return 1 if $line =~ /$dir/; + } +} + +sub _dir_mounted($dir) { + open my $in,"<","/proc/mounts" or die $!; + while (my $line = <$in>) { + chomp $line; + my ($type,$dir_m) = split /\s+/,$line; + return 1 if $dir eq $dir_m; + } + close $in; + return _dir_findmnt($dir); +} + +sub _umount_old_ram_fs() { + my $base_pool = base_pool_name(); + open my $in,"<","/proc/mounts" or die $!; + while (my $line = <$in>) { + my ($dev,$dir) = split /\s+/,$line; + if ($dir =~ m{/$base_pool}) { + `umount $dir`; + } + } +} + +sub _remove_old_ram_fs_dev() { + my $base_pool = base_pool_name(); + + opendir my $ls,"/dev" or die $!; + while (my $file = readdir $ls) { + unlink "/dev/$file" or die "$! /dev/$file" + if $file =~ /^$base_pool/; + } + closedir $ls; +} + +sub remove_old_ram_fs() { + _umount_old_ram_fs(); + _remove_old_ram_fs_dev(); +} + +sub create_ram_fs($dir=undef,$size=1024*1024) { + if (!$dir ) { + $dir = "/var/tmp/$"; + mkdir $dir if ! -e $dir; + + $dir .= new_pool_name(); + } + mkdir $dir if ! -e $dir; + + if (_dir_mounted($dir)) { + my @cmd =("umount",$dir); + my ($in, $out, $err); + run3(\@cmd, \$in, \$out, \$err); + die $err if $err; + } + + my ($name) = $dir =~ m{.*/(.*)}; + my $dev = "/dev/$name"; + my @cmd = ("mkfs","-q", $dev, $size); + my ($in, $out, $err); + run3(\@cmd, \$in, \$out, \$err); + die $err if $err; + + @cmd = ("mount",$dev,$dir); + run3(\@cmd, \$in, \$out, \$err); + die $err if $err; + + return ($dir,$size, $dev); +} + 1; diff --git a/t/storage_move.t b/t/storage_move.t new file mode 100644 index 000000000..d1bad3f68 --- /dev/null +++ b/t/storage_move.t @@ -0,0 +1,242 @@ +use warnings; +use strict; + +use Carp qw(confess); +use Data::Dumper; +use IPC::Run3 qw(run3); +use Test::More; + +use lib 't/lib'; +use Test::Ravada; + +no warnings "experimental::signatures"; +use feature qw(signatures); + +my $DIR_TMP = "/var/tmp/$$"; +mkdir $DIR_TMP if ! -e $DIR_TMP; + +######################################################################## + +sub _create_storage_pool($vm, $dir=undef) { + + my $name; + if (!defined $dir) { + $name = new_pool_name(); + $dir = "$DIR_TMP/$name"; + } else { + ($name) = $dir =~ m{.*/(.*)}; + } + mkdir $dir if ! -e $dir; + + my ($old) = grep {$_ eq $name } $vm->list_storage_pools(); + return($name, $dir) if $old; + + my $req = Ravada::Request->create_storage_pool( + uid => user_admin->id + ,id_vm => $vm->id + ,name => $name + ,directory => $dir + ); + wait_request(); + + return ($name, $dir); +} + +sub test_fail_nonvol($domain, $sp) { + my $req = Ravada::Request->move_volume( + uid => user_admin->id + ,id_domain => $domain->id + ,volume => 'missing' + ,storage => $sp + ); + wait_request( check_error => 0); + like($req->error, qr/Volume .*not found in/); +} + +sub test_do_not_overwrite($vm) { + my $domain = create_domain_v2(vm => $vm, data => 1, swap => 1 ); + my ($sp, $dir) = _create_storage_pool($vm); + + my ($vol) = ( $domain->list_volumes ); + + my ($filename)= $vol =~ m{.*/(.*)}; + die "Unknown filename from volume $vol" if !$filename; + + open my $out,">","$dir/$filename" or die "$! $dir/$filename"; + print $out "\n"; + close $out; + + my $req = Ravada::Request->move_volume( + uid => user_admin->id + ,id_domain => $domain->id + ,volume => $vol + ,storage => $sp + ); + wait_request( debug => 0, check_error => 0); + is($req->status,'done'); + like($req->error, qr/already exist/); + + unlink "$dir/$filename" or die "$! $dir/$filename"; + rmdir($dir) or die "$! $dir"; +} + +sub _search_free_space($dir) { + diag($dir); + open my $mounts,"<","/proc/mounts" or die $!; + my $found; + while (my $line = <$mounts>) { + my ($type,$partition) = split /\s+/, $line; + if ($partition eq $dir) { + $found = $partition; + last; + } + } + close $mounts; + if ( $found ) { + my @cmd = ("stat","-f","-c",'%a',$found); + my ($in, $out, $err); + run3(\@cmd,\$in,\$out,\$err); + die $err if $err; + chomp $out; + my $blocks = $out; + @cmd=("stat","-f","-c",'%S',$found); + run3(\@cmd,\$in,\$out,\$err); + die $err if $err; + chomp $out; + my $size = $out; + return $size * $blocks / (1024*1024*1024); + + } + + my ($dir2) = $dir =~ m{(/.*)/.*}; + return if !$dir2; + + return _find_mount($dir2); +} + +sub test_fail($vm) { + return if $< || $vm->type eq 'Void'; + + my ($dir,$size, $dev) = create_ram_fs(); + + $dir .= "/".new_pool_name(); + + my ($sp) = _create_storage_pool($vm, $dir); + + my $domain = create_domain_v2(vm => $vm, data => 1, swap => 1 ); + + my $vol = $domain->add_volume( name => new_domain_name() + ,size => $size + ,allocation => $size*1024 + ,format => 'raw' + ); + + my $req = Ravada::Request->move_volume( + uid => user_admin->id + ,id_domain => $domain->id + ,volume => $vol + ,storage => $sp + ); + + wait_request( check_error => 0, debug => 0); + like($req->error,qr/./,"Expecting $vol failed to copy to $sp") + or exit; + + $domain->remove(user_admin); + `umount $dir`; + rmdir $dir or die "$! $dir"; + unlink $dev or die "$! $dev"; + +} + +sub test_move_volume($vm) { + my $domain = create_domain_v2(vm => $vm, data => 1, swap => 1 ); + $domain->add_volume( name => new_domain_name().".raw" + ,type => "raw" + ); + my ($sp, $dir) = _create_storage_pool($vm); + + test_fail_nonvol($domain, $sp); + + my %done; + my %md5; + for my $vol ( $domain->list_volumes ) { + my $md5sum = `md5sum $vol`; + $md5sum =~ s/(.*?) .*/$1/; + my ($filename)= $vol =~ m{.*/(.*)}; + + if ( -e "$dir/$filename" ) { + diag("removing previously copied $dir/$filename"); + unlink("$dir/$filename") or die "$! $dir/$filename"; + $vm->refresh_storage(); + } + + $md5{$filename} = $md5sum; + my $req = Ravada::Request->move_volume( + uid => user_admin->id + ,id_domain => $domain->id + ,volume => $vol + ,storage => $sp + ); + ok(!$done{$req->id}++); + wait_request( debug => 0); + is($req->status,'done'); + is($req->error, ''); + if ($vol =~ /iso$/) { + ok( -e $vol) or die "Expecting $vol not removed"; + } else { + ok(! -e $vol) or die "Expecting $vol removed"; + } + ok(-e "$dir/$filename", "Expecting $dir/$filename") or exit; + } + for my $vol ($domain->list_volumes_info ) { + + is($vol->info->{storage_pool},$sp, $vol->file) or exit; + like($vol->file, qr/^$dir/); + my $file = $vol->file; + my $md5sum = `md5sum $file`; + $md5sum =~ s/(.*?) .*/$1/; + my ($filename)= $file =~ m{.*/(.*)}; + is($md5sum,$md5{$filename}, $file) or exit; + unlink $file or die "$! $file" + if $file =~ /\.iso$/ && -e $file; + } + my @volumes = $domain->list_volumes(); + $domain->remove(user_admin); + for my $vol (@volumes) { + ok(!-e $vol); + } + + rmdir($dir) or die "$! $dir"; +} + +######################################################################## + +init(); +clean(); + +for my $vm_name ( vm_names() ) { + + SKIP: { + my $vm = rvd_back->search_vm($vm_name); + + my $msg = "SKIPPED test: No $vm_name VM found "; + if ($vm && $vm_name eq 'KVM' && $>) { + $msg = "SKIPPED: Test must run as root"; + $vm = undef; + } + + diag($msg) if !$vm; + skip $msg,10 if !$vm; + + diag("test $vm_name"); + test_fail($vm); + test_move_volume($vm); + test_do_not_overwrite($vm); + } +} + +end(); + +done_testing(); + diff --git a/templates/bootstrap/footer.html.ep b/templates/bootstrap/footer.html.ep index e380739cb..38c55ccb3 100644 --- a/templates/bootstrap/footer.html.ep +++ b/templates/bootstrap/footer.html.ep @@ -28,7 +28,7 @@ % }
Copyright © 2016 - 2022 Ravada
+Copyright © 2016 - 2023 Ravada