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

Refactor: add hardware on create machine (#1698)

correct downloading of extra ISO

* test: extra iso
* refactor: chain start request to pending requests
* refactor: improved remove machine
parent b78318e9
......@@ -2,5 +2,14 @@
**Implemented enhancements:**
- Define min swap space for machine templates [\#1697]
- Filter virtual machine by name [\#1696]
- Download extra ISO file for installation [\#1695]
**Refactors**
- enforce ordered adding of hardware [\#1697]
**Bugfixes**
- hibernate button was always disabled
......@@ -304,7 +304,7 @@ sub list_dists {
die "Error: no dists control files found in 'debian' dir"
if !@dists;
return @dists;
return reverse @dists;
}
sub set_control_file {
......
......@@ -709,7 +709,6 @@ sub _update_isos {
,min_swap_size => '2'
,arch => 'x86_64'
,extra_iso => 'https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/archive-virtio/virtio-win-0.1.215-\d+/virtio-win-0.1.\d+.iso'
}
,windows_xp => {
name => 'Windows XP'
......@@ -818,7 +817,9 @@ sub _scheduled_fedora_releases($self,$data) {
my $url_file = $url.$release
.'/Workstation/x86_64/iso/Fedora-Workstation-.*-x86_64-'.$release
.'-.*\.iso';
my @found = $vm->_search_url_file($url_file);
my @found;
eval { @found = $vm->_search_url_file($url_file) };
die $@ if $@ && $@ !~ /Not Found/i;
if(!@found) {
next if $url =~ m{//archives};
......@@ -2764,49 +2765,60 @@ sub create_domain {
if ($error =~ /has \d+ requests/) {
$request->status('retry');
}
$request->id_domain($domain->id) if $domain;
} elsif ($error) {
die $error;
}
return if !$domain;
my $req_add_swap;
if ($swap) {
$req_add_swap = Ravada::Request->add_hardware(
uid => $args{id_owner}
,id_domain => $domain->id
,name => 'disk'
,data => { size => $swap, type => 'swap' }
);
}
my $req_add_data;
if ($data) {
$req_add_data = Ravada::Request->add_hardware(
uid => $args{id_owner}
,id_domain => $domain->id
,name => 'disk'
,data => { size => $data, type => 'data' }
);
$req_add_data->after_request($req_add_swap->id)
if $req_add_swap;
my $req_add_swap = _req_add_disk($args{id_owner}, $domain->id,
,'swap', $swap ,$request);
my $req_add_data = _req_add_disk($args{id_owner}, $domain->id
,'data', $data, ($req_add_swap or $request ));
my $previous_req = ($req_add_data or $req_add_swap or $request);
my $req_add_iso = _add_extra_iso($domain, $request,$previous_req);
if ( $start ) {
$previous_req = ($req_add_iso or $req_add_data or $req_add_swap
or $request);
_start_domain_after_create($domain, $request, $id_owner, $previous_req)
}
my $previous_req = ( $req_add_data or $req_add_swap );
$previous_req = _add_extra_iso($domain, $request,$previous_req);
_start_domain_after_create($domain, $request, $previous_req) if $start;
return $domain;
}
sub _start_domain_after_create($domain, $request, $previous_request) {
sub _req_add_disk($uid, $id_domain, $type, $size, $request) {
return if !$size;
my @after_req;
@after_req = (after_request => $request->id ) if $request;
return Ravada::Request->add_hardware(
uid => $uid
,id_domain => $id_domain
,name => 'disk'
,data => { size => $size, type => $type }
,@after_req
);
}
sub _start_domain_after_create($domain, $request, $uid,$previous_request) {
my $remote_ip;
$remote_ip = $request->defined_arg('remote_ip') if $request;
my $uid = $request->defined_arg('id_owner');
$uid = $request->defined_arg('uid') if !$uid;
my @after_req;
@after_req = (after_request => $previous_request->id );
my $req_refresh = Ravada::Request->refresh_machine(
uid => $uid
,id_domain => $domain->id
,@after_req
);
@after_req = (after_request => $req_refresh->id )
if $req_refresh;
my $req = Ravada::Request->start_domain(
uid => $uid
,id_domain => $domain->id
,remote_ip => $remote_ip
,at => time + 3
,@after_req
);
$req->after_request($previous_request->id) if $previous_request;
}
......@@ -2829,18 +2841,31 @@ sub _add_extra_iso($domain, $request, $previous_request) {
my $extra_iso = $iso->{extra_iso};
return if !$extra_iso;
my ($url, $file_re) = $extra_iso =~ m{(.*)/(.*)};
$previous_request = $request if !$previous_request;
my ($url, $file_re) = $extra_iso =~ m{(.*/)(.*)};
my $volume = $domain->_vm->search_volume_path_re(qr($file_re));
my $download = 0;
if (!$volume) {
my ($url) = $domain->_vm->_search_url_file($extra_iso);
my ($device) = $url =~ m{.*/(.*)};
my ($url_match) = $domain->_vm->_search_url_file($extra_iso);
my ($device) = $url_match =~ m{.*/(.*)};
die "Error: file not found in $extra_iso\n"
if !$device;
$volume = $domain->_vm->dir_img()."/$device";
$domain->_vm->_download_file_external($url, $volume) ;
$download = 1 if $device;
}
my $req = Ravada::Request->refresh_storage(id_vm => $domain->_vm->id);
$req->after_request($previous_request->id) if $previous_request;
my @after_request;
@after_request = ( after_request => $previous_request->id )
if $previous_request;
return Ravada::Request->add_hardware(
my $req = Ravada::Request->refresh_storage(id_vm => $domain->_vm->id
,@after_request);
@after_request = ( after_request => $req->id ) if $req;
my $req_add = Ravada::Request->add_hardware(
name => 'disk'
,uid => Ravada::Utils::user_daemon->id
,id_domain => $domain->id
......@@ -2848,9 +2873,14 @@ sub _add_extra_iso($domain, $request, $previous_request) {
file => $volume
,device => 'cdrom'
}
,after_request => $req->id
,@after_request
,at => time+5
);
$domain->_vm->_download_file_external($extra_iso, $volume)
if $download;
return $req_add;
}
sub _check_args($args,@) {
......
......@@ -83,7 +83,7 @@ our %VALID_ARG = (
,set_driver => {uid => 1, id_domain => 1, id_option => 1}
,hybernate=> {uid => 1, id_domain => 1}
,download => {uid => 2, id_iso => 1, id_vm => 2, vm => 2, verbose => 2, delay => 2, test => 2}
,refresh_storage => { id_vm => 2 }
,refresh_storage => { id_vm => 2, uid => 2 }
,list_storage_pools => { id_vm => 1 , uid => 1 }
,check_storage => { uid => 1 }
,set_base_vm=> {uid => 1, id_vm=> 1, id_domain => 1, value => 2 }
......@@ -760,9 +760,13 @@ sub _validate_start_domain($self) {
$id_domain = _search_domain_id(undef,$domain_name);
}
return if !$id_domain;
my $req = $self->_search_request('%_hardware', id_domain => $id_domain);
$self->after_request($req->id) if $req;
for my $command ('start','%_hardware') {
my $req=$self->_search_request($command, id_domain => $id_domain);
next if !$req;
next if $req->at_time;
next if $command eq 'start' && !$req->after_request();
$self->after_request($req->id);
}
}
sub _validate_change_hardware($self) {
......
......@@ -1289,7 +1289,7 @@ sub _download_file_external($self, $url, $device, $verbose=1, $test=0) {
$url .= "/" if $url !~ m{/$} && $url !~ m{.*/([^/]+\.[^/]+)$};
if ($url =~ m{[^*]}) {
my @found = $self->_search_url_file($url);
confess "No match for $url" if !scalar @found;
die "Error: URL not found '$url'" if !scalar @found;
$url = $found[-1];
}
if ( $url =~ m{/$} ) {
......@@ -1361,7 +1361,7 @@ sub _download($self, $url) {
$url =~ s{(http://.*)//(.*)}{$1/$2};
if ($url =~ m{[^*]}) {
my @found = $self->_search_url_file($url);
confess "No match for $url" if !scalar @found;
die "Error: URL not found '$url'" if !scalar @found;
$url = $found[-1];
}
......@@ -1389,13 +1389,9 @@ sub _match_url($self,$url) {
$url2 = '' if !$url2;
confess "No url1 from $url" if !defined $url1;
my $ua = Mojo::UserAgent->new;
my $res = $ua->get(($url1 or '/'))->res;
confess "ERROR ".$res->code." ".$res->message." : $url1"
unless $res->code == 200 || $res->code == 301;
my $dom = $self->_ua_get($url1);
my @found;
my $links = $res->dom->find('a')->map( attr => 'href');
my $links = $dom->find('a')->map( attr => 'href');
for my $link (@$links) {
next if !defined $link;
$link =~ s{/$}{};
......@@ -1406,19 +1402,40 @@ sub _match_url($self,$url) {
return @found;
}
sub _ua_get($self, $url) {
my $cache = $self->_cache_get($url);
if ( $cache ) {
my $dom = Mojo::DOM->new($cache);
return $dom;
}
my ($ip) = $url =~ m{://(.*?)[:/]};
sleep 1 if !$ip || $self->{_url_get}->{$ip};
my $ua = $self->_web_user_agent();
my $res;
for my $try ( 1 .. 3 ) {
$res = $ua->get($url)->res;
last if $res && defined $res->code;
sleep 1+$try;
}
confess "Error getting '$url'" if !$res;
confess "ERROR ".$res->code." ".$res->message." : $url"
unless $res->code == 200 || $res->code == 301;
$self->_cache_store($url,$res->body);
return $res->dom;
}
sub _cache_get($self, $url) {
my $file = _cache_filename($url);
my @stat = stat($file) or return;
return if time-$stat[9] > 300;
return if time-$stat[9] > 600;
open my $in ,'<' , $file or return;
return join("",<$in>);
}
sub _cache_store {
my $self = shift;
my $url = shift;
my $content = shift;
sub _cache_store($self, $url, $content) {
my $file = _cache_filename($url);
open my $out ,'>' , $file or die "$! $file";
......@@ -1527,17 +1544,7 @@ sub _match_file($self, $url, $file_re) {
$url .= '/' if $url !~ m{/$};
my $res;
for ( 1 .. 10 ) {
eval { $res = $self->_web_user_agent->get($url)->res(); };
last if !$@ && $res && defined $res->code;
next if $@ && $@ =~ /timeout/i;
confess $@ if $@;
}
return unless defined $res->code && ( $res->code == 200 || $res->code == 301);
my $dom= $res->dom;
my $dom = $self->_ua_get($url);
my @found;
......
......@@ -90,7 +90,7 @@ sub _type_from_extension($file) {
);
return $type{$ext} if exists $type{$ext};
return uc($ext);
return 'RAW';
}
sub _type($file,$vm = undef) {
......
......@@ -171,7 +171,6 @@ ravadaApp.directive("solShowMachine", swMach)
}
if ( $scope.swap.value < iso.min_swap_size ) {
$scope.swap.value = iso.min_swap_size + 0.1;
console.log($scope.swap.value);
}
return (iso.device != null) ? iso.device : "";
};
......
......@@ -265,7 +265,7 @@
if (data === null || typeof(data) == undefined ) {
console.log("close");
ws.close();
$scope.domain_removed = true;
window.location.href="/";
return;
}
$scope.$apply(function () {
......
use warnings;
use strict;
use Carp qw(confess);
use Data::Dumper;
use IPC::Run3;
use JSON::XS;
use Test::More;
use lib 't/lib';
use Test::Ravada;
no warnings "experimental::signatures";
use feature qw(signatures);
###################################################################
sub test_extra_isos($vm) {
my $isos = rvd_front->list_iso_images();
for my $iso (@$isos) {
next if !$iso->{extra_iso};
my @url = $vm->_search_url_file($iso->{extra_iso});
ok(scalar(@url));
my $name = new_domain_name();
my $req = Ravada::Request->create_domain(
name => $name
,id_owner => user_admin->id
,vm => $vm->type
,id_iso => $iso->{id}
,iso_file => "/var/tmp/a.iso"
);
wait_request();
my $domain = rvd_back->search_domain($name);
ok($domain) or next;
my $disks = $domain->info(user_admin)->{hardware}->{disk};
my @cds = grep { $_->{file} =~ /\.iso$/ } @$disks;
is(scalar(@cds),2);
remove_domain($domain);
}
}
###################################################################
init();
clean();
for my $vm_name ( 'KVM' ) {
SKIP: {
my $vm = rvd_back->search_vm($vm_name);
my $msg = "SKIPPED test: No $vm_name VM found ";
if ($vm && $>) {
$msg = "SKIPPED: Test must run as root";
$vm = undef;
}
diag($msg) if !$vm;
skip $msg,10 if !$vm;
test_extra_isos($vm);
}
}
end();
done_testing();
......@@ -18,34 +18,23 @@
</h2>
% } else {
<h2><%=l 'Virtual Machine' %>
<a ng-hide="domain_remove_done"
<a ng-hide="domain_remove_done"
href="/machine/view/<%= $domain->id %>.html"><%= $domain->name %></a>
<%=l 'Settings' %>
</h2>
% }
</div>
<div class="card-body" ng-cloak="1">
<div class="row" ng-hide="domain_removed">
<div class="row">
<div class="col-2">
%= include 'main/settings_machine_tabs_head'
</div>
<div class="col-10" ng-hide="domain_removed">
<div class="col-10">
%= include 'main/settings_machine_tabs_body'
</div>
</div>
<div class="row" ng-show="domain_removed">
<div>
%= include 'main/settings_machine_tabs_head'
</div>
<div class="col-10" ng-show="domain_removed">
<h3><%=l 'Machine removed' %></h3>
<p>
<a href="/"><%=l 'Available Machines' %></a>
</p>
</div>
</div>
</div>
<div class="alert alert-danger" ng-show="error">{{error}}</div>
<div ng-cloak="1" class="alert alert-danger" ng-show="error">{{error}}</div>
</div><!-- del panel default-->
</div><!-- del page-header -->
</div> <!-- del page-wrapper -->
......
......@@ -36,7 +36,7 @@
</div> <!-- from list clones -->
</div> <!-- from card-header -->
<div ng-show="domain_remove || !showmachine.has_clones"
<div ng-show="(domain_remove || !showmachine.has_clones) && !domain_remove_done"
class="card-header"
>
<p><%=l 'Once you delete the machine, there is no going back. Please be certain.' %></p>
......@@ -56,7 +56,7 @@
<div ng-show="domain_remove_clones">
<b><%=l 'Danger'%></b>.
<%=l 'This will remove all the'%> {{showmachine.has_clones}} <%=l 'clones of the machine' %><i><%= $domain->name %></i>.
<%=l 'This will remove all the'%> {{showmachine.has_clones}} <%=l 'clones of the machine' %> <i><%= $domain->name %></i>.
<b><%=l 'This action can\'t be undone' %></b>.
<%=l 'Are you sure ?'%><br/>
<a type="button" class="btn btn-primary text-white"
......@@ -67,7 +67,7 @@
</div><!-- del domain remove clones-->
<div ng-show="domain_remove_done">
<%=l 'Virtual Machine' %><b>{{showmachine.name}}</b> <%=l 'removed' %>.
<%=l 'Virtual Machine' %> <b>{{showmachine.name}}</b> <%=l 'removed' %>.
</div>
</div>
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment