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

Feature do not remove CD (#1158)

feature(backend): add option with_cd on create base

* test(volumes): check for CD
* refactor(tests): do not wait for some reqs
* test(backend): check ISO files in bases
* wip(backend): set driver for CD-ROM

issue #1116
parent b6a5fb0b
......@@ -2812,12 +2812,14 @@ sub _cmd_prepare_base {
my $user = Ravada::Auth::SQL->search_by_id( $uid)
or confess "Error: Unknown user id $uid in request ".Dumper($request);
my $with_cd = $request->defined_arg('with_cd');
my $domain = $self->search_domain_by_id($id_domain);
die "Unknown domain id '$id_domain'\n" if !$domain;
$self->_remove_unnecessary_downs($domain);
$domain->prepare_base($user);
$domain->prepare_base(user => $user, with_cd => $with_cd);
}
......
......@@ -548,6 +548,8 @@ sub _around_add_volume {
}
sub _check_volume_added($self, $file) {
return if $file =~ /\.iso$/i;
my $sth = $$CONNECTOR->dbh->prepare("SELECT id,id_domain FROM volumes "
." WHERE file=?"
);
......@@ -592,10 +594,21 @@ sub _around_list_volumes_info($orig, $self, $attribute=undef, $value=undef) {
return @volumes;
}
sub _around_prepare_base($orig, $self, $user, $request = undef) {
sub _around_prepare_base($orig, $self, @args) {
#sub _around_prepare_base($orig, $self, $user, $request = undef) {
my ($user, $request, $with_cd);
if(ref($args[0]) =~/^Ravada::/) {
($user, $request) = @args;
} else {
my %args = @args;
$user = delete $args{user};
$request = delete $args{request};
$with_cd = delete $args{with_cd};
confess "Error: uknown args". Dumper(\%args) if keys %args;
}
$self->_pre_prepare_base($user, $request);
my @base_img = $self->$orig();
my @base_img = $self->$orig($with_cd);
die "Error: No information files returned from prepare_base"
if !scalar (\@base_img);
......@@ -605,16 +618,17 @@ sub _around_prepare_base($orig, $self, $user, $request = undef) {
$self->_post_prepare_base($user, $request);
}
sub prepare_base($self) {
sub prepare_base($self, $with_cd) {
my @base_img;
for my $volume ($self->list_volumes_info(device => 'disk')) {
confess "Undefined info->target ".Dumper($volume)
if !$volume->info->{target};
for my $volume ($self->list_volumes_info()) {
my $base_file = $volume->base_filename;
next if !$base_file || $base_file =~ /\.iso$/;
die "Error: file '$base_file' already exists" if $self->_vm->file_exists($base_file);
}
for my $volume ($self->list_volumes_info(device => 'disk')) {
for my $volume ($self->list_volumes_info()) {
next if !$volume->info->{target} && $volume->info->{device} eq 'cdrom';
next if $volume->info->{device} eq 'cdrom' && !$with_cd;
confess "Undefined info->target ".Dumper($volume)
if !$volume->info->{target};
......@@ -1331,6 +1345,12 @@ sub info($self, $user) {
$info->{bases} = $self->_bases_vm();
$info->{clones} = $self->_clones_vm();
$info->{ports} = [$self->list_ports()];
my @cdrom = ();
for my $disk (@{$info->{hardware}->{disk}}) {
push @cdrom,($disk->{file}) if $disk->{file} && $disk->{file} =~ /\.iso$/;
}
$info->{cdrom} = \@cdrom;
return $info;
}
......@@ -1553,6 +1573,7 @@ sub _remove_files_base {
my $self = shift;
for my $file ( $self->list_files_base ) {
next if $file =~ /\.iso$/;
unlink $file or die "$! $file" if -e $file;
}
}
......@@ -1856,7 +1877,9 @@ sub clone {
my $request = delete $args{request};
my $memory = delete $args{memory};
my $start = delete $args{start};
my $is_pool = delete $args{is_pool};
my $no_pool = delete $args{no_pool};
my $with_cd = delete $args{with_cd};
confess "ERROR: Unknown args ".join(",",sort keys %args)
if keys %args;
......@@ -1879,7 +1902,7 @@ sub clone {
if ( !$self->is_base() ) {
$request->status("working","Preparing base") if $request;
$self->prepare_base($user)
$self->prepare_base(user => $user, with_cd => $with_cd)
}
my @args_copy = ();
......@@ -3636,7 +3659,7 @@ sub _pre_clone($self,%args) {
confess "ERROR: Missing user owner of new domain" if !$user;
for (qw(is_pool start add_to_pool from_pool)) {
for (qw(is_pool start add_to_pool from_pool with_cd)) {
delete $args{$_};
}
confess "ERROR: Unknown arguments ".join(",",sort keys %args) if keys %args;
......
......@@ -217,6 +217,8 @@ sub _vol_remove {
my $file = shift;
my $warning = shift;
confess "Error: I won't remove an iso file " if $file =~ /\.iso$/i;
my $name;
($name) = $file =~ m{.*/(.*)} if $file =~ m{/};
......@@ -276,6 +278,7 @@ sub remove {
}
eval { $self->_remove_file_image() };
warn $@ if $@;
confess $@ if $@ && $@ !~ /libvirt error code: 42/;
# warn "WARNING: Problem removing file image for ".$self->name." : $@" if $@ && $0 !~ /\.t$/;
......@@ -290,12 +293,14 @@ sub remove {
sub _remove_file_image {
my $self = shift;
for my $file ( $self->list_files_base ) {
next if $file && $file =~ /\.iso$/i;
next if !$file || ! -e $file;
chmod 0770, $file or die "$! chmodding $file";
chown $<,$(,$file or die "$! chowning $file";
eval { $self->_vol_remove($file,1) };
warn $@ if $@;
if ( -e $file ) {
eval {
......@@ -423,6 +428,7 @@ sub disk_device {
sub _create_qcow_base {
confess "Deprecated";
my $self = shift;
my @base_img;
......@@ -894,7 +900,7 @@ sub add_volume {
my $self = shift;
my %args = @_;
my $bus = (delete $args{driver} or 'virtio');
my $bus = delete $args{driver};# or 'virtio');
my $boot = (delete $args{boot} or undef);
my $device = (delete $args{device} or 'disk');
my %valid_arg = map { $_ => 1 } ( qw( driver name size vm xml swap target file allocation));
......@@ -936,6 +942,13 @@ sub add_volume {
$driver_type = 'raw';
}
if ( !defined $bus ) {
if ($device eq 'cdrom') {
$bus = 'ide';
} else {
$bus = 'virtio'
}
}
my $xml_device = $self->_xml_new_device(
bus => $bus
,file => $path
......
......@@ -229,31 +229,6 @@ sub start($self, @args) {
$self->_set_display( $listen_ip );
}
=pod
sub prepare_base {
my $self = shift;
my @img;
for my $volume ($self->list_volumes_info(device => 'disk')) {;
next if $volume->{device} ne 'disk';
my $file_qcow = $volume->{file};
my $file_base = $file_qcow.".qcow";
if ( $file_qcow =~ /.SWAP.img$/ ) {
$file_base = $file_qcow;
$file_base =~ s/(\.SWAP.img$)/base-$1/;
}
open my $out,'>',$file_base or die "$! $file_base";
print $out "$file_qcow\n";
close $out;
push @img,([$file_base, $volume->{target}]);
}
return @img;
}
=cut
sub list_disks {
my @disks;
for my $disk ( list_volumes_info(@_)) {
......@@ -282,6 +257,7 @@ sub remove_disks {
my $file = $vol->{file};
my $device = $vol->info->{device};
next if $device eq 'cdrom';
next if $file =~ /\.iso$/;
$self->_vol_remove($file);
}
......
......@@ -34,7 +34,7 @@ our %FIELD = map { $_ => 1 } qw(error output);
our %FIELD_RO = map { $_ => 1 } qw(id name);
our $args_manage = { name => 1 , uid => 1 };
our $args_prepare = { id_domain => 1 , uid => 1 };
our $args_prepare = { id_domain => 1 , uid => 1, with_cd => 2 };
our $args_remove_base = { id_domain => 1 , uid => 1 };
our $args_manage_iptables = {uid => 1, id_domain => 1, remote_ip => 1};
......@@ -85,6 +85,7 @@ our %VALID_ARG = (
,add_to_pool => 2
,start => 2,
,remote_ip => 2
,with_cd => 2
}
,change_owner => {uid => 1, id_domain => 1}
,add_hardware => {uid => 1, id_domain => 1, name => 1, number => 2, data => 2 }
......
......@@ -948,6 +948,7 @@ sub _domain_create_from_base {
if $self->search_domain($args{name});
my $base = $args{base};
my $with_cd = delete $args{with_cd};
$base = $self->_search_domain_by_id($args{id_base}) if $args{id_base};
confess "Unknown base id: $args{id_base}" if !$base;
......@@ -960,7 +961,10 @@ sub _domain_create_from_base {
my @device_disk = $self->_create_disk($base, $args{name});
# _xml_modify_cdrom($xml);
_xml_remove_cdrom($xml);
if ( !defined $with_cd ) {
$with_cd = grep (/\.iso$/ ,@device_disk);
}
_xml_remove_cdrom($xml) if !$with_cd;
my ($node_name) = $xml->findnodes('/domain/name/text()');
$node_name->setData($args{name});
......
......@@ -64,7 +64,7 @@ has 'name' => (
);
# after prepare base the original file is cloned so it gets empty
has 'clone_original' => (
has 'clone_base_after_prepare' => (
isa => 'Int'
,is => 'ro'
,default => sub { 1 }
......
......@@ -18,8 +18,14 @@ sub _around_prepare_base($orig, $self) {
confess "Error: unknown VM " if !defined $self->vm;
confess if !$self->capacity;
$self->vm->_check_free_disk($self->capacity);
my $storage_pool = ($self->vm->base_storage_pool or $self->vm->default_storage_pool_name);
$self->vm->_check_free_disk($self->capacity, $storage_pool);
my $base_file = $orig->($self);
confess if !$base_file;
return $base_file if ! $self->clone_base_after_prepare;
$self->vm->remove_file($self->file);
my $base = Ravada::Volume->new(
......@@ -27,7 +33,7 @@ sub _around_prepare_base($orig, $self) {
,is_base => 1
,vm => $self->vm
);
$base->clone(file => $self->file) if $self->clone_original;
$base->clone(file => $self->file);
return $base_file;
}
......@@ -37,6 +43,7 @@ sub _around_clone($orig, $self, %args) {
my $file_clone = ( delete $args{file} or $self->clone_filename($name));
confess "Error: unkonwn args ".Dumper(\%args) if keys %args;
confess "Error: empty clone filename" if !defined $file_clone || !length($file_clone);
return Ravada::Volume->new(
file => $orig->($self, $file_clone)
......
......@@ -3,16 +3,42 @@ package Ravada::Volume::ISO;
use Moose;
extends 'Ravada::Volume';
with 'Ravada::Volume::Class';
no warnings "experimental::signatures";
use feature qw(signatures);
has 'clone_base_after_prepare' => (
isa => 'Int'
,is => 'rw'
,default => sub { 0 }
);
sub prepare_base($self) {
$self->clone_base_after_prepare(0);
return $self->file;
}
sub capacity($self) {
return undef;
return 1;
}
sub backing_file($self) {
return $self->file;
}
sub clone($self, $filename) {
confess "Error: ISO file clone is himself because it is read only"
if $filename ne $self->file;
return $self->file;
}
sub clone_filename($self, $name=undef) {
return $self->file;
}
sub base_filename($self) {
return $self->file;
}
1;
......@@ -208,6 +208,14 @@ ravadaApp.directive("solShowMachine", swMach)
$scope.hide_clones = !$scope.hide_clones;
$scope.auto_hide_clones = false;
}
$scope.request = function(request, args) {
$http.post('/request/'+request+'/'
,JSON.stringify(args)
).then(function(response) {
console.log(response);
});
};
$scope.action = function(target,action,machineId){
$http.get('/'+target+'/'+action+'/'+machineId+'.json')
.then(function() {
......@@ -238,6 +246,11 @@ ravadaApp.directive("solShowMachine", swMach)
$scope.open_modal=function(prefix,machine){
$scope.modalOpened=true;
$('#'+prefix+machine.id).modal({show:true})
$scope.with_cd = false;
$http.get("/machine/info/"+machine.id+".json").then(function(response) {
machine.info=response.data;
}
);
}
$scope.cancel_modal=function(){
$scope.modalOpened=false;
......
......@@ -524,10 +524,12 @@
$scope.change_disk = function(id_machine, index ) {
var new_settings={
driver: $scope.showmachine.hardware.disk[index].driver,
capacity: $scope.showmachine.hardware.disk[index].capacity,
boot: $scope.showmachine.hardware.disk[index].boot,
file: $scope.showmachine.hardware.disk[index].file,
};
if ($scope.showmachine.hardware.disk[index].device === 'disk') {
new_settings.capacity = $scope.showmachine.hardware.disk[index].capacity;
}
console.log(new_settings);
$http.post('/machine/hardware/change'
,JSON.stringify({
......
......@@ -944,6 +944,8 @@ post '/request/(:name)/' => sub {
my $c = shift;
my $args = decode_json($c->req->body);
confess "Error: uid should not be provided".Dumper($args)
if exists $args->{uid};
warn Dumper($args);
my $req = Ravada::Request->new_request(
......
......@@ -38,6 +38,7 @@ sub _test_base_iso($volume, $base) {
}
sub _test_clone_iso($base, $clone) {
is($base->file, $clone);
}
sub _test_base_void($volume, $base) {
......@@ -158,9 +159,16 @@ sub test_clone($vm, $base) {
like($clone->file,qr($ext$));
like($clone->file,qr(\.SWAP\.$ext$)) if $base =~ /\.SWAP\./;
like($clone->file,qr(^.*/$name)) or exit;
#ISOs should be identical and we test no more
if ($ext eq 'iso') {
is($clone->file, $base);
is($clone->name, $vol_base->name);
return;
}
like($clone->file,qr(^.*/$name));
isnt($clone->name, $vol_base->name);
$test->($vol_base, $clone->file);
copy($clone->file,$clone->file.".tmp");
......@@ -194,7 +202,7 @@ sub _do_test_file($type, $vm, $file) {
is($volume->info->{name}, $file);
my $base_file = test_base($volume);
test_clone($vm, $base_file) if $type ne 'ISO';
test_clone($vm, $base_file);
unlink $file if $type ne 'ISO';
......
......@@ -29,7 +29,7 @@ sub test_new_domain {
,vm => $vm->type
,id_owner => $USER->id
,memory => 1.5*1024*1024
,disk => 1 * 1024 * 1024
,disk => 1 * 1024*1024
)
};
if ($freemem < 1 ) {
......@@ -65,7 +65,7 @@ sub test_new_domain_req {
,vm => $vm->type
,id_owner => $USER->id
,memory => (_check_free_memory() * 2) * 1024 * 1024
,disk => 1 * 1024 * 1024
,disk => 1 * 1024*1024
)
};
is(''.$@,'') or return;
......
......@@ -399,6 +399,7 @@ sub _remove_old_domains_vm($vm_name) {
$domain = $vm->search_domain($domain->name);
eval {$domain->remove( $USER_ADMIN ) } if $domain;
warn $@ if $@;
if ( $@ && $@ =~ /No DB info/i ) {
eval { $domain->domain->undefine() if $domain->domain };
}
......@@ -687,7 +688,6 @@ sub wait_request {
if ( $done_all ) {
for my $req (@$request) {
next if $skip{$req->command};
confess if $req->command eq 'enforce_limits';
if ($req->status ne 'done') {
$done_all = 0;
diag("Waiting for request ".$req->id." ".$req->command);
......
......@@ -27,10 +27,13 @@ sub test_fail_different_storage_pools($node) {
eval {
$base->migrate($node);
$base->migrate($vm);
};
is($@, '');
is(''.$@, '',"migrating to ".$node->name) or BAIL_OUT();
eval {
$base->migrate($vm);
};
is(''.$@, '',"migrating to ".$vm->name);
my $sp_default = $vm->default_storage_pool_name();
$vm->default_storage_pool_name($sp_name);
......
......@@ -87,7 +87,7 @@ sub test_add_hardware_request($vm, $domain, $hardware, $data={}) {
is($@,'') or return;
$USER->unread_messages();
ok($req, 'Request');
rvd_back->_process_all_requests_dont_fork(1);
rvd_back->_process_all_requests_dont_fork();
is($req->status(),'done');
is($req->error(),'') or exit;
......@@ -489,11 +489,11 @@ sub test_change_hardware($vm, $domain, $hardware) {
sub test_change_drivers($domain, $hardware) {
my $info = $domain->info(user_admin);
my ($index) = _search_disk($domain);
my $options = $info->{drivers}->{$hardware};
ok(scalar @$options,"No driver options for $hardware") or exit;
for my $option (@$options) {
my ($index) = _search_disk($domain);
diag("Testing $hardware type $option in $hardware $index");
$option = lc($option);
my $req = Ravada::Request->change_hardware(
......
......@@ -222,6 +222,176 @@ sub test_prepare_base {
}
sub test_prepare_base_with_cd {
my $vm = shift;
my $domain = create_domain($vm);
my @volumes = $domain->list_volumes_info;
my ($cd) = grep { $_->file =~ /\.iso$/ } @volumes;
die "Expecting a CDROM\n".Dumper(@volumes) if !$cd;
eval {
$domain->prepare_base(user => user_admin, with_cd => 1);
};
is($@,'') or exit;
my @volumes_base = $domain->list_files_base_target;
my ($cd_base) = grep { $_->[0] =~ /\.iso$/ } @volumes_base;
ok($cd_base,"Expecting a CD base ".Dumper(\@volumes_base)) or exit;
my $clone = rvd_back->create_domain(
name => new_domain_name
, id_base => $domain->id
,id_owner => user_admin->id
);
my @volumes_clone = $clone->list_volumes_info;
for my $vol (@volumes_clone) {
like(ref $vol->domain, qr/^Ravada::Domain/);
like(ref $vol->vm, qr/^Ravada::VM/);
}
my ($cd_clone ) = grep { defined $_->file && $_->file =~ /\.iso$/ } @volumes_clone;
ok($cd_clone,"Expecting a CD in clone ".Dumper([ map { delete $_->{domain}; delete $_->{vm}; $_ } @volumes_clone])) or exit;
}
sub test_prepare_base_with_cd_req {
my $vm = shift;
my $domain = create_domain($vm);
my @volumes = $domain->list_volumes_info;
my ($cd) = grep { $_->file =~ /\.iso$/ } @volumes;
die "Expecting a CDROM\n".Dumper(@volumes) if !$cd;
my $domain_f = Ravada::Front::Domain->open($domain->id);
ok($domain_f->info(user_admin)->{cdrom}) or die Dumper($domain_f->info(user_admin)->{hardware}->{disk});
like($domain_f->info(user_admin)->{cdrom}->[0],qr/\.iso$/) or die Dumper($domain_f->info(user_admin)->{hardware}->{disk});
my $req = Ravada::Request->prepare_base(
id_domain => $domain->id
,uid => user_admin->id
,with_cd => 1
);
wait_request( debug => 1 );
is($req->status, 'done');
is($req->error, '');
is($domain->is_base, 1);
my @volumes_base = $domain->list_files_base_target;
my ($cd_base) = grep { $_->[0] =~ /\.iso$/ } @volumes_base;
ok($cd_base,"Expecting a CD base ".Dumper(\@volumes_base));
my $clone = rvd_back->create_domain(
name => new_domain_name
, id_base => $domain->id
,id_owner => user_admin->id
);
my @volumes_clone = $clone->list_volumes_info;
my ($cd_clone ) = grep {defined $_->file && $_->file =~ /\.iso$/ } @volumes_clone;
ok($cd_clone,"Expecting a CD in clone ".Dumper([ map { delete $_->{domain}; delete $_->{vm} } @volumes_clone])) or exit;
$clone->remove(user_admin);
for my $vol ( @volumes_clone ) {
if ($vol->file =~ /\.iso$/) {
ok(-e $vol->file, $vol->file);
} else {
ok(!-e $vol->file, $vol->file);
}
}
$domain->remove_base(user_admin);
for my $volume ( @volumes_base ) {
my $file = $volume->[0];
die $file if $file !~ m{^[0-9a-z_/\-\.]+$}i;
if ($file =~ /\.iso$/) {
ok(-e $file, $file);
} else {
ok(!-e $file, $file);
}
}
$domain->prepare_base(user => user_admin, with_cd => 1);
my @volumes_base2 = $domain->list_files_base;
ok(grep(/\.iso$/,@volumes_base2));
for my $volume ( @volumes_base ) {
my $file = $volume->[0];