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

feat(grants) shutdown clones

* test(grants): list virtual machines

issue #710

* feat(grants): allow remove all machines

issue #710

* fix(backend): cope with failure to resume

When stopping it tries to resume just in case. It crashes
if it is not paused

issue #739

* wip(grants): generic grants per machine

issue #749

* test(grants): shutdown clones

* feat(grants): shutdown clones

* fix(grants): ignore duplicated grant on startup

issue #739

* fix(backend): set default shutdown to 2 min

issue #739

* fix(shutdown): cleanup not necessary shutdowns

issue #739

* test(shutdown): process all requests

issue #739

* test(limits): test shuts down extra domain

issue #739

* wip(limits): only 1 running domain available

issue #739

* wip(grants): shutdown is now a long process

call to run all process

issue #739
parent 226f43d4
......@@ -80,7 +80,7 @@ $DIR_SQL = "/usr/share/doc/ravada/sql/mysql" if ! -e $DIR_SQL;
# LONG commands take long
our %HUGE_COMMAND = map { $_ => 1 } qw(download);
our %LONG_COMMAND = map { $_ => 1 } (qw(prepare_base remove_base screenshot ), keys %HUGE_COMMAND);
our %LONG_COMMAND = map { $_ => 1 } (qw(prepare_base remove_base screenshot shutdown force_shutdown ), keys %HUGE_COMMAND);
our $USER_DAEMON;
our $USER_DAEMON_NAME = 'daemon';
......@@ -663,12 +663,57 @@ sub _update_data {
}
sub _update_grants($self) {
my $sth = $CONNECTOR->dbh->prepare(
my %rename = (
create_domain => 'create_machine'
,remove_clone => 'remove_clones'
,shutdown_clone => 'shutdown_clones'
);
for my $old ( keys %rename ) {
my $sth = $CONNECTOR->dbh->prepare(
"UPDATE grant_types"
." SET name='create_machine' "
." WHERE name = 'create_domain'"
." SET name=? "
." WHERE name = ?"
);
$sth->execute($rename{$old}, $old);
}
$self->_add_grant('shutdown', 1);
}
sub _add_grant($self, $grant, $allowed) {
my $sth = $CONNECTOR->dbh->prepare(
"SELECT id FROM grant_types WHERE name=?"
);
$sth->execute();
$sth->execute($grant);
my ($id) = $sth->fetchrow();
$sth->finish;
return if $id;
$sth = $CONNECTOR->dbh->prepare("INSERT INTO grant_types (name, description)"
." VALUES (?,?)");
$sth->execute($grant,"can shutdown any virtual machine owned by the user");
$sth->finish;
return if !$allowed;
$sth = $CONNECTOR->dbh->prepare("SELECT id FROM grant_types WHERE name=?");
$sth->execute($grant);
my ($id_grant) = $sth->fetchrow;
$sth->finish;
my $sth_insert = $CONNECTOR->dbh->prepare(
"INSERT INTO grants_user (id_user, id_grant, allowed) VALUES(?,?,?) ");
$sth = $CONNECTOR->dbh->prepare("SELECT id FROM users ");
$sth->execute;
while (my ($id_user) = $sth->fetchrow ) {
eval { $sth_insert->execute($id_user, $id_grant, $allowed) };
die $@ if $@ && $@ !~/Duplicate entry /;
}
}
sub _null_grants($self) {
......@@ -695,8 +740,8 @@ sub _enable_grants($self) {
,'clone', 'clone_all', 'create_base', 'create_machine'
,'grant'
,'manage_users'
,'remove', 'remove_all', 'remove_clone', 'remove_clone_all'
,'shutdown_all', 'shutdown_clone'
,'remove', 'remove_all', 'remove_clones', 'remove_clone_all'
,'shutdown', 'shutdown_all', 'shutdown_clones'
);
$sth = $CONNECTOR->dbh->prepare("SELECT id,name FROM grant_types");
......@@ -2257,6 +2302,10 @@ sub _refresh_down_domains($self, $active_domain, $active_vm) {
my $status = 'shutdown';
$status = 'active' if $domain->is_active;
$domain->_set_data(status => $status);
for my $req ( $domain->list_requests ) {
next if $req->command !~ /shutdown/i;
$req->status('done');
}
}
}
}
......@@ -2274,6 +2323,10 @@ sub _refresh_volatile_domains($self) {
my $sth_del = $CONNECTOR->dbh->prepare("DELETE FROM domains WHERE id=?");
$sth_del->execute($id_domain);
$sth_del->finish;
$sth_del = $CONNECTOR->dbh->prepare("DELETE FROM requests where id_domain=?");
$sth_del->execute($id_domain);
$sth_del->finish;
}
}
}
......
......@@ -344,10 +344,10 @@ Returns true if the user is admin or has been granted special permissions
sub is_operator {
my $self = shift;
return $self->is_admin()
|| $self->can_shutdown_clone()
|| $self->can_shutdown_clones()
# || $self->can_hibernate_clone()
|| $self->can_change_settings_clones()
|| $self->can_remove_clone()
|| $self->can_remove_clones()
|| $self->can_remove_clone_all()
|| $self->can_create_base()
|| $self->can_create_machine
......@@ -385,8 +385,9 @@ sub can_list_clones {
}
sub can_list_clones_from_own_base($self) {
return 1 if $self->can_remove_clone || $self->can_remove_clone_all
|| $self->can_shutdown_clone;
return 1 if $self->can_remove_clones || $self->can_remove_clone_all
|| $self->can_shutdown_clones
|| $self->can_change_settings_clones;
return 0;
}
......@@ -562,6 +563,22 @@ sub can_do($self, $grant) {
return $self->{_grant}->{$grant};
}
sub can_do_domain($self, $grant, $domain) {
my %valid_grant = map { $_ => 1 } qw(change_settings shutdown);
confess "Invalid grant here '$grant'" if !$valid_grant{$grant};
return 0 if !$self->can_do($grant);
return 1 if $self->can_do("${grant}_all");
return 1 if $domain->id_owner == $self->id;
if ($self->can_do("${grant}_clones") && $domain->id_base) {
my $base = Ravada::Front::Domain->open($domain->id_base);
return 1 if $base->id_owner == $self->id;
}
return 0;
}
sub _load_grants($self) {
my $sth;
eval { $sth= $$CON->dbh->prepare(
......@@ -593,6 +610,7 @@ sub grant_user_permissions($self,$user) {
$self->grant($user, 'clone');
$self->grant($user, 'change_settings');
$self->grant($user, 'remove');
$self->grant($user, 'shutdown');
# $self->grant($user, 'screenshot');
}
......@@ -759,6 +777,8 @@ sub list_permissions($self) {
return @list;
}
=pod
sub can_change_settings($self, $id_domain=undef) {
if (!defined $id_domain) {
return $self->can_do("change_settings");
......@@ -773,30 +793,33 @@ sub can_change_settings($self, $id_domain=undef) {
return 0;
}
=cut
sub can_manage_machine($self, $domain) {
$domain = Ravada::Front::Domain->open($domain) if !ref $domain;
return 1 if $self->can_change_settings
&& $domain->id_owner == $self->id;
return 1 if $self->can_change_settings($domain);
return 1 if $self->can_remove_clone_all
&& $domain->id_base;
if ( $self->can_remove_clone && $domain->id_base ) {
if ( $self->can_remove_clones && $domain->id_base ) {
my $base = Ravada::Front::Domain->open($domain->id_base);
return 1 if $base->id_owner == $self->id;
}
return 0;
}
sub can_remove_clones($self, $id_domain) {
sub can_remove_clones($self, $id_domain=undef) {
return $self->can_do('remove_clones') if !$id_domain;
my $domain = Ravada::Front::Domain->open($id_domain);
confess "ERROR: domain is not a base " if !$domain->id_base;
return 1 if $self->can_remove_clone_all();
return 0 if !$self->can_remove_clone();
return 0 if !$self->can_remove_clones();
my $base = Ravada::Front::Domain->open($domain->id_base);
return 1 if $base->id_owner == $self->id;
......@@ -817,22 +840,6 @@ sub can_remove_machine($self, $domain) {
return 0;
}
sub can_shutdown_machine($self, $domain) {
return 1 if $self->can_shutdown_all();
$domain = Ravada::Front::Domain->open($domain) if !ref $domain;
return 1 if $self->id == $domain->id_owner;
if ($domain->id_base && $self->can_shutdown_clone()) {
my $base = Ravada::Front::Domain->open($domain->id_base);
return 1 if $base->id_owner == $self->id;
}
return 0;
}
sub grants($self) {
$self->_load_grants() if !$self->{_grant};
return () if !$self->{_grant};
......@@ -840,7 +847,7 @@ sub grants($self) {
}
sub AUTOLOAD($self) {
sub AUTOLOAD($self, $domain=undef) {
my $name = $AUTOLOAD;
$name =~ s/.*://;
......@@ -849,7 +856,10 @@ sub AUTOLOAD($self) {
if !ref($self) || $name !~ /^can_(.*)/;
my ($permission) = $name =~ /^can_([a-z_]+)/;
return $self->can_do($permission) if $permission;
return $self->can_do($permission) if !$domain;
$domain = Ravada::Front::Domain->open($domain) if !ref $domain;
return $self->can_do_domain($permission,$domain);
}
1;
......@@ -276,7 +276,7 @@ sub _allow_remove($self, $user) {
$self->_check_has_clones() if $self->is_known();
if ( $self->is_known
&& $self->id_base
&& ($user->can_remove_clone() || $user->can_remove_clone_all())
&& ($user->can_remove_clones() || $user->can_remove_clone_all())
) {
my $base = $self->open($self->id_base);
return if ($user->can_remove_clone_all() || ($base->id_owner == $user->id));
......@@ -297,7 +297,7 @@ sub _allow_shutdown {
confess "User ".$user->name." [".$user->id."] not allowed to shutdown ".$self->name
." owned by ".($self->id_owner or '<UNDEF>')
if !$user->can_shutdown_machine($self->id);
if !$user->can_shutdown($self->id);
}
sub _around_add_volume {
......@@ -1284,7 +1284,7 @@ sub _pre_shutdown {
$self->_pre_shutdown_domain();
if ($self->is_paused) {
$self->resume(user => $user);
$self->resume(user => Ravada::Utils::user_daemon);
}
$self->list_disks;
}
......
......@@ -569,6 +569,7 @@ sub start {
sub _pre_shutdown_domain {
my $self = shift;
my ($state, $reason) = $self->domain->get_state();
if ($state == Sys::Virt::Domain::STATE_PMSUSPENDED_UNKNOWN
......@@ -583,6 +584,7 @@ sub _pre_shutdown_domain {
$self->domain->managed_save_remove()
if $self->domain->has_managed_save_image();
}
=head2 shutdown
......@@ -642,7 +644,6 @@ sub _do_force_shutdown {
eval { $self->domain->destroy };
warn $@ if $@;
}
......@@ -665,7 +666,8 @@ Resumes a paused the domain
sub resume {
my $self = shift;
return $self->domain->resume();
eval { $self->domain->resume() };
die $@ if $@ && $@ !~ /libvirt error code: 55/;
}
......
......@@ -175,9 +175,10 @@ sub list_machines_user {
sub list_machines($self, $user) {
return $self->list_domains() if $user->can_list_machines;
if ($user->can_remove_clone() || $user->can_shutdown_clone() ) {
if ($user->can_remove_clones() || $user->can_shutdown_clones() ) {
my $machines = $self->list_bases( id_owner => $user->id );
for my $base (@$machines) {
confess "ERROR: BAse without id ".Dumper($base) if !$base->{id};
push @$machines,@{$self->list_domains( id_base => $base->{id} )};
}
return $machines;
......@@ -197,7 +198,7 @@ sub list_machines($self, $user) {
sub _around_list_machines($orig, $self, $user) {
my $machines = $self->$orig($user);
for my $m (@$machines) {
$m->{can_shutdown} = $user->can_shutdown_machine($m->{id});
$m->{can_shutdown} = $user->can_shutdown($m->{id});
$m->{can_start} = 0;
$m->{can_start} = 1 if $m->{id_owner} == $user->id || $user->is_admin;
......
......@@ -306,7 +306,7 @@ sub shutdown_domain {
my $args = _check_args('shutdown_domain', @_ );
$args->{timeout} = 10 if !exists $args->{timeout};
$args->{timeout} = 120 if !exists $args->{timeout};
confess "ERROR: You must supply either id_domain or name ".Dumper($args)
if !$args->{id_domain} && !$args->{name};
......
......@@ -370,7 +370,7 @@ get '/machine/clone/(:id).(:type)' => sub {
get '/machine/shutdown/(:id).(:type)' => sub {
my $c = shift;
return access_denied($c) if !$USER ->can_shutdown_machine($c->stash('id'));
return access_denied($c) if !$USER ->can_shutdown($c->stash('id'));
return shutdown_machine($c);
};
......@@ -427,7 +427,7 @@ get '/machine/pause/(:id).(:type)' => sub {
get '/machine/hybernate/(:id).(:type)' => sub {
my $c = shift;
return access_denied($c)
unless $USER->is_admin() || $USER->can_shutdown_machine($c->stash('id'));
unless $USER->is_admin() || $USER->can_shutdown($c->stash('id'));
return hybernate_machine($c);
};
......
......@@ -130,7 +130,7 @@ sub test_start {
# stop
my $req3 = Ravada::Request->force_shutdown_domain(id_domain => $id_domain, uid => $USER->id);
$RAVADA->process_requests();
$RAVADA->_process_all_requests_dont_fork(0);
wait_request($req3);
ok($req3->status eq 'done',"[$vm_name] expecting request done , got "
.$req3->status);
......
......@@ -32,7 +32,7 @@ sub test_defaults {
ok($user->can_remove);
ok(!$user->can_remove_clone);
ok(!$user->can_remove_clones);
# ok(!$user->can_clone_all);
ok(!$user->can_change_settings_all);
......@@ -105,7 +105,7 @@ sub test_operator {
ok($usera->is_operator);
ok($usera->is_admin);
$usera->grant($usero,'shutdown_clone');
$usera->grant($usero,'shutdown_clones');
ok($usero->is_operator);
ok(!$usero->is_admin);
......@@ -132,8 +132,8 @@ sub test_remove_clone {
eval { $clone2 = rvd_back->search_domain($clone->name) };
ok($clone2, "Expecting ".$clone->name." not removed");
$usera->grant($user,'remove_clone');
is($user->can_remove_clone, 1);
$usera->grant($user,'remove_clones');
is($user->can_remove_clones, 1);
eval { $clone->remove($user); };
is($@,'');
......@@ -143,7 +143,7 @@ sub test_remove_clone {
# revoking remove clone permission
$clone = $domain->clone(user => $usera,name => new_domain_name());
$usera->revoke($user,'remove_clone');
$usera->revoke($user,'remove_clones');
eval { $clone->remove($user); };
like($@,qr(.));
......@@ -212,8 +212,8 @@ sub test_shutdown_clone {
is($clone->is_active,1) or return;
$usera->grant($user,'shutdown_clone');
is($user->can_shutdown_clone,1);
$usera->grant($user,'shutdown_clones');
is($user->can_shutdown_clones,1);
eval { $clone->shutdown_now($user); };
is($@,'');
......@@ -223,7 +223,7 @@ sub test_shutdown_clone {
$clone->start($usera) if !$clone->is_active;
is($clone->is_active,1);
$usera->revoke($user,'shutdown_clone');
$usera->revoke($user,'shutdown_clones');
eval { $clone->shutdown_now($user); };
like($@,qr(.));
is($clone->is_active,1);
......
......@@ -195,7 +195,7 @@ sub test_list_clones_from_own_base {
my $list = rvd_front->list_machines($user);
is(scalar @$list , 0);
user_admin->grant($user, 'remove_clone');
user_admin->grant($user, 'remove_clones');
is($user->can_list_machines, 0);
$list = rvd_front->list_machines($user);
......@@ -232,7 +232,7 @@ sub test_list_clones_from_own_base_2 {
my $list = rvd_front->list_machines($user);
is(scalar @$list , 0);
user_admin->grant($user, 'remove_clone');
user_admin->grant($user, 'remove_clones');
is($user->can_list_machines, 0);
$list = rvd_front->list_machines($user);
......@@ -337,7 +337,7 @@ sub test_list_clones_from_own_base_deny {
my $list = rvd_front->list_machines($user);
is(scalar @$list , 0);
user_admin->grant($user, 'remove_clone');
user_admin->grant($user, 'remove_clones');
is($user->can_list_machines, 0);
is($user->can_list_clones_from_own_base, 1);
......
......@@ -22,7 +22,7 @@ sub test_shutdown_admin {
my $domain = create_domain($vm->type);
$domain->start(user_admin) if !$domain->is_active();
is(user_admin->can_shutdown_machine($domain->id), 1);
is(user_admin->can_shutdown($domain->id), 1);
is($domain->is_active,1) or return;
eval { $domain->shutdown( user => user_admin ) };
......@@ -49,6 +49,9 @@ sub test_shutdown_own {
# can shutdown by default
is($user->can_remove_machine($base), 1);
is($user->can_shutdown, 1);
is($user->can_shutdown($base), 1);
$base->shutdown_now( $user );
is($base->is_active,0,"[".$base->type."] expecting base down");
......@@ -68,7 +71,7 @@ sub test_shutdown_all {
my $user = create_user("kevin.garvey","sleepwalk");
is($user->can_shutdown_machine($base), 0);
is($user->can_shutdown($base), 0);
eval { $base->shutdown_now( $user ) };
like($@, qr'.');
......@@ -80,7 +83,7 @@ sub test_shutdown_all {
$base->start(user_admin) if !$base->is_active;
is($user->can_shutdown_machine($base), 1);
is($user->can_shutdown($base), 1);
eval { $base->shutdown_now( $user ) };
is($@, '');
......@@ -107,11 +110,11 @@ sub test_shutdown_clones_from_own_base {
$clone->start(user_admin);
is($clone->is_active, 1);
is($user->can_shutdown_machine($clone->id), 0);
is($user->can_shutdown($clone->id), 0);
user_admin->grant($user,'shutdown_clone');
user_admin->grant($user,'shutdown_clones');
is($user->can_shutdown_machine($clone->id), 1);
is($user->can_shutdown($clone->id), 1);
is($user->is_operator,1);
eval { $clone->shutdown_now($user)};
......@@ -123,6 +126,10 @@ sub test_shutdown_clones_from_own_base {
is($@, '');
is($clone->is_hibernated, 1);
eval { $clone->shutdown_now($user)};
is($@, '');
is($clone->is_active, 0);
$clone->remove(user_admin);
$base->remove(user_admin);
......@@ -155,7 +162,7 @@ sub test_list_all{
is(scalar @$list , 2);
for my $m (@$list) {
is($user->can_shutdown_machine($m->{id}) ,1);
is($user->can_shutdown($m->{id}) ,1);
next if !$m->{id_base};
my $machine = Ravada::Domain->open($m->{id});
......@@ -194,7 +201,7 @@ sub test_list_clones_from_own_base {
my $list = rvd_front->list_machines($user);
is(scalar @$list , 0);
user_admin->grant($user, 'shutdown_clone');
user_admin->grant($user, 'shutdown_clones');
is($user->can_list_machines, 0);
$list = rvd_front->list_machines($user);
......@@ -233,14 +240,16 @@ sub test_list_clones_from_own_base_2 {
my $list = rvd_front->list_machines($user);
is(scalar @$list , 0);
user_admin->grant($user, 'shutdown_clone');
user_admin->grant($user, 'shutdown_clones');
is($user->can_list_machines, 0);
$list = rvd_front->list_machines($user);
is(scalar @$list , 3) and do {
is($list->[0]->{name}, $base->name);
is($list->[1]->{name}, $clone->name, Dumper($list->[1]));
is($list->[1]->{can_shutdown}, 1);
is($list->[2]->{name}, $clone2->name, Dumper($list->[2]));
is($list->[2]->{can_shutdown}, 1);
};
#####################################################################3
......@@ -300,7 +309,7 @@ sub test_list_clones_from_own_base_deny {
my $list = rvd_front->list_machines($user);
is(scalar @$list , 0);
user_admin->grant($user, 'shutdown_clone');
user_admin->grant($user, 'shutdown_clones');
is($user->can_list_machines, 0);
is($user->can_list_clones_from_own_base, 1);
......
......@@ -494,9 +494,10 @@ sub test_domain_limit_already_requested {
sleep 3;
rvd_back->_process_requests_dont_fork();
} else {
@list_requests = $domain->list_requests;
is(scalar @list_requests,0,"Expecting 0 request ".Dumper(\@list_requests)) or exit;
}
@list_requests = $domain->list_requests;
is(scalar @list_requests,0,"Expecting 0 request ".Dumper(\@list_requests)) or exit;
my @list = rvd_back->list_domains(user => user_admin , active => 1);
is(scalar @list,1) or die Dumper(\@list);
......
......@@ -44,16 +44,17 @@ sub test_run_timeout {
is($clone->run_timeout(),$timeout);
$clone->start(user => $USER);
is(scalar($clone->list_requests(1)),1) or exit;
is($clone->is_active,1);
rvd_back->_process_requests_dont_fork();
rvd_back->_process_all_requests_dont_fork();
is($clone->is_active,1);
sleep($timeout);
rvd_back->_process_requests_dont_fork();
rvd_back->_process_all_requests_dont_fork();
for ( 1 .. 60 ) {
last if !$clone->is_active;
sleep 1;
rvd_back->_process_requests_dont_fork();
rvd_back->_process_all_requests_dont_fork(1);
}
is($clone->is_active,0);
......
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