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

Fix #1131 pool (#1133)

fix(backend): fixed create clone when pooling

* fix(backend): reviewed pool clones management
* refactor(frontend): improved user feedback
* test(backend): pick a machine from pool

issue #1131
parent 7ead6d59
...@@ -1551,7 +1551,7 @@ sub create_domain { ...@@ -1551,7 +1551,7 @@ sub create_domain {
my $start = $args{start}; my $start = $args{start};
my $id_base = $args{id_base}; my $id_base = $args{id_base};
my $id_owner = $args{id_owner} or confess "Error: missing id_owner ".Dumper(\%args); my $id_owner = $args{id_owner} or confess "Error: missing id_owner ".Dumper(\%args);
_check_args(\%args,qw(iso_file id_base id_iso id_owner name active swap memory disk id_template start remote_ip request vm)); _check_args(\%args,qw(iso_file id_base id_iso id_owner name active swap memory disk id_template start remote_ip request vm add_to_pool));
confess "ERROR: Argument vm required" if !$id_base && !$vm_name; confess "ERROR: Argument vm required" if !$id_base && !$vm_name;
...@@ -2418,7 +2418,7 @@ sub _cmd_manage_pools($self, $request) { ...@@ -2418,7 +2418,7 @@ sub _cmd_manage_pools($self, $request) {
} }
sub _pool_create_clones($self, $domain, $number, $request) { sub _pool_create_clones($self, $domain, $number, $request) {
my @arg_clone = ( no_pool => 1 ); my @arg_clone = ( );
$request->status("cloning $number"); $request->status("cloning $number");
if (!$domain->is_base) { if (!$domain->is_base) {
my @requests = $domain->list_requests(); my @requests = $domain->list_requests();
...@@ -2434,7 +2434,7 @@ sub _pool_create_clones($self, $domain, $number, $request) { ...@@ -2434,7 +2434,7 @@ sub _pool_create_clones($self, $domain, $number, $request) {
uid => $request->args('uid') uid => $request->args('uid')
,id_domain => $domain->id ,id_domain => $domain->id
,number => $number ,number => $number
,is_pool => 1 ,add_to_pool => 1
,start => 1 ,start => 1
,@arg_clone ,@arg_clone
); );
...@@ -2489,12 +2489,16 @@ sub _cmd_create{ ...@@ -2489,12 +2489,16 @@ sub _cmd_create{
if ( $request->defined_arg('id_base') ) { if ( $request->defined_arg('id_base') ) {
my $base = Ravada::Domain->open($request->args('id_base')); my $base = Ravada::Domain->open($request->args('id_base'));
if ( $base->pools && !$request->defined_arg('no_pools') ) { if ( $request->defined_arg('pool') ) {
$request->{args}->{id_domain} = delete $request->{args}->{id_base}; if ( $base->pools ) {
$request->{args}->{uid} = delete $request->{args}->{id_owner}; $request->{args}->{id_domain} = delete $request->{args}->{id_base};
my $clone = $self->_cmd_clone($request); $request->{args}->{uid} = delete $request->{args}->{id_owner};
$request->id_domain($clone->id); my $clone = $self->_cmd_clone($request);
return $clone; $request->id_domain($clone->id);
return $clone;
} else {
confess "Error: this base has no pools";
}
} }
} }
...@@ -2646,10 +2650,8 @@ sub _cmd_open_iptables { ...@@ -2646,10 +2650,8 @@ sub _cmd_open_iptables {
} }
sub _cmd_clone($self, $request) { sub _cmd_clone($self, $request) {
my $number = $request->defined_arg('number');
my $no_pool = $request->defined_arg('no_pool');
return _req_clone_many($self, $request) if $number; return _req_clone_many($self, $request) if $request->defined_arg('number');
my $domain = Ravada::Domain->open($request->args('id_domain')) my $domain = Ravada::Domain->open($request->args('id_domain'))
or confess "Error: Domain ".$request->args('id_domain')." not found"; or confess "Error: Domain ".$request->args('id_domain')." not found";
...@@ -2665,18 +2667,6 @@ sub _cmd_clone($self, $request) { ...@@ -2665,18 +2667,6 @@ sub _cmd_clone($self, $request) {
} }
my $name = ( $request->defined_arg('name') or $domain->name."-".$user->name ); my $name = ( $request->defined_arg('name') or $domain->name."-".$user->name );
if ( $domain->pools && !$no_pool ) {
my $clone = $domain->_search_pool_clone($user);
my $remote_ip = $request->defined_arg('remote_ip');
my $start = $request->defined_arg('start');
if ($start || $clone->is_active) {
$clone->start(user => $user, remote_ip => $remote_ip);
$clone->_data('client_status', 'connecting ...');
$clone->_data('client_status_time_checked',time);
Ravada::Request->manage_pools( uid => Ravada::Utils::user_daemon->id);
}
return $clone;
}
my $clone = $domain->clone( my $clone = $domain->clone(
name => $name name => $name
......
...@@ -1843,18 +1843,31 @@ sub clone { ...@@ -1843,18 +1843,31 @@ sub clone {
confess "ERROR: Clones can't be created in readonly mode" confess "ERROR: Clones can't be created in readonly mode"
if $self->_vm->readonly(); if $self->_vm->readonly();
return $self->_copy_clone(@_) if $self->id_base(); my $add_to_pool = delete $args{add_to_pool};
my $from_pool = delete $args{from_pool};
my $remote_ip = delete $args{remote_ip}; my $remote_ip = delete $args{remote_ip};
my $request = delete $args{request}; my $request = delete $args{request};
my $memory = delete $args{memory}; my $memory = delete $args{memory};
my $start = delete $args{start}; my $start = delete $args{start};
my $is_pool = delete $args{is_pool};
my $no_pool = delete $args{no_pool};
confess "ERROR: Unknown args ".join(",",sort keys %args) confess "ERROR: Unknown args ".join(",",sort keys %args)
if keys %args; if keys %args;
confess "Error: This base has no pools"
if $add_to_pool && !$self->pools;
$from_pool = 1 if !defined $from_pool && !$add_to_pool && $self->pools;
confess "Error: you can't add to pool if you pick from pool"
if $from_pool && $add_to_pool;
return $self->_clone_from_pool(@_) if $from_pool;
my %args2 = @_;
delete $args2{from_pool};
return $self->_copy_clone(%args2) if $self->id_base();
my $uid = $user->id; my $uid = $user->id;
if ( !$self->is_base() ) { if ( !$self->is_base() ) {
...@@ -1867,6 +1880,8 @@ sub clone { ...@@ -1867,6 +1880,8 @@ sub clone {
push @args_copy, ( memory => $memory ) if $memory; push @args_copy, ( memory => $memory ) if $memory;
push @args_copy, ( request => $request ) if $request; push @args_copy, ( request => $request ) if $request;
push @args_copy, ( remote_ip => $remote_ip) if $remote_ip; push @args_copy, ( remote_ip => $remote_ip) if $remote_ip;
push @args_copy, ( from_pool => $from_pool) if defined $from_pool;
push @args_copy, ( add_to_pool => $add_to_pool) if defined $add_to_pool;
my $vm = $self->_vm; my $vm = $self->_vm;
if ($self->volatile_clones ) { if ($self->volatile_clones ) {
...@@ -1882,7 +1897,23 @@ sub clone { ...@@ -1882,7 +1897,23 @@ sub clone {
,id_owner => $uid ,id_owner => $uid
,@args_copy ,@args_copy
); );
$clone->is_pool(1) if $is_pool; $clone->is_pool(1) if $add_to_pool;
return $clone;
}
sub _clone_from_pool($self, %args) {
my $user = delete $args{user};
my $remote_ip = delete $args{remote_ip};
my $start = delete $args{start};
my $clone = $self->_search_pool_clone($user);
if ($start || $clone->is_active) {
$clone->start(user => $user, remote_ip => $remote_ip);
$clone->_data('client_status', 'connecting ...');
$clone->_data('client_status_time_checked',time);
Ravada::Request->manage_pools( uid => Ravada::Utils::user_daemon->id);
}
return $clone; return $clone;
} }
...@@ -1891,6 +1922,7 @@ sub _copy_clone($self, %args) { ...@@ -1891,6 +1922,7 @@ sub _copy_clone($self, %args) {
my $user = delete $args{user} or confess "ERROR: Missing user"; my $user = delete $args{user} or confess "ERROR: Missing user";
my $memory = delete $args{memory}; my $memory = delete $args{memory};
my $request = delete $args{request}; my $request = delete $args{request};
my $add_to_pool = delete $args{add_to_pool};
confess "ERROR: Unknown arguments ".join(",",sort keys %args) confess "ERROR: Unknown arguments ".join(",",sort keys %args)
if keys %args; if keys %args;
...@@ -1907,6 +1939,7 @@ sub _copy_clone($self, %args) { ...@@ -1907,6 +1939,7 @@ sub _copy_clone($self, %args) {
name => $name name => $name
,id_base => $base->id ,id_base => $base->id
,id_owner => $user->id ,id_owner => $user->id
,from_pool => 0
,@copy_arg ,@copy_arg
); );
my @volumes = $self->list_volumes_info(device => 'disk'); my @volumes = $self->list_volumes_info(device => 'disk');
...@@ -1918,6 +1951,7 @@ sub _copy_clone($self, %args) { ...@@ -1918,6 +1951,7 @@ sub _copy_clone($self, %args) {
copy($volumes{$target}, $copy_volumes{$target}) copy($volumes{$target}, $copy_volumes{$target})
or die "$! $volumes{$target}, $copy_volumes{$target}" or die "$! $volumes{$target}, $copy_volumes{$target}"
} }
$copy->is_pool(1) if $add_to_pool;
return $copy; return $copy;
} }
...@@ -3556,7 +3590,7 @@ sub _pre_clone($self,%args) { ...@@ -3556,7 +3590,7 @@ sub _pre_clone($self,%args) {
confess "ERROR: Missing user owner of new domain" if !$user; confess "ERROR: Missing user owner of new domain" if !$user;
for (qw(is_pool start no_pool)) { for (qw(is_pool start add_to_pool from_pool)) {
delete $args{$_}; delete $args{$_};
} }
confess "ERROR: Unknown arguments ".join(",",sort keys %args) if keys %args; confess "ERROR: Unknown arguments ".join(",",sort keys %args) if keys %args;
...@@ -3706,6 +3740,7 @@ sub _search_pool_clone($self, $user) { ...@@ -3706,6 +3740,7 @@ sub _search_pool_clone($self, $user) {
if !$self->pools; if !$self->pools;
my ($clone_down, $clone_free_up, $clone_free_down); my ($clone_down, $clone_free_up, $clone_free_down);
my ($clones_in_pool, $clones_used) = (0,0);
for my $current ( $self->clones) { for my $current ( $self->clones) {
if ( $current->{id_owner} == $user->id if ( $current->{id_owner} == $user->id
&& $current->{status} =~ /^(active|hibernated)$/) { && $current->{status} =~ /^(active|hibernated)$/) {
...@@ -3716,6 +3751,7 @@ sub _search_pool_clone($self, $user) { ...@@ -3716,6 +3751,7 @@ sub _search_pool_clone($self, $user) {
if ( $current->{id_owner} == $user->id ) { if ( $current->{id_owner} == $user->id ) {
$clone_down = $current; $clone_down = $current;
} elsif ($current->{is_pool}) { } elsif ($current->{is_pool}) {
$clones_in_pool++;
my $clone = Ravada::Domain->open($current->{id}); my $clone = Ravada::Domain->open($current->{id});
if(!$clone->client_status || $clone->client_status eq 'disconnected') { if(!$clone->client_status || $clone->client_status eq 'disconnected') {
if ( $clone->status =~ /^(active|hibernated)$/ ) { if ( $clone->status =~ /^(active|hibernated)$/ ) {
...@@ -3723,6 +3759,8 @@ sub _search_pool_clone($self, $user) { ...@@ -3723,6 +3759,8 @@ sub _search_pool_clone($self, $user) {
} else { } else {
$clone_free_down = $current; $clone_free_down = $current;
} }
} else {
$clones_used++;
} }
} }
} }
...@@ -3730,6 +3768,7 @@ sub _search_pool_clone($self, $user) { ...@@ -3730,6 +3768,7 @@ sub _search_pool_clone($self, $user) {
my $clone_data = ($clone_down or $clone_free_up or $clone_free_down); my $clone_data = ($clone_down or $clone_free_up or $clone_free_down);
die "Error: no free clones in pool for ".$self->name die "Error: no free clones in pool for ".$self->name
.". Usage: $clones_used used from $clones_in_pool virtual machines created.\n"
if !$clone_data; if !$clone_data;
my $clone = Ravada::Domain->open($clone_data->{id}); my $clone = Ravada::Domain->open($clone_data->{id});
......
...@@ -76,8 +76,14 @@ our %VALID_ARG = ( ...@@ -76,8 +76,14 @@ our %VALID_ARG = (
,refresh_storage => { id_vm => 2 } ,refresh_storage => { id_vm => 2 }
,set_base_vm=> {uid => 1, id_vm=> 1, id_domain => 1, value => 2 } ,set_base_vm=> {uid => 1, id_vm=> 1, id_domain => 1, value => 2 }
,cleanup => { } ,cleanup => { }
,clone => { uid => 1, id_domain => 1, name => 2, memory => 2, number => 2, is_pool => 2 ,clone => { uid => 1, id_domain => 1, name => 2, memory => 2, number => 2
,start => 2, no_pool => 2 # If base has pools, from_pool = 1 if undefined
# when from_pool is true the clone is picked from the pool
# when from_pool is false the clone is created
,from_pool => 2
# If base has pools, create anew and add to the pool
,add_to_pool => 2
,start => 2,
,remote_ip => 2 ,remote_ip => 2
} }
,change_owner => {uid => 1, id_domain => 1} ,change_owner => {uid => 1, id_domain => 1}
......
...@@ -362,6 +362,7 @@ sub _around_create_domain { ...@@ -362,6 +362,7 @@ sub _around_create_domain {
my $self = shift; my $self = shift;
my %args = @_; my %args = @_;
my $remote_ip = delete $args{remote_ip}; my $remote_ip = delete $args{remote_ip};
my $add_to_pool = delete $args{add_to_pool};
my %args_create = %args; my %args_create = %args;
my $id_owner = delete $args{id_owner} or confess "ERROR: Missing id_owner"; my $id_owner = delete $args{id_owner} or confess "ERROR: Missing id_owner";
...@@ -374,6 +375,7 @@ sub _around_create_domain { ...@@ -374,6 +375,7 @@ sub _around_create_domain {
my $active = delete $args{active}; my $active = delete $args{active};
my $name = delete $args{name}; my $name = delete $args{name};
my $swap = delete $args{swap}; my $swap = delete $args{swap};
my $from_pool = delete $args{from_pool};
# args get deleted but kept on %args_create so when we call $self->$orig below are passed # args get deleted but kept on %args_create so when we call $self->$orig below are passed
delete $args{disk}; delete $args{disk};
...@@ -390,6 +392,11 @@ sub _around_create_domain { ...@@ -390,6 +392,11 @@ sub _around_create_domain {
$base = $self->search_domain_by_id($id_base) $base = $self->search_domain_by_id($id_base)
or confess "Error: I can't find domain $id_base on ".$self->name; or confess "Error: I can't find domain $id_base on ".$self->name;
$volatile = 1 if $base->volatile_clones; $volatile = 1 if $base->volatile_clones;
if ($add_to_pool) {
confess "Error: you can't add to pool and also pick from pool" if $from_pool;
$from_pool = 0;
}
$from_pool = 1 if !defined $from_pool && $base->pools();
} }
confess "ERROR: User ".$owner->name." is not allowed to create machines" confess "ERROR: User ".$owner->name." is not allowed to create machines"
...@@ -400,10 +407,18 @@ sub _around_create_domain { ...@@ -400,10 +407,18 @@ sub _around_create_domain {
confess "ERROR: Base ".$base->name." is private" confess "ERROR: Base ".$base->name." is private"
if !$owner->is_admin && $base && !$base->is_public(); if !$owner->is_admin && $base && !$base->is_public();
if ($add_to_pool) {
confess "Error: This machine can only be added to a pool if it is a clone"
if !$base;
confess("Error: Requested to add a clone for the pool but this base has no pools")
if !$base->pools;
}
$args_create{listen_ip} = $self->listen_ip($remote_ip); $args_create{listen_ip} = $self->listen_ip($remote_ip);
$args_create{spice_password} = $self->_define_spice_password($remote_ip); $args_create{spice_password} = $self->_define_spice_password($remote_ip);
$self->_pre_create_domain(%args_create); $self->_pre_create_domain(%args_create);
return $base->_search_pool_clone($owner) if $from_pool;
my $domain = $self->$orig(%args_create, volatile => $volatile); my $domain = $self->$orig(%args_create, volatile => $volatile);
$domain->add_volume_swap( size => $swap ) if $swap; $domain->add_volume_swap( size => $swap ) if $swap;
...@@ -432,6 +447,7 @@ sub _around_create_domain { ...@@ -432,6 +447,7 @@ sub _around_create_domain {
$domain->info($owner); $domain->info($owner);
$domain->display($owner) if $domain->is_active; $domain->display($owner) if $domain->is_active;
$domain->is_pool(1) if $add_to_pool;
return $domain; return $domain;
} }
...@@ -665,7 +681,7 @@ sub _check_require_base { ...@@ -665,7 +681,7 @@ sub _check_require_base {
delete $args{start}; delete $args{start};
delete $args{remote_ip}; delete $args{remote_ip};
delete @args{'_vm','name','vm', 'memory','description','id_iso','listen_ip','spice_password'}; delete @args{'_vm','name','vm', 'memory','description','id_iso','listen_ip','spice_password','from_pool'};
confess "ERROR: Unknown arguments ".join(",",keys %args) confess "ERROR: Unknown arguments ".join(",",keys %args)
if keys %args; if keys %args;
......
...@@ -293,17 +293,31 @@ ...@@ -293,17 +293,31 @@
$scope.new_name_duplicated=false; $scope.new_name_duplicated=false;
} }
}; };
$scope.set_bool = function(field, value) { $scope.set_bool = function(field, value) {
if (value ) value=1; if (value ) value=1;
else value=0; else value=0;
console.log(field+" "+value);
$scope.showmachine[field]=value; $scope.showmachine[field]=value;
value_show = true;
if (! value) {
value_show = false;
}
$scope.add_message("Setting "+$scope.showmachine.name+" "+field+" to "+value_show);
$http.get("/machine/set/"+$scope.showmachine.id+"/"+field+"/"+value); $http.get("/machine/set/"+$scope.showmachine.id+"/"+field+"/"+value);
}; };
$scope.set = function(field) { $scope.set = function(field) {
console.log(field+" = "+$scope.showmachine[field]); $scope.add_message("Setting "+$scope.showmachine.name+" "+field+" to "
+$scope.showmachine[field]);
$http.get("/machine/set/"+$scope.showmachine.id+"/"+field+"/"+$scope.showmachine[field]); $http.get("/machine/set/"+$scope.showmachine.id+"/"+field+"/"+$scope.showmachine[field]);
}; };
$scope.add_message = function(text) {
$scope.message.push(text);
setTimeout(function () {
$scope.message = [];
}, 5000);
};
$scope.set_public = function(machineId, value) { $scope.set_public = function(machineId, value) {
if (value) value=1; if (value) value=1;
else value=0; else value=0;
...@@ -555,6 +569,7 @@ ...@@ -555,6 +569,7 @@
capacity: '1G', capacity: '1G',
allocation: '0.1G' allocation: '0.1G'
}; };
$scope.message = [];
$scope.disk_remove = []; $scope.disk_remove = [];
$scope.pending_before = 10; $scope.pending_before = 10;
// $scope.getSingleMachine(); // $scope.getSingleMachine();
......
...@@ -2100,7 +2100,7 @@ sub copy_machine { ...@@ -2100,7 +2100,7 @@ sub copy_machine {
my $base = $RAVADA->search_domain_by_id($id_base) or confess "I can't find domain $id_base"; my $base = $RAVADA->search_domain_by_id($id_base) or confess "I can't find domain $id_base";
my $name = ( $arg->{new_name} or $base->name."-".$USER->name ); my $name = ( $arg->{new_name} or $base->name."-".$USER->name );
my @create_args = ( no_pool => 1 ); my @create_args = ( from_pool => 0 );
push @create_args,( memory => $ram ) if $ram; push @create_args,( memory => $ram ) if $ram;
my @reqs; my @reqs;
if ($number == 1 ) { if ($number == 1 ) {
......
...@@ -29,6 +29,7 @@ sub test_new_domain { ...@@ -29,6 +29,7 @@ sub test_new_domain {
,vm => $vm->type ,vm => $vm->type
,id_owner => $USER->id ,id_owner => $USER->id
,memory => 1.5*1024*1024 ,memory => 1.5*1024*1024
,disk => 1 * 1024 * 1024
) )
}; };
if ($freemem < 1 ) { if ($freemem < 1 ) {
...@@ -64,6 +65,7 @@ sub test_new_domain_req { ...@@ -64,6 +65,7 @@ sub test_new_domain_req {
,vm => $vm->type ,vm => $vm->type
,id_owner => $USER->id ,id_owner => $USER->id
,memory => (_check_free_memory() * 2) * 1024 * 1024 ,memory => (_check_free_memory() * 2) * 1024 * 1024
,disk => 1 * 1024 * 1024
) )
}; };
is(''.$@,'') or return; is(''.$@,'') or return;
......
...@@ -637,7 +637,7 @@ sub wait_request { ...@@ -637,7 +637,7 @@ sub wait_request {
$done_all = 0; $done_all = 0;
} elsif (!$done{$req->id}) { } elsif (!$done{$req->id}) {
$done{$req->{id}}++; $done{$req->{id}}++;
is($req->error,'') if $check_error; is($req->error,'') or confess if $check_error;
} }
} }
my $post = join(".",_list_requests); my $post = join(".",_list_requests);
......
...@@ -42,14 +42,14 @@ sub test_clones($domain, $n_clones) { ...@@ -42,14 +42,14 @@ sub test_clones($domain, $n_clones) {
wait_request(); wait_request();
$domain->pool_clones($n_clones); $domain->pool_clones($n_clones);
is($domain->pool_clones, $n_clones); is($domain->pool_clones, $n_clones);
wait_request(); wait_request(debug => 1);
is($domain->is_base,1); is($domain->is_base,1);
is($domain->clones(), $n_clones) or exit; is($domain->clones(), $n_clones) or exit;
is($domain->clones(is_pool => 1), $n_clones); is($domain->clones(is_pool => 1), $n_clones);
my $clone = $domain->clone(name => new_domain_name, user => user_admin); my $clone = $domain->clone(name => new_domain_name, user => user_admin, from_pool => 0);
ok($clone); ok($clone);
is($domain->clones(), $n_clones+1); is($domain->clones(), $n_clones+1) or exit;
is($domain->clones(is_pool => 1), $n_clones); is($domain->clones(is_pool => 1), $n_clones);
my $clone_f = Ravada::Front::Domain->open($clone->id); my $clone_f = Ravada::Front::Domain->open($clone->id);
...@@ -92,6 +92,38 @@ sub _set_clones_client_status($base) { ...@@ -92,6 +92,38 @@ sub _set_clones_client_status($base) {
$sth->finish; $sth->finish;
} }
sub test_user_create($base, $n_start) {
$base->is_public(1);
my $user = create_user('kevin','carter');
my @clones = $base->clones();
wait_request();
_remove_enforce_limits();
_set_clones_client_status($base);
my $req = Ravada::Request->create_domain(
id_owner => $user->id
,start => 1
,name => new_domain_name()
,id_base => $base->id
,remote_ip => '1.2.3.4'
);
ok($req);
wait_request(debug => 0,skip => 'enforce_limits');
_remove_enforce_limits();
is($req->status,'done');
is($req->error,'');
my @clones2 = $base->clones();
is(scalar(@clones2), scalar(@clones));
my ($clone) = grep { $_->{id_owner} == $user->id } @clones2;
ok($clone,"Expecting clone that belongs to ".$user->name);
like($clone->{client_status},qr'^1.2.3.4$', $clone->{name}) or exit;
is($clone->{is_pool},1) or exit;
$user->remove();
}
sub test_user($base, $n_start) { sub test_user($base, $n_start) {
# TODO : test $domain->_data('comment');