From 620c8f6db8c48639df9830694fc38cd04bee1b07 Mon Sep 17 00:00:00 2001 From: frankiejol Date: Mon, 24 Jul 2017 11:00:29 +0200 Subject: [PATCH 01/52] [#315] test active domains limit --- t/vm/20_base.t | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/t/vm/20_base.t b/t/vm/20_base.t index 3de75c1df..8f4bb4e7c 100644 --- a/t/vm/20_base.t +++ b/t/vm/20_base.t @@ -330,7 +330,40 @@ sub test_dont_remove_base_cloned { } +sub test_domain_limit { + my $vm_name = shift; + + for my $domain ( rvd_back->list_domains()) { + $domain->shutdown_now(user_admin); + } + my $domain = create_domain($vm_name, $USER); + ok($domain,"Expecting a new domain created") or exit; + $domain->shutdown_now($USER) if $domain->is_active; + + is(rvd_back->list_domains(user => $USER, active => 1),0 + ,Dumper(rvd_back->list_domains())) or exit; + + $domain->start($USER); + is($domain->is_active,1); + ok($domain->start_time <= time,"Expecting start time <= ".time + ." got ".time); + + sleep 1; + is(rvd_back->list_domains(user => $USER, active => 1),1); + + my $domain2 = create_domain($vm_name, $USER); + $domain2->shutdown_now($USER) if $domain2->is_active; + is(rvd_back->list_domains(user => $USER, active => 1),1); + + $domain2->start($USER); + rvd_back->enforce_limits(timeout => 2); + sleep 2; + rvd_back->_process_requests_dont_fork(1); + my @list = rvd_back->list_domains(user => $USER, active => 1); + is(scalar @list,1) or die Dumper(\@list); + is($list[0]->name, $domain2->name) if $list[0]; +} #######################################################################33 @@ -367,6 +400,8 @@ for my $vm_name (reverse sort @VMS) { test_prepare_base_active($vm_name); test_remove_base($vm_name); test_dont_remove_base_cloned($vm_name); + + test_domain_limit($vm_name); } } From c5dd60292421873d6f70c3dc803d870040891acc Mon Sep 17 00:00:00 2001 From: frankiejol Date: Mon, 24 Jul 2017 11:01:30 +0200 Subject: [PATCH 02/52] [#315] store start time In the future we should add an accounting table to store start , shutdown, etc. Or we can read this information from the requests table. --- sql/mysql/domains.sql | 1 + sql/sqlite/domains.sql | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/sql/mysql/domains.sql b/sql/mysql/domains.sql index b475b96d0..8387e6893 100644 --- a/sql/mysql/domains.sql +++ b/sql/mysql/domains.sql @@ -14,6 +14,7 @@ CREATE TABLE `domains` ( `vm` char(120) NOT NULL, `spice_password` char(20) DEFAULT NULL, `description` text, + `start_time` int not null default 0, PRIMARY KEY (`id`), UNIQUE KEY `id_base` (`id_base`,`name`), UNIQUE KEY `name` (`name`) diff --git a/sql/sqlite/domains.sql b/sql/sqlite/domains.sql index 09318ce40..ebd194c32 100644 --- a/sql/sqlite/domains.sql +++ b/sql/sqlite/domains.sql @@ -13,7 +13,8 @@ CREATE TABLE `domains` ( , `id_owner` integer , `vm` char(120) NOT NULL , `spice_password` char(20) DEFAULT NULL -, `description` text DEFAULT NULL +, `description` text +, `start_time` integer not null default 0 , UNIQUE (`id_base`,`name`) , UNIQUE (`name`) ); From abe94702b669981ab0e1c521ebf865b9bccea14e Mon Sep 17 00:00:00 2001 From: frankiejol Date: Mon, 24 Jul 2017 11:04:37 +0200 Subject: [PATCH 03/52] [#315] add start time field for upgrades --- lib/Ravada.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index e97ecd5cf..aadec895a 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -601,6 +601,7 @@ sub _upgrade_tables { $self->_upgrade_table('domains','spice_password','varchar(20) DEFAULT NULL'); $self->_upgrade_table('domains','description','text DEFAULT NULL'); + $self->_upgrade_table('domains','start_time','int DEFAULT 0'); } From 7c9ec3e0ab4c5a58ea4c0a735b247f24e6361d4c Mon Sep 17 00:00:00 2001 From: frankiejol Date: Mon, 24 Jul 2017 11:08:48 +0200 Subject: [PATCH 04/52] [#315] add Void Virtual Manager when testing --- lib/Ravada.pm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index aadec895a..807743cf1 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -735,6 +735,9 @@ sub _create_vm { if (!@vms) { warn "No VMs found: $err\n" if $self->warn_error; } + if ($0 =~ /\.t$/) { + push @vms,(Ravada::VM::Void->new()); + } return \@vms; } From c032598fc2137dce2dcb64c67c321cfaa46d90e5 Mon Sep 17 00:00:00 2001 From: frankiejol Date: Mon, 24 Jul 2017 11:11:58 +0200 Subject: [PATCH 05/52] [#315] filter domain list --- lib/Ravada.pm | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index 807743cf1..ef22b1902 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -912,13 +912,36 @@ List all created domains my @list = $ravada->list_domains(); +This list can be filtered: + + my @active = $ravada->list_domains(active => 1); + my @inactive = $ravada->list_domains(active => 0); + + my @user_domains = $ravada->list_domains(user => $id_user); + + my @user_active = $ravada->list_domains(user => $id_user, active => 1); + =cut sub list_domains { my $self = shift; + my %args = @_; + + my $active = delete $args{active}; + my $user = delete $args{user}; + + die "ERROR: Unknown arguments ".join(",",sort keys %args) + if keys %args; + my @domains; for my $vm ($self->list_vms) { for my $domain ($vm->list_domains) { + next if defined $active && + ( $domain->is_active && !$active + || !$domain->is_active && $active ); + + next if $user && $domain->id_owner != $user->id; + push @domains,($domain); } } From c89a3d7b524568a848332c23a54e92121b4a8044 Mon Sep 17 00:00:00 2001 From: frankiejol Date: Mon, 24 Jul 2017 11:35:09 +0200 Subject: [PATCH 06/52] [#315] enforce active domains by user limit --- lib/Ravada.pm | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index ef22b1902..26a708db7 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -1865,6 +1865,50 @@ sub import_domain { return $vm->import_domain($name, $user); } +=head enforce_limits + +Check no user has passed the limits and take action. + +Some limits: + +- More than 1 domain running at a time ( older get shut down ) + + +=cut + +sub enforce_limits { + _enforce_limits_active(@_); +} + +sub _enforce_limits_active { + my $self = shift; + my %args = @_; + + my $timeout = (delete $args{timeout} or 10); + + confess "ERROR: Unknown arguments ".join(",",sort keys %args) + if keys %args; + + my %domains; + for my $domain ($self->list_domains( active => 1 )) { + push @{$domains{$domain->id_owner}},$domain; + } + for my $id_user(keys %domains) { + next if scalar @{$domains{$id_user}}<2; + + my @domains_user = sort { $a->start_time <=> $b->start_time } + @{$domains{$id_user}}; + +# my @list = map { $_->name => $_->start_time } @domains_user; + my $last = pop @domains_user; + for my $domain (@domains_user) { + warn "requesting shut down ".$domain->name; + #TODO check the domain shutdown has been already requested + $domain->shutdown(timeout => $timeout, user => $USER_DAEMON ); + } + } +} + =head2 version Returns the version of the module From e0119a6b3796b679ad0f04609fa337840f41e258 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Fri, 21 Jul 2017 16:15:19 +0200 Subject: [PATCH 07/52] enforced create domain args --- lib/Ravada/Request.pm | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/lib/Ravada/Request.pm b/lib/Ravada/Request.pm index 5d9a3be51..e5ccd9702 100644 --- a/lib/Ravada/Request.pm +++ b/lib/Ravada/Request.pm @@ -37,11 +37,11 @@ our %VALID_ARG = ( vm => 1 ,name => 1 ,swap => 2 - ,id_iso => 1 + ,id_iso => 2 ,iso_file => 2 - ,id_base => 1 + ,id_base => 2 ,id_owner => 1 - ,id_template => 1 + ,id_template => 2 ,memory => 2 ,disk => 2 ,network => 2 @@ -140,19 +140,15 @@ sub create_domain { my %args = @_; - confess "Missing domain name " - if !$args{name}; + my $args = _check_args('create_domain', @_ ); - for (keys %args) { - confess "Invalid argument $_" if !$VALID_ARG{'create_domain'}->{$_}; - } my $self = {}; - if ($args{network}) { - $args{network} = JSON::XS->new->convert_blessed->encode($args{network}); + if ($args->{network}) { + $args->{network} = JSON::XS->new->convert_blessed->encode($args->{network}); } bless($self,$class); - return $self->_new_request(command => 'create' , args => encode_json(\%args)); + return $self->_new_request(command => 'create' , args => encode_json($args)); } =head2 remove_domain From c6f1202d4c212ed84534b74b8870e38cf634930d Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Fri, 21 Jul 2017 16:15:37 +0200 Subject: [PATCH 08/52] [#314] test prepare base must run before create --- t/30_request.t | 41 +++++++++++++++++++++++++++++++++++++++++ t/lib/Test/Ravada.pm | 1 + 2 files changed, 42 insertions(+) diff --git a/t/30_request.t b/t/30_request.t index b2bcdf57f..3e15e4d29 100644 --- a/t/30_request.t +++ b/t/30_request.t @@ -222,6 +222,45 @@ sub test_unread_messages { $user->mark_all_messages_read(); } +sub test_requests_by_domain { + my $vm_name = shift; + + my $vm = rvd_back->search_vm($vm_name); + my $domain = create_domain($vm_name, user_admin); + ok($domain,"Expecting new domain created") or exit; + + my $req1 = Ravada::Request->prepare_base(uid => user_admin->id, id_domain => $domain->id); + ok($domain->list_requests == 1); + + my $req2 = Ravada::Request->remove_base(uid => user_admin->id, id_domain => $domain->id); + ok($domain->list_requests == 2); + + my $clone_name = new_domain_name(); + my $req_clone = Ravada::Request->create_domain ( + name => $clone_name + ,id_owner => user_admin->id + ,id_base => $domain->id + ,vm => $vm_name + ); + + my $req3 = Ravada::Request->prepare_base(uid => user_admin->id, id_domain => $domain->id); + ok($domain->list_requests == 3); + + eval { + rvd_back->_process_all_requests_dont_fork(1); + }; + + is($@,''); + like($req_clone->error,qr(Waiting)); + is($req_clone->status , 'retry'); + + rvd_back->_process_all_requests_dont_fork(1); + like($req_clone->error,qr(done)); + is($req_clone->status , 'done'); + + my $clone = $vm->search_domain($clone_name); + ok($clone,"Expecting domain $clone_name created") or exit; +} ################################################ eval { $ravada = Ravada->new(connector => $test->connector) }; @@ -247,6 +286,7 @@ for my $vm_name ( qw(Void KVM)) { diag("Testing requests with ".(ref $vm or '')); + test_requests_by_domain($vm_name); my $domain_iso0 = test_req_create_domain_iso($vm_name); test_req_remove_domain_obj($vm, $domain_iso0) if $domain_iso0; @@ -258,6 +298,7 @@ for my $vm_name ( qw(Void KVM)) { test_req_start_domain($vm,$domain_base->name); test_req_remove_domain_name($vm, $domain_base->name); } + }; } diff --git a/t/lib/Test/Ravada.pm b/t/lib/Test/Ravada.pm index f406e73df..04f8016f0 100644 --- a/t/lib/Test/Ravada.pm +++ b/t/lib/Test/Ravada.pm @@ -34,6 +34,7 @@ our $CHAIN = 'RAVADA'; my %ARG_CREATE_DOM = ( kvm => [ id_iso => 1 ] + ,void => [] ); sub user_admin { From f6ff248133885cd941e8753e32329920721c92bd Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Fri, 21 Jul 2017 16:16:02 +0200 Subject: [PATCH 09/52] [#314] retrying requests --- lib/Ravada.pm | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index 8c8f99bd6..b08589d58 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -799,7 +799,17 @@ sub create_domain { confess "I can't find any vm ".Dumper($self->vm) if !$vm; - return $vm->create_domain(@_); + my $domain; + eval { $domain = $vm->create_domain(@_) }; + my $error = $@; + warn "ERROR AQUI: $error" if $error; + $request->error($error) if $error; + if ($error =~ /has requests/) { + warn "hey ".$request->id; + $request->error("Waiting for other requests from base"); + $request->status('retry'); + } + return $domain; } =head2 remove_domain @@ -1123,7 +1133,7 @@ sub process_requests { my $sth = $CONNECTOR->dbh->prepare("SELECT id,id_domain FROM requests " ." WHERE " - ." ( status='requested' OR status like 'retry %' OR status='waiting')" + ." ( status='requested' OR status like 'retry%' OR status='waiting')" ." AND ( at_time IS NULL OR at_time = 0 OR at_time<=?) " ." ORDER BY date_req" ); @@ -1275,9 +1285,11 @@ sub _execute { if ($dont_fork || !$CAN_FORK || !$LONG_COMMAND{$request->command}) { eval { $sub->($self,$request) }; + warn $request->id." ".$request->status()." ".($request->error or ''); my $err = ($@ or ''); - $request->error($err); - $request->status('done') if $request->status() ne 'done'; + $request->error($err) if $err; + $request->status('done') if $request->status() ne 'done' + && $request->status !~ /retry/; return $err; } @@ -1316,7 +1328,9 @@ sub _do_execute_command { }; my $err = ( $@ or ''); $request->error($err); - $request->status('done') if $request->status() ne 'done'; + $request->status('done') + if $request->status() ne 'done' + && $request->status() !~ /^retry/i; exit; } @@ -1370,9 +1384,9 @@ sub _cmd_create{ .$request->args('name')."" ." created." ; + $request->status('done',$msg); } - $request->status('done',$msg); } From 98c069f86b9cf524bd7c0d03ca0ee25b453f81db Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Fri, 21 Jul 2017 16:16:29 +0200 Subject: [PATCH 10/52] [#314] list domain requests pending --- lib/Ravada/Domain.pm | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index c2ca840ad..edaf7cffc 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -1411,6 +1411,27 @@ sub remote_ip { } +=head2 list_requests + +Returns a list of pending requests from the domain + +=cut + +sub list_requests { + my $self = shift; + my $sth = $$CONNECTOR->dbh->prepare( + "SELECT * FROM requests WHERE id_domain = ? AND status ne 'done'" + ); + $sth->execute($self->id); + my @list; + while ( my $req_data = $sth->fetchrow_hashref ) { + push @list,($req_data); + } + $sth->finish; + return scalar @list if !wantarray; + return map { Ravada::Request->open($_->{id}) } @list; +} + sub _dbh { my $self = shift; _init_connector() if !$CONNECTOR || !$$CONNECTOR; From c103e106430577f6c4f7ed42fa23442afa608355 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Fri, 21 Jul 2017 16:16:45 +0200 Subject: [PATCH 11/52] [#314] don't prepare base if base has pending requests --- lib/Ravada/VM.pm | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/Ravada/VM.pm b/lib/Ravada/VM.pm index 6d2341821..58911827e 100644 --- a/lib/Ravada/VM.pm +++ b/lib/Ravada/VM.pm @@ -133,6 +133,7 @@ sub _around_create_domain { my %args = @_; $self->_pre_create_domain(@_); + my $domain = $self->$orig(@_); $domain->add_volume_swap( size => $args{swap}) if $args{swap}; @@ -297,7 +298,11 @@ sub _check_require_base { my %args = @_; return if !$args{id_base}; - my $base = $self->search_domain_by_id($args{id_base}); + my $base = Ravada::Domain->open($args{id_base}); + if ($base->list_requests) { + die "ERROR: Domain ".$self->name." has ".$base->list_requests." requests "; + } + die "ERROR: Domain ".$self->name." is not base" if !$base->is_base(); From c370d35c8de9a0de1709abeb671c06ae6ac50f30 Mon Sep 17 00:00:00 2001 From: frankiejol Date: Mon, 24 Jul 2017 10:29:55 +0200 Subject: [PATCH 12/52] [#314] prepare base before copy if necessary --- rvd_front.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rvd_front.pl b/rvd_front.pl index 9cd5955dc..c795a6133 100644 --- a/rvd_front.pl +++ b/rvd_front.pl @@ -1512,7 +1512,7 @@ sub copy_machine { my $name = $c->req->param($param_name) if $param_name; $name = $base->name."-".$USER->name if !$name; - if (!$base->is_base) { + if (!$base->is_base || $base->is_locked) { my $req = Ravada::Request->prepare_base( id_domain => $id_base ,uid => $USER->id From d7da51a3c4fdd55cc5308806d9ae3d3f6ed98e05 Mon Sep 17 00:00:00 2001 From: frankiejol Date: Mon, 24 Jul 2017 10:33:01 +0200 Subject: [PATCH 13/52] [#314] test request must wait if domain busy --- t/30_request.t | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/t/30_request.t b/t/30_request.t index 3e15e4d29..6b9d60b00 100644 --- a/t/30_request.t +++ b/t/30_request.t @@ -243,20 +243,30 @@ sub test_requests_by_domain { ,vm => $vm_name ); - my $req3 = Ravada::Request->prepare_base(uid => user_admin->id, id_domain => $domain->id); + my $req4 = Ravada::Request->prepare_base(uid => user_admin->id, id_domain => $domain->id); ok($domain->list_requests == 3); eval { - rvd_back->_process_all_requests_dont_fork(1); + rvd_back->_process_all_requests_dont_fork(); }; + + is($req1->status , 'done'); + is($req2->status , 'done'); + is($@,''); - like($req_clone->error,qr(Waiting)); + like($req_clone->error,qr(has \d req)) or exit; is($req_clone->status , 'retry'); - rvd_back->_process_all_requests_dont_fork(1); - like($req_clone->error,qr(done)); - is($req_clone->status , 'done'); + is($req4->status , 'done'); + is($domain->is_base,1) or exit; + + my $req4b = Ravada::Request->open($req4->id); + is($req4b->status , 'done') or exit; + + rvd_back->_process_all_requests_dont_fork(); + like($req_clone->status,qr(done)) or exit; + is($req_clone->error, '') or exit; my $clone = $vm->search_domain($clone_name); ok($clone,"Expecting domain $clone_name created") or exit; From d31e42a16df58387664067e6b756f64e2c0bade1 Mon Sep 17 00:00:00 2001 From: frankiejol Date: Mon, 24 Jul 2017 10:35:18 +0200 Subject: [PATCH 14/52] Give up after 3 retries --- lib/Ravada.pm | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index b08589d58..e97ecd5cf 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -802,11 +802,8 @@ sub create_domain { my $domain; eval { $domain = $vm->create_domain(@_) }; my $error = $@; - warn "ERROR AQUI: $error" if $error; $request->error($error) if $error; - if ($error =~ /has requests/) { - warn "hey ".$request->id; - $request->error("Waiting for other requests from base"); + if ($error =~ /has \d+ requests/) { $request->status('retry'); } return $domain; @@ -1170,6 +1167,8 @@ sub process_requests { if ( $n_retry < 3) { warn $req->id." ".$req->command." to retry" if $DEBUG; $req->status("retry ".++$n_retry) + } else { + $req->status("done"); } } next if !$DEBUG && !$debug; @@ -1282,10 +1281,10 @@ sub _execute { confess "Unknown command ".$request->command if !$sub; + $request->error(''); if ($dont_fork || !$CAN_FORK || !$LONG_COMMAND{$request->command}) { eval { $sub->($self,$request) }; - warn $request->id." ".$request->status()." ".($request->error or ''); my $err = ($@ or ''); $request->error($err) if $err; $request->status('done') if $request->status() ne 'done' From 7dafebb4a45370e31b10cf39de4761e9d7fbba2f Mon Sep 17 00:00:00 2001 From: frankiejol Date: Mon, 24 Jul 2017 10:35:46 +0200 Subject: [PATCH 15/52] Virtual Manager optional --- lib/Ravada/Request.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Ravada/Request.pm b/lib/Ravada/Request.pm index e5ccd9702..17d7c89c6 100644 --- a/lib/Ravada/Request.pm +++ b/lib/Ravada/Request.pm @@ -34,7 +34,7 @@ our $args_manage_iptables = {uid => 1, id_domain => 1, remote_ip => 1}; our %VALID_ARG = ( create_domain => { - vm => 1 + vm => 2 ,name => 1 ,swap => 2 ,id_iso => 2 From 7d8d45b7f266cbd4a06797cc45347f0fc1ba0843 Mon Sep 17 00:00:00 2001 From: frankiejol Date: Mon, 24 Jul 2017 10:52:08 +0200 Subject: [PATCH 16/52] [#314] search for unifished requests --- lib/Ravada/Domain.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index edaf7cffc..0f8291189 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -1420,7 +1420,7 @@ Returns a list of pending requests from the domain sub list_requests { my $self = shift; my $sth = $$CONNECTOR->dbh->prepare( - "SELECT * FROM requests WHERE id_domain = ? AND status ne 'done'" + "SELECT * FROM requests WHERE id_domain = ? AND status <> 'done'" ); $sth->execute($self->id); my @list; From d9a3b475666c2e41546461170266e881e0aae714 Mon Sep 17 00:00:00 2001 From: frankiejol Date: Mon, 24 Jul 2017 10:52:32 +0200 Subject: [PATCH 17/52] [#314] die if unfinished requests --- lib/Ravada/VM.pm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Ravada/VM.pm b/lib/Ravada/VM.pm index 58911827e..4513dcf1c 100644 --- a/lib/Ravada/VM.pm +++ b/lib/Ravada/VM.pm @@ -300,7 +300,8 @@ sub _check_require_base { my $base = Ravada::Domain->open($args{id_base}); if ($base->list_requests) { - die "ERROR: Domain ".$self->name." has ".$base->list_requests." requests "; + die "ERROR: Domain ".$base->name." has ".$base->list_requests + ." requests.\n"; } die "ERROR: Domain ".$self->name." is not base" From e8a3533b3480d9794bc1163801c4dd75357985ce Mon Sep 17 00:00:00 2001 From: frankiejol Date: Mon, 24 Jul 2017 11:00:29 +0200 Subject: [PATCH 18/52] [#315] test active domains limit --- t/vm/20_base.t | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/t/vm/20_base.t b/t/vm/20_base.t index 3de75c1df..8f4bb4e7c 100644 --- a/t/vm/20_base.t +++ b/t/vm/20_base.t @@ -330,7 +330,40 @@ sub test_dont_remove_base_cloned { } +sub test_domain_limit { + my $vm_name = shift; + + for my $domain ( rvd_back->list_domains()) { + $domain->shutdown_now(user_admin); + } + my $domain = create_domain($vm_name, $USER); + ok($domain,"Expecting a new domain created") or exit; + $domain->shutdown_now($USER) if $domain->is_active; + + is(rvd_back->list_domains(user => $USER, active => 1),0 + ,Dumper(rvd_back->list_domains())) or exit; + + $domain->start($USER); + is($domain->is_active,1); + ok($domain->start_time <= time,"Expecting start time <= ".time + ." got ".time); + + sleep 1; + is(rvd_back->list_domains(user => $USER, active => 1),1); + + my $domain2 = create_domain($vm_name, $USER); + $domain2->shutdown_now($USER) if $domain2->is_active; + is(rvd_back->list_domains(user => $USER, active => 1),1); + + $domain2->start($USER); + rvd_back->enforce_limits(timeout => 2); + sleep 2; + rvd_back->_process_requests_dont_fork(1); + my @list = rvd_back->list_domains(user => $USER, active => 1); + is(scalar @list,1) or die Dumper(\@list); + is($list[0]->name, $domain2->name) if $list[0]; +} #######################################################################33 @@ -367,6 +400,8 @@ for my $vm_name (reverse sort @VMS) { test_prepare_base_active($vm_name); test_remove_base($vm_name); test_dont_remove_base_cloned($vm_name); + + test_domain_limit($vm_name); } } From f13aa9754491ee32672387a6c606620bd51a8443 Mon Sep 17 00:00:00 2001 From: frankiejol Date: Mon, 24 Jul 2017 11:01:30 +0200 Subject: [PATCH 19/52] [#315] store start time In the future we should add an accounting table to store start , shutdown, etc. Or we can read this information from the requests table. --- sql/mysql/domains.sql | 1 + sql/sqlite/domains.sql | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/sql/mysql/domains.sql b/sql/mysql/domains.sql index b475b96d0..8387e6893 100644 --- a/sql/mysql/domains.sql +++ b/sql/mysql/domains.sql @@ -14,6 +14,7 @@ CREATE TABLE `domains` ( `vm` char(120) NOT NULL, `spice_password` char(20) DEFAULT NULL, `description` text, + `start_time` int not null default 0, PRIMARY KEY (`id`), UNIQUE KEY `id_base` (`id_base`,`name`), UNIQUE KEY `name` (`name`) diff --git a/sql/sqlite/domains.sql b/sql/sqlite/domains.sql index 09318ce40..ebd194c32 100644 --- a/sql/sqlite/domains.sql +++ b/sql/sqlite/domains.sql @@ -13,7 +13,8 @@ CREATE TABLE `domains` ( , `id_owner` integer , `vm` char(120) NOT NULL , `spice_password` char(20) DEFAULT NULL -, `description` text DEFAULT NULL +, `description` text +, `start_time` integer not null default 0 , UNIQUE (`id_base`,`name`) , UNIQUE (`name`) ); From ef2f3d5a555aaa1a5d32b2f783b94b922e43b457 Mon Sep 17 00:00:00 2001 From: frankiejol Date: Mon, 24 Jul 2017 11:04:37 +0200 Subject: [PATCH 20/52] [#315] add start time field for upgrades --- lib/Ravada.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index e97ecd5cf..aadec895a 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -601,6 +601,7 @@ sub _upgrade_tables { $self->_upgrade_table('domains','spice_password','varchar(20) DEFAULT NULL'); $self->_upgrade_table('domains','description','text DEFAULT NULL'); + $self->_upgrade_table('domains','start_time','int DEFAULT 0'); } From 0f0d4d0e03be073bbf5bcea9072f998996150c56 Mon Sep 17 00:00:00 2001 From: frankiejol Date: Mon, 24 Jul 2017 11:08:48 +0200 Subject: [PATCH 21/52] [#315] add Void Virtual Manager when testing --- lib/Ravada.pm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index aadec895a..807743cf1 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -735,6 +735,9 @@ sub _create_vm { if (!@vms) { warn "No VMs found: $err\n" if $self->warn_error; } + if ($0 =~ /\.t$/) { + push @vms,(Ravada::VM::Void->new()); + } return \@vms; } From fe7af5552a8ffb4a0dd5b4346bab637de62328a0 Mon Sep 17 00:00:00 2001 From: frankiejol Date: Mon, 24 Jul 2017 11:11:58 +0200 Subject: [PATCH 22/52] [#315] filter domain list --- lib/Ravada.pm | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index 807743cf1..ef22b1902 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -912,13 +912,36 @@ List all created domains my @list = $ravada->list_domains(); +This list can be filtered: + + my @active = $ravada->list_domains(active => 1); + my @inactive = $ravada->list_domains(active => 0); + + my @user_domains = $ravada->list_domains(user => $id_user); + + my @user_active = $ravada->list_domains(user => $id_user, active => 1); + =cut sub list_domains { my $self = shift; + my %args = @_; + + my $active = delete $args{active}; + my $user = delete $args{user}; + + die "ERROR: Unknown arguments ".join(",",sort keys %args) + if keys %args; + my @domains; for my $vm ($self->list_vms) { for my $domain ($vm->list_domains) { + next if defined $active && + ( $domain->is_active && !$active + || !$domain->is_active && $active ); + + next if $user && $domain->id_owner != $user->id; + push @domains,($domain); } } From 7cb86f9a6c2bee4a3c230545ea929b9ce277f0fe Mon Sep 17 00:00:00 2001 From: frankiejol Date: Mon, 24 Jul 2017 11:35:09 +0200 Subject: [PATCH 23/52] [#315] enforce active domains by user limit --- lib/Ravada.pm | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index ef22b1902..26a708db7 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -1865,6 +1865,50 @@ sub import_domain { return $vm->import_domain($name, $user); } +=head enforce_limits + +Check no user has passed the limits and take action. + +Some limits: + +- More than 1 domain running at a time ( older get shut down ) + + +=cut + +sub enforce_limits { + _enforce_limits_active(@_); +} + +sub _enforce_limits_active { + my $self = shift; + my %args = @_; + + my $timeout = (delete $args{timeout} or 10); + + confess "ERROR: Unknown arguments ".join(",",sort keys %args) + if keys %args; + + my %domains; + for my $domain ($self->list_domains( active => 1 )) { + push @{$domains{$domain->id_owner}},$domain; + } + for my $id_user(keys %domains) { + next if scalar @{$domains{$id_user}}<2; + + my @domains_user = sort { $a->start_time <=> $b->start_time } + @{$domains{$id_user}}; + +# my @list = map { $_->name => $_->start_time } @domains_user; + my $last = pop @domains_user; + for my $domain (@domains_user) { + warn "requesting shut down ".$domain->name; + #TODO check the domain shutdown has been already requested + $domain->shutdown(timeout => $timeout, user => $USER_DAEMON ); + } + } +} + =head2 version Returns the version of the module From 6ebb4d7ad41e64b9bfe5c31f409948d8b904f235 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 24 Jul 2017 16:07:44 +0200 Subject: [PATCH 24/52] [#315] list void domains properly --- lib/Ravada/VM/Void.pm | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/lib/Ravada/VM/Void.pm b/lib/Ravada/VM/Void.pm index 8643aff81..96fba2829 100644 --- a/lib/Ravada/VM/Void.pm +++ b/lib/Ravada/VM/Void.pm @@ -75,28 +75,38 @@ sub dir_img { } sub list_domains { + my $self = shift; + opendir my $ls,$Ravada::Domain::Void::DIR_TMP or return; - my %domain; + my @list; while (my $file = readdir $ls ) { next if $file !~ /\.yml$/; $file =~ s/\.\w+//; $file =~ s/(.*)\.qcow.*$/$1/; next if $file !~ /\w/; - $domain{$file}++; + + my $domain = Ravada::Domain::Void->new( + domain => $file + , _vm => $self + ); + eval { $domain->id }; + next if $@ && $@ =~ /No db info/i; + die $@ if $@; + push @list,($domain); } closedir $ls; - return keys %domain; + return @list; } sub search_domain { my $self = shift; my $name = shift or confess "ERROR: Missing name"; - for my $name_vm ( $self->list_domains ) { - next if $name_vm ne $name; + for my $domain ( $self->list_domains ) { + next if $domain->name ne $name; my $domain = Ravada::Domain::Void->new( domain => $name From b265103c1dbbf233835eb350cfb89f72e02b2000 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 24 Jul 2017 16:24:08 +0200 Subject: [PATCH 25/52] [#315] enforce limits from backend --- bin/rvd_back.pl | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/rvd_back.pl b/bin/rvd_back.pl index f4e6ca00e..94278790c 100755 --- a/bin/rvd_back.pl +++ b/bin/rvd_back.pl @@ -92,6 +92,7 @@ sub do_start { my $t0 = time; $ravada->process_requests(); $ravada->process_long_requests(0,$NOFORK) if $NOFORK; + $ravada->enforce_limits(); sleep 1 if time - $t0 <1; } } From 9603c4023de2d015e2774c674a0127a357c29d90 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 24 Jul 2017 16:25:19 +0200 Subject: [PATCH 26/52] [#315] set and get start_time --- lib/Ravada/Domain.pm | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index 0f8291189..827847238 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -450,6 +450,18 @@ sub is_known { return $self->_select_domain_db(name => $self->name); } +=head2 start_time + +Returns the last time (epoch format in seconds) the +domain was started. + +=cut + +sub start_time { + my $self = shift; + return $self->_data('start_time'); +} + sub _select_domain_db { my $self = shift; my %args = @_; @@ -1082,6 +1094,13 @@ sub _post_resume { sub _post_start { my $self = shift; + my $sth = $$CONNECTOR->dbh->prepare( + "UPDATE domains set start_time=? " + ." WHERE id=?" + ); + $sth->execute(time, $self->id); + $sth->finish; + $self->_add_iptable(@_); } From 603375fc713a2f681f02108e44814cbd08b14559 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 24 Jul 2017 16:51:11 +0200 Subject: [PATCH 27/52] [#315] ignore forced shutdown if already down --- lib/Ravada/Domain.pm | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index 827847238..fcd3c9999 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -146,10 +146,9 @@ before 'resume' => \&_allow_manage; before 'shutdown' => \&_pre_shutdown; after 'shutdown' => \&_post_shutdown; -after 'shutdown_now' => \&_post_shutdown_now; -before 'force_shutdown' => \&_pre_shutdown_now; -after 'force_shutdown' => \&_post_shutdown_now; +around 'shutdown_now' => \&_around_shutdown_now; +around 'force_shutdown' => \&_around_shutdown_now; before 'remove_base' => \&_pre_remove_base; after 'remove_base' => \&_post_remove_base; @@ -970,14 +969,22 @@ sub _post_pause { sub _pre_shutdown { my $self = shift; + my %arg = @_; + + my $user = delete $arg{user}; + delete $arg{timeout}; + + confess "Unknown args ".join(",",sort keys %arg) + if keys %arg; - $self->_allow_manage_args(@_); + + $self->_allow_manage_args(user => $user); $self->_pre_shutdown_domain(); if ($self->is_paused) { my %args = @_; - $self->resume(user => $args{user}); + $self->resume(user => $user); } } @@ -1005,15 +1012,14 @@ sub _post_shutdown { } } -sub _pre_shutdown_now { - my $self = shift; - return if !$self->is_active; -} - -sub _post_shutdown_now { +sub _around_shutdown_now { + my $orig = shift; my $self = shift; my $user = shift; + if ($self->is_active) { + $self->$orig($user); + } $self->_post_shutdown(user => $user); } From 3bb8df375b09cc3c25a882272f9f77bdc012e634 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 24 Jul 2017 17:05:48 +0200 Subject: [PATCH 28/52] [#315] check no domain can be shut down from front --- t/front/30_show_domain.t | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/t/front/30_show_domain.t b/t/front/30_show_domain.t index 7188fafc0..b3377bfb3 100644 --- a/t/front/30_show_domain.t +++ b/t/front/30_show_domain.t @@ -115,7 +115,7 @@ sub test_shutdown_domain { ok($domain_f->is_active); - eval { $domain_f->shutdown( force => 1) }; + eval { $domain_f->shutdown( force => 1, user => user_admin) }; ok($@,"[$vm_name] Shutdown should be denied from front "); ok($domain_f->is_active,"[$vm_name] Domain should be active"); @@ -126,8 +126,8 @@ sub test_shutdown_domain { ok($@,"[$vm_name] Shutdown should be denied from front "); } - eval { $domain_b->shutdown(user => $USER,force => 1) }; - ok(!$@,$@); + eval { $domain_b->force_shutdown($USER) }; + is($@,''); ok(!$domain_f->is_active);# && !$domain_f->is_active); From d3d71fc915f069425c5e14e39abfa42a95d23c92 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 24 Jul 2017 17:17:47 +0200 Subject: [PATCH 29/52] [#315] removed debug --- lib/Ravada.pm | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index 26a708db7..152c5be8c 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -1902,7 +1902,6 @@ sub _enforce_limits_active { # my @list = map { $_->name => $_->start_time } @domains_user; my $last = pop @domains_user; for my $domain (@domains_user) { - warn "requesting shut down ".$domain->name; #TODO check the domain shutdown has been already requested $domain->shutdown(timeout => $timeout, user => $USER_DAEMON ); } From f166b31dea4cb5bb23a70f679bb7191aa0fd64cf Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 24 Jul 2017 17:24:08 +0200 Subject: [PATCH 30/52] [#315] doc enforce_limits --- lib/Ravada.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index 152c5be8c..080f269a4 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -1865,7 +1865,7 @@ sub import_domain { return $vm->import_domain($name, $user); } -=head enforce_limits +=head2 enforce_limits Check no user has passed the limits and take action. From 002a9540c8862d8410d566e3394838337ab91779 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Tue, 25 Jul 2017 12:41:59 +0200 Subject: [PATCH 31/52] [#415] skip if down requested, try to hybernate --- lib/Ravada.pm | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index 080f269a4..78bc59b97 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -1313,9 +1313,9 @@ sub _execute { eval { $sub->($self,$request) }; my $err = ($@ or ''); - $request->error($err) if $err; $request->status('done') if $request->status() ne 'done' && $request->status !~ /retry/; + $request->error($err) if $err; return $err; } @@ -1679,16 +1679,16 @@ sub _cmd_shutdown { my $request = shift; my $uid = $request->args('uid'); - my $name = $request->args('name'); + my $id_domain = $request->args('id_domain'); my $timeout = ($request->args('timeout') or 60); my $domain; - $domain = $self->search_domain($name); - die "Unknown domain '$name'\n" if !$domain; + $domain = $self->search_domain_by_id($id_domain); + die "Unknown domain '$id_domain'\n" if !$domain; my $user = Ravada::Auth::SQL->search_by_id( $uid); - $domain->shutdown(timeout => $timeout, name => $name, user => $user + $domain->shutdown(timeout => $timeout, id_domain => $id_domain, user => $user , request => $request); } @@ -1698,11 +1698,11 @@ sub _cmd_force_shutdown { my $request = shift; my $uid = $request->args('uid'); - my $name = $request->args('name'); + my $id_domain = $request->args('id_domain'); my $domain; - $domain = $self->search_domain($name); - die "Unknown domain '$name'\n" if !$domain; + $domain = $self->search_domain_by_id($id_domain); + die "Unknown domain '$id_domain'\n" if !$domain; my $user = Ravada::Auth::SQL->search_by_id( $uid); @@ -1901,9 +1901,16 @@ sub _enforce_limits_active { # my @list = map { $_->name => $_->start_time } @domains_user; my $last = pop @domains_user; - for my $domain (@domains_user) { + DOMAIN: for my $domain (@domains_user) { #TODO check the domain shutdown has been already requested - $domain->shutdown(timeout => $timeout, user => $USER_DAEMON ); + for my $request ($domain->list_requests) { + next DOMAIN if $request->command =~ /shutdown/; + } + if ($domain->can_hybernate) { + $domain->hybernate($USER_DAEMON); + } else { + $domain->shutdown(timeout => $timeout, user => $USER_DAEMON ); + } } } } From 7aced9ab6f7839eb64d00d159508b25a2799a3d6 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Tue, 25 Jul 2017 12:42:32 +0200 Subject: [PATCH 32/52] [#315] test for duplicated requests --- t/vm/20_base.t | 58 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/t/vm/20_base.t b/t/vm/20_base.t index 8f4bb4e7c..22647b43d 100644 --- a/t/vm/20_base.t +++ b/t/vm/20_base.t @@ -359,11 +359,64 @@ sub test_domain_limit { $domain2->start($USER); rvd_back->enforce_limits(timeout => 2); sleep 2; - rvd_back->_process_requests_dont_fork(1); + rvd_back->_process_requests_dont_fork(); my @list = rvd_back->list_domains(user => $USER, active => 1); is(scalar @list,1) or die Dumper(\@list); is($list[0]->name, $domain2->name) if $list[0]; } + +sub test_domain_limit_already_requested { + my $vm_name = shift; + + for my $domain ( rvd_back->list_domains()) { + $domain->shutdown_now(user_admin); + } + my $domain = create_domain($vm_name, $USER); + ok($domain,"Expecting a new domain created") or return; + $domain->shutdown_now($USER) if $domain->is_active; + + is(rvd_back->list_domains(user => $USER, active => 1),0 + ,Dumper(rvd_back->list_domains())) or return; + + $domain->start($USER); + is($domain->is_active,1); + + ok($domain->start_time <= time,"Expecting start time <= ".time + ." got ".time); + + sleep 1; + is(rvd_back->list_domains(user => $USER, active => 1),1); + + my $domain2 = create_domain($vm_name, $USER); + $domain2->shutdown_now($USER) if $domain2->is_active; + is(rvd_back->list_domains(user => $USER, active => 1),1); + + $domain2->start($USER); + my @list_requests = $domain->list_requests; + is(scalar @list_requests,0,"Expecting 0 requests ".Dumper(\@list_requests)); + + rvd_back->enforce_limits(timeout => 2); + + if (!$domain->can_hybernate) { + @list_requests = $domain->list_requests; + is(scalar @list_requests,1,"Expecting 1 request ".Dumper(\@list_requests)); + rvd_back->enforce_limits(timeout => 2); + + @list_requests = $domain->list_requests; + is(scalar @list_requests,1,"Expecting 1 request ".Dumper(\@list_requests)); + + sleep 3; + + rvd_back->_process_requests_dont_fork(); + } + @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, active => 1); + is(scalar @list,1) or die Dumper(\@list); + is($list[0]->name, $domain2->name) if $list[0]; +} + #######################################################################33 @@ -395,6 +448,8 @@ for my $vm_name (reverse sort @VMS) { use_ok($CLASS); + test_domain_limit_already_requested($vm_name); + my $domain = test_create_domain($vm_name); test_prepare_base($vm_name, $domain); test_prepare_base_active($vm_name); @@ -402,6 +457,7 @@ for my $vm_name (reverse sort @VMS) { test_dont_remove_base_cloned($vm_name); test_domain_limit($vm_name); + } } From e650d48f03861f379c03e3fa5b2d6deacd01444d Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Tue, 25 Jul 2017 13:17:42 +0200 Subject: [PATCH 33/52] [#315] shutdown called by id_domain instead name --- lib/Ravada.pm | 2 +- lib/Ravada/Domain.pm | 4 ++-- lib/Ravada/Domain/KVM.pm | 2 +- lib/Ravada/Domain/Void.pm | 1 - lib/Ravada/Request.pm | 11 +++++++++-- rvd_front.pl | 6 +++--- t/35_request_start.t | 4 +++- 7 files changed, 19 insertions(+), 11 deletions(-) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index 78bc59b97..9ead16051 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -1688,7 +1688,7 @@ sub _cmd_shutdown { my $user = Ravada::Auth::SQL->search_by_id( $uid); - $domain->shutdown(timeout => $timeout, id_domain => $id_domain, user => $user + $domain->shutdown(timeout => $timeout, user => $user , request => $request); } diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index fcd3c9999..592f67415 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -973,6 +973,7 @@ sub _pre_shutdown { my $user = delete $arg{user}; delete $arg{timeout}; + delete $arg{request}; confess "Unknown args ".join(",",sort keys %arg) if keys %arg; @@ -983,7 +984,6 @@ sub _pre_shutdown { $self->_pre_shutdown_domain(); if ($self->is_paused) { - my %args = @_; $self->resume(user => $user); } } @@ -1005,7 +1005,7 @@ sub _post_shutdown { } my $req = Ravada::Request->force_shutdown_domain( - name => $self->name + id_domain => $self->id , uid => $arg{user}->id , at => time+$timeout ); diff --git a/lib/Ravada/Domain/KVM.pm b/lib/Ravada/Domain/KVM.pm index 4f95c5c92..c2029a7be 100644 --- a/lib/Ravada/Domain/KVM.pm +++ b/lib/Ravada/Domain/KVM.pm @@ -576,7 +576,7 @@ Shuts down uncleanly the domain sub force_shutdown{ my $self = shift; - $self->_do_force_shutdown(); + return $self->_do_force_shutdown() if $self->is_active; } sub _do_force_shutdown { diff --git a/lib/Ravada/Domain/Void.pm b/lib/Ravada/Domain/Void.pm index fef8b305f..1b4d6b047 100644 --- a/lib/Ravada/Domain/Void.pm +++ b/lib/Ravada/Domain/Void.pm @@ -67,7 +67,6 @@ sub display { sub is_active { my $self = shift; - return ($self->_value('is_active') or 0); } diff --git a/lib/Ravada/Request.pm b/lib/Ravada/Request.pm index 17d7c89c6..26ef5d77b 100644 --- a/lib/Ravada/Request.pm +++ b/lib/Ravada/Request.pm @@ -52,8 +52,8 @@ our %VALID_ARG = ( ,pause_domain => $args_manage ,resume_domain => {%$args_manage, remote_ip => 1 } ,remove_domain => $args_manage - ,shutdown_domain => { name => 1, uid => 1, timeout => 2 } - ,force_shutdown_domain => { name => 1, uid => 1, at => 2 } + ,shutdown_domain => { id_domain => 1, uid => 1, timeout => 2 } + ,force_shutdown_domain => { id_domain => 1, uid => 1, at => 2 } ,screenshot_domain => { id_domain => 1, filename => 2 } ,start_domain => {%$args_manage, remote_ip => 1 } ,rename_domain => { uid => 1, name => 1, id_domain => 1} @@ -418,6 +418,13 @@ sub _new_request { $args{args}->{uid} = $args{args}->{id_owner} if !exists $args{args}->{uid}; $args{at_time} = $args{args}->{at} if exists $args{args}->{at}; + my $id_domain_args = $args{args}->{id_domain}; + + if ($id_domain_args) { + confess "ERROR: Different id_domain: ".Dumper(\%args) + if $args{id_domain} && $args{id_domain} ne $id_domain_args; + $args{id_domain} = $id_domain_args; + } $args{args} = encode_json($args{args}); } _init_connector() if !$CONNECTOR || !$$CONNECTOR; diff --git a/rvd_front.pl b/rvd_front.pl index c795a6133..85735f173 100644 --- a/rvd_front.pl +++ b/rvd_front.pl @@ -1256,7 +1256,7 @@ sub manage_machine { return access_denied($c) if $domain->id_owner != $USER->id && !$USER->is_admin; - Ravada::Request->shutdown_domain(name => $domain->name, uid => $USER->id) if $c->param('shutdown'); + Ravada::Request->shutdown_domain(id_domain => $domain->id, uid => $USER->id) if $c->param('shutdown'); Ravada::Request->start_domain( uid => $USER->id ,name => $domain->name , remote_ip => _remote_ip($c) @@ -1281,7 +1281,7 @@ sub settings_machine { $c->stash(domain => $domain); $c->stash(USER => $USER); - my $req = Ravada::Request->shutdown_domain(name => $domain->name, uid => $USER->id) + my $req = Ravada::Request->shutdown_domain(id_domain => $domain->id, uid => $USER->id) if $c->param('shutdown') && $domain->is_active; $req = Ravada::Request->start_domain( @@ -1377,7 +1377,7 @@ sub shutdown_machine { return login($c) if !_logged_in($c); my ($domain, $type) = _search_requested_machine($c); - my $req = Ravada::Request->shutdown_domain(name => $domain->name, uid => $USER->id); + my $req = Ravada::Request->shutdown_domain(id_domain => $domain->id, uid => $USER->id); return $c->redirect_to('/machines') if $type eq 'html'; return $c->render(json => { req => $req->id }); diff --git a/t/35_request_start.t b/t/35_request_start.t index f82d7c6fe..c64a26903 100644 --- a/t/35_request_start.t +++ b/t/35_request_start.t @@ -111,8 +111,10 @@ sub test_start { ok($req2->status eq 'done',"Expecting request status 'done' , got " .$req2->status); + my $id_domain; { my $domain = $RAVADA->search_domain($name); + $id_domain = $domain->id; $domain->start($USER) if !$domain->is_active(); ok($domain->is_active); @@ -127,7 +129,7 @@ sub test_start { # # stop - my $req3 = Ravada::Request->force_shutdown_domain(name => $name, uid => $USER->id); + my $req3 = Ravada::Request->force_shutdown_domain(id_domain => $id_domain, uid => $USER->id); $RAVADA->process_requests(); wait_request($req3); ok($req3->status eq 'done',"[$vm_name] expecting request done , got " From c422d13e596d77b229abccfda157594c7056d4c8 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Tue, 25 Jul 2017 13:36:48 +0200 Subject: [PATCH 34/52] Force shutdown on tests --- t/vm/70_clone.t | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/t/vm/70_clone.t b/t/vm/70_clone.t index 3f0bbc25b..bc91ab8e2 100644 --- a/t/vm/70_clone.t +++ b/t/vm/70_clone.t @@ -70,8 +70,9 @@ sub test_clone { my $name_clone = new_domain_name(); # diag("[$vm_name] Cloning from base ".$base->name." to $name_clone"); eval { $clone1 = $base->clone(name => $name_clone, user => $USER) }; - ok(!$@,"Expecting error='', got='".($@ or '')."'"); - ok($clone1,"Expecting new cloned domain from ".$base->name) or last; + ok(!$@,"Expecting error='', got='".($@ or '')) + or die Dumper($base->list_requests); + ok($clone1,"Expecting new cloned domain from ".$base->name) or return; is($clone1->description,undef); $clone1->shutdown_now($USER) if $clone1->is_active(); @@ -91,7 +92,7 @@ sub test_clone { sub test_mess_with_bases { my ($vm_name, $base, $clones) = @_; for my $clone (@$clones) { - $clone->shutdown(user => $USER, timeout => 1) if $clone->is_active; + $clone->force_shutdown($USER) if $clone->is_active; ok($clone->id_base,"Expecting clone has id_base , got " .($clone->id_base or '')); $clone->prepare_base($USER); @@ -105,14 +106,13 @@ sub test_mess_with_bases { ok(!$@,"Expecting error: '' , got: ".($@ or '')) or exit; ok($clone->is_active); - $clone->shutdown(user => $USER, timeout => 1) if $clone->is_active; + $clone->force_shutdown($USER) if $clone->is_active; $clone->remove_base($USER); eval { $clone->start($USER); }; ok(!$@,"[$vm_name] Expecting error: '' , got '".($@ or '')."'"); ok($clone->is_active); - $clone->shutdown(user => $USER, timeout => 1); - + $clone->force_shutdown($USER); } } From f00edb81652f2962d7cb54b979912a2bd8b49e7e Mon Sep 17 00:00:00 2001 From: frankiejol Date: Mon, 24 Jul 2017 11:00:29 +0200 Subject: [PATCH 35/52] [#315] test active domains limit --- t/vm/20_base.t | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/t/vm/20_base.t b/t/vm/20_base.t index 81c38ab12..4edb5638f 100644 --- a/t/vm/20_base.t +++ b/t/vm/20_base.t @@ -419,6 +419,40 @@ sub test_private_base { ok(!$clone2,"Expecting no clone"); } +sub test_domain_limit { + my $vm_name = shift; + + for my $domain ( rvd_back->list_domains()) { + $domain->shutdown_now(user_admin); + } + my $domain = create_domain($vm_name, $USER); + ok($domain,"Expecting a new domain created") or exit; + $domain->shutdown_now($USER) if $domain->is_active; + + is(rvd_back->list_domains(user => $USER, active => 1),0 + ,Dumper(rvd_back->list_domains())) or exit; + + $domain->start($USER); + is($domain->is_active,1); + + ok($domain->start_time <= time,"Expecting start time <= ".time + ." got ".time); + + sleep 1; + is(rvd_back->list_domains(user => $USER, active => 1),1); + + my $domain2 = create_domain($vm_name, $USER); + $domain2->shutdown_now($USER) if $domain2->is_active; + is(rvd_back->list_domains(user => $USER, active => 1),1); + + $domain2->start($USER); + rvd_back->enforce_limits(timeout => 2); + sleep 2; + rvd_back->_process_requests_dont_fork(1); + my @list = rvd_back->list_domains(user => $USER, active => 1); + is(scalar @list,1) or die Dumper(\@list); + is($list[0]->name, $domain2->name) if $list[0]; +} #######################################################################33 @@ -459,6 +493,7 @@ for my $vm_name ('Void','KVM') { test_private_base($vm_name); test_spinned_off_base($vm_name); + test_domain_limit($vm_name); } } From 1d7b04860b4b78ea4ba18b24b33694f97ea51680 Mon Sep 17 00:00:00 2001 From: frankiejol Date: Mon, 24 Jul 2017 11:01:30 +0200 Subject: [PATCH 36/52] [#315] store start time In the future we should add an accounting table to store start , shutdown, etc. Or we can read this information from the requests table. --- sql/mysql/domains.sql | 1 + sql/sqlite/domains.sql | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/sql/mysql/domains.sql b/sql/mysql/domains.sql index b475b96d0..8387e6893 100644 --- a/sql/mysql/domains.sql +++ b/sql/mysql/domains.sql @@ -14,6 +14,7 @@ CREATE TABLE `domains` ( `vm` char(120) NOT NULL, `spice_password` char(20) DEFAULT NULL, `description` text, + `start_time` int not null default 0, PRIMARY KEY (`id`), UNIQUE KEY `id_base` (`id_base`,`name`), UNIQUE KEY `name` (`name`) diff --git a/sql/sqlite/domains.sql b/sql/sqlite/domains.sql index 09318ce40..ebd194c32 100644 --- a/sql/sqlite/domains.sql +++ b/sql/sqlite/domains.sql @@ -13,7 +13,8 @@ CREATE TABLE `domains` ( , `id_owner` integer , `vm` char(120) NOT NULL , `spice_password` char(20) DEFAULT NULL -, `description` text DEFAULT NULL +, `description` text +, `start_time` integer not null default 0 , UNIQUE (`id_base`,`name`) , UNIQUE (`name`) ); From 53571330016b90331f6937fe31a0d4ac5bc67be5 Mon Sep 17 00:00:00 2001 From: frankiejol Date: Mon, 24 Jul 2017 11:04:37 +0200 Subject: [PATCH 37/52] [#315] add start time field for upgrades --- lib/Ravada.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index fa2ffb63e..c0edac827 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -673,6 +673,7 @@ sub _upgrade_tables { $self->_upgrade_table('domains','spice_password','varchar(20) DEFAULT NULL'); $self->_upgrade_table('domains','description','text DEFAULT NULL'); $self->_upgrade_table('domains','run_timeout','int DEFAULT NULL'); + $self->_upgrade_table('domains','start_time','int DEFAULT 0'); } From caf695c15545dab1d054b5abaa6a091d8ac9df55 Mon Sep 17 00:00:00 2001 From: frankiejol Date: Mon, 24 Jul 2017 11:11:58 +0200 Subject: [PATCH 38/52] [#315] filter domain list --- lib/Ravada.pm | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index c0edac827..765a9b4b5 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -1013,13 +1013,36 @@ List all created domains my @list = $ravada->list_domains(); +This list can be filtered: + + my @active = $ravada->list_domains(active => 1); + my @inactive = $ravada->list_domains(active => 0); + + my @user_domains = $ravada->list_domains(user => $id_user); + + my @user_active = $ravada->list_domains(user => $id_user, active => 1); + =cut sub list_domains { my $self = shift; + my %args = @_; + + my $active = delete $args{active}; + my $user = delete $args{user}; + + die "ERROR: Unknown arguments ".join(",",sort keys %args) + if keys %args; + my @domains; for my $vm ($self->list_vms) { for my $domain ($vm->list_domains) { + next if defined $active && + ( $domain->is_active && !$active + || !$domain->is_active && $active ); + + next if $user && $domain->id_owner != $user->id; + push @domains,($domain); } } From 6129ccfb6a086665f62e895d8267e7e705320601 Mon Sep 17 00:00:00 2001 From: frankiejol Date: Mon, 24 Jul 2017 11:35:09 +0200 Subject: [PATCH 39/52] [#315] enforce active domains by user limit --- lib/Ravada.pm | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index 765a9b4b5..acd869975 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -1977,6 +1977,50 @@ sub import_domain { return $vm->import_domain($name, $user); } +=head enforce_limits + +Check no user has passed the limits and take action. + +Some limits: + +- More than 1 domain running at a time ( older get shut down ) + + +=cut + +sub enforce_limits { + _enforce_limits_active(@_); +} + +sub _enforce_limits_active { + my $self = shift; + my %args = @_; + + my $timeout = (delete $args{timeout} or 10); + + confess "ERROR: Unknown arguments ".join(",",sort keys %args) + if keys %args; + + my %domains; + for my $domain ($self->list_domains( active => 1 )) { + push @{$domains{$domain->id_owner}},$domain; + } + for my $id_user(keys %domains) { + next if scalar @{$domains{$id_user}}<2; + + my @domains_user = sort { $a->start_time <=> $b->start_time } + @{$domains{$id_user}}; + +# my @list = map { $_->name => $_->start_time } @domains_user; + my $last = pop @domains_user; + for my $domain (@domains_user) { + warn "requesting shut down ".$domain->name; + #TODO check the domain shutdown has been already requested + $domain->shutdown(timeout => $timeout, user => $USER_DAEMON ); + } + } +} + =head2 version Returns the version of the module From b34927d63830b6da5ae04aacb7e74207bc3e71d8 Mon Sep 17 00:00:00 2001 From: frankiejol Date: Mon, 24 Jul 2017 11:08:48 +0200 Subject: [PATCH 40/52] [#315] add Void Virtual Manager when testing --- lib/Ravada.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index acd869975..17545e714 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -839,6 +839,7 @@ sub _create_vm { push @vms,($vm) if $vm; } die "No VMs found: $err\n" if $self->warn_error && !@vms; + return \@vms; } From 092f45b1b64079d9242064ce815bf958832064fb Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 24 Jul 2017 16:24:08 +0200 Subject: [PATCH 41/52] [#315] enforce limits from backend --- bin/rvd_back.pl | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/rvd_back.pl b/bin/rvd_back.pl index 39767415e..01012b07d 100755 --- a/bin/rvd_back.pl +++ b/bin/rvd_back.pl @@ -137,6 +137,7 @@ sub do_start { my $t0 = time; $ravada->process_requests(); $ravada->process_long_requests(0,$NOFORK) if $NOFORK; + $ravada->enforce_limits(); sleep 1 if time - $t0 <1; } } From 618e3b62520936dfe65acb3c5572bab9e0168219 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 24 Jul 2017 16:25:19 +0200 Subject: [PATCH 42/52] [#315] set and get start_time --- lib/Ravada/Domain.pm | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index 199902b12..42e01a324 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -503,6 +503,18 @@ sub is_known { return $self->_select_domain_db(name => $self->name); } +=head2 start_time + +Returns the last time (epoch format in seconds) the +domain was started. + +=cut + +sub start_time { + my $self = shift; + return $self->_data('start_time'); +} + sub _select_domain_db { my $self = shift; my %args = @_; @@ -1153,11 +1165,13 @@ sub _post_start { %arg = @_; } - if (scalar @_ % 2) { - $arg{user} = $_[0]; - } else { - %arg = @_; - } + my $sth = $$CONNECTOR->dbh->prepare( + "UPDATE domains set start_time=? " + ." WHERE id=?" + ); + $sth->execute(time, $self->id); + $sth->finish; + $self->_add_iptable(@_); if ($self->run_timeout) { From 033f8c8cb866d1be47d870a7be834e0246081923 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 24 Jul 2017 16:51:11 +0200 Subject: [PATCH 43/52] [#315] ignore forced shutdown if already down --- lib/Ravada/Domain.pm | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index 42e01a324..b4832aad0 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -150,10 +150,9 @@ before 'resume' => \&_allow_manage; before 'shutdown' => \&_pre_shutdown; after 'shutdown' => \&_post_shutdown; -after 'shutdown_now' => \&_post_shutdown_now; -before 'force_shutdown' => \&_pre_shutdown_now; -after 'force_shutdown' => \&_post_shutdown_now; +around 'shutdown_now' => \&_around_shutdown_now; +around 'force_shutdown' => \&_around_shutdown_now; before 'remove_base' => \&_pre_remove_base; after 'remove_base' => \&_post_remove_base; @@ -1024,14 +1023,22 @@ sub _post_pause { sub _pre_shutdown { my $self = shift; + my %arg = @_; + + my $user = delete $arg{user}; + delete $arg{timeout}; + + confess "Unknown args ".join(",",sort keys %arg) + if keys %arg; $self->_allow_shutdown(@_); + $self->_allow_manage_args(user => $user); $self->_pre_shutdown_domain(); if ($self->is_paused) { my %args = @_; - $self->resume(user => $args{user}); + $self->resume(user => $user); } } @@ -1059,15 +1066,14 @@ sub _post_shutdown { } } -sub _pre_shutdown_now { - my $self = shift; - return if !$self->is_active; -} - -sub _post_shutdown_now { +sub _around_shutdown_now { + my $orig = shift; my $self = shift; my $user = shift; + if ($self->is_active) { + $self->$orig($user); + } $self->_post_shutdown(user => $user); } From 10f22d62b90e6e0ada55673d8e82fa912c333885 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 24 Jul 2017 17:05:48 +0200 Subject: [PATCH 44/52] [#315] check no domain can be shut down from front --- t/front/30_show_domain.t | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/t/front/30_show_domain.t b/t/front/30_show_domain.t index 7188fafc0..b3377bfb3 100644 --- a/t/front/30_show_domain.t +++ b/t/front/30_show_domain.t @@ -115,7 +115,7 @@ sub test_shutdown_domain { ok($domain_f->is_active); - eval { $domain_f->shutdown( force => 1) }; + eval { $domain_f->shutdown( force => 1, user => user_admin) }; ok($@,"[$vm_name] Shutdown should be denied from front "); ok($domain_f->is_active,"[$vm_name] Domain should be active"); @@ -126,8 +126,8 @@ sub test_shutdown_domain { ok($@,"[$vm_name] Shutdown should be denied from front "); } - eval { $domain_b->shutdown(user => $USER,force => 1) }; - ok(!$@,$@); + eval { $domain_b->force_shutdown($USER) }; + is($@,''); ok(!$domain_f->is_active);# && !$domain_f->is_active); From 3912a6f2eb3ba4ef19753cde4f2ee90a08ee5edb Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 24 Jul 2017 17:17:47 +0200 Subject: [PATCH 45/52] [#315] removed debug --- lib/Ravada.pm | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index 17545e714..4f93f08e4 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -2015,7 +2015,6 @@ sub _enforce_limits_active { # my @list = map { $_->name => $_->start_time } @domains_user; my $last = pop @domains_user; for my $domain (@domains_user) { - warn "requesting shut down ".$domain->name; #TODO check the domain shutdown has been already requested $domain->shutdown(timeout => $timeout, user => $USER_DAEMON ); } From 7dbdf7554e8cfc23c6b70df0cb6cfa0fadf76d3a Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Mon, 24 Jul 2017 17:24:08 +0200 Subject: [PATCH 46/52] [#315] doc enforce_limits --- lib/Ravada.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index 4f93f08e4..d7edb1605 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -1978,7 +1978,7 @@ sub import_domain { return $vm->import_domain($name, $user); } -=head enforce_limits +=head2 enforce_limits Check no user has passed the limits and take action. From ed1679d26106c84b51acc7d43bcfd03a12efb0c5 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Tue, 25 Jul 2017 12:41:59 +0200 Subject: [PATCH 47/52] [#415] skip if down requested, try to hybernate --- lib/Ravada.pm | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index d7edb1605..2423e313e 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -1415,9 +1415,9 @@ sub _execute { eval { $sub->($self,$request) }; my $err = ($@ or ''); - $request->error($err) if $err; $request->status('done') if $request->status() ne 'done' && $request->status !~ /retry/; + $request->error($err) if $err; return $err; } @@ -1801,7 +1801,7 @@ sub _cmd_shutdown { my $user = Ravada::Auth::SQL->search_by_id( $uid); - $domain->shutdown(timeout => $timeout, name => $name, user => $user + $domain->shutdown(timeout => $timeout, id_domain => $id_domain, user => $user , request => $request); } @@ -1811,11 +1811,11 @@ sub _cmd_force_shutdown { my $request = shift; my $uid = $request->args('uid'); - my $name = $request->args('name'); + my $id_domain = $request->args('id_domain'); my $domain; - $domain = $self->search_domain($name); - die "Unknown domain '$name'\n" if !$domain; + $domain = $self->search_domain_by_id($id_domain); + die "Unknown domain '$id_domain'\n" if !$domain; my $user = Ravada::Auth::SQL->search_by_id( $uid); @@ -2014,9 +2014,16 @@ sub _enforce_limits_active { # my @list = map { $_->name => $_->start_time } @domains_user; my $last = pop @domains_user; - for my $domain (@domains_user) { + DOMAIN: for my $domain (@domains_user) { #TODO check the domain shutdown has been already requested - $domain->shutdown(timeout => $timeout, user => $USER_DAEMON ); + for my $request ($domain->list_requests) { + next DOMAIN if $request->command =~ /shutdown/; + } + if ($domain->can_hybernate) { + $domain->hybernate($USER_DAEMON); + } else { + $domain->shutdown(timeout => $timeout, user => $USER_DAEMON ); + } } } } From a42dc1d01bad3f90ce57af2793399ad6a3c38989 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Tue, 25 Jul 2017 12:42:32 +0200 Subject: [PATCH 48/52] [#315] test for duplicated requests --- t/vm/20_base.t | 58 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/t/vm/20_base.t b/t/vm/20_base.t index 4edb5638f..5a080e16b 100644 --- a/t/vm/20_base.t +++ b/t/vm/20_base.t @@ -448,11 +448,64 @@ sub test_domain_limit { $domain2->start($USER); rvd_back->enforce_limits(timeout => 2); sleep 2; - rvd_back->_process_requests_dont_fork(1); + rvd_back->_process_requests_dont_fork(); my @list = rvd_back->list_domains(user => $USER, active => 1); is(scalar @list,1) or die Dumper(\@list); is($list[0]->name, $domain2->name) if $list[0]; } + +sub test_domain_limit_already_requested { + my $vm_name = shift; + + for my $domain ( rvd_back->list_domains()) { + $domain->shutdown_now(user_admin); + } + my $domain = create_domain($vm_name, $USER); + ok($domain,"Expecting a new domain created") or return; + $domain->shutdown_now($USER) if $domain->is_active; + + is(rvd_back->list_domains(user => $USER, active => 1),0 + ,Dumper(rvd_back->list_domains())) or return; + + $domain->start($USER); + is($domain->is_active,1); + + ok($domain->start_time <= time,"Expecting start time <= ".time + ." got ".time); + + sleep 1; + is(rvd_back->list_domains(user => $USER, active => 1),1); + + my $domain2 = create_domain($vm_name, $USER); + $domain2->shutdown_now($USER) if $domain2->is_active; + is(rvd_back->list_domains(user => $USER, active => 1),1); + + $domain2->start($USER); + my @list_requests = $domain->list_requests; + is(scalar @list_requests,0,"Expecting 0 requests ".Dumper(\@list_requests)); + + rvd_back->enforce_limits(timeout => 2); + + if (!$domain->can_hybernate) { + @list_requests = $domain->list_requests; + is(scalar @list_requests,1,"Expecting 1 request ".Dumper(\@list_requests)); + rvd_back->enforce_limits(timeout => 2); + + @list_requests = $domain->list_requests; + is(scalar @list_requests,1,"Expecting 1 request ".Dumper(\@list_requests)); + + sleep 3; + + rvd_back->_process_requests_dont_fork(); + } + @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, active => 1); + is(scalar @list,1) or die Dumper(\@list); + is($list[0]->name, $domain2->name) if $list[0]; +} + #######################################################################33 @@ -484,6 +537,8 @@ for my $vm_name ('Void','KVM') { use_ok($CLASS); + test_domain_limit_already_requested($vm_name); + my $domain = test_create_domain($vm_name); test_prepare_base($vm_name, $domain); test_prepare_base_active($vm_name); @@ -494,6 +549,7 @@ for my $vm_name ('Void','KVM') { test_spinned_off_base($vm_name); test_domain_limit($vm_name); + } } From 4d456bf425a94bcee810527b75c814786e55959d Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Tue, 25 Jul 2017 13:17:42 +0200 Subject: [PATCH 49/52] [#315] shutdown called by id_domain instead name --- lib/Ravada.pm | 2 +- lib/Ravada/Domain.pm | 4 ++-- lib/Ravada/Domain/KVM.pm | 2 +- lib/Ravada/Domain/Void.pm | 1 - lib/Ravada/Request.pm | 9 +++++++-- rvd_front.pl | 6 +++--- t/35_request_start.t | 4 +++- 7 files changed, 17 insertions(+), 11 deletions(-) diff --git a/lib/Ravada.pm b/lib/Ravada.pm index 2423e313e..6a71772eb 100644 --- a/lib/Ravada.pm +++ b/lib/Ravada.pm @@ -1801,7 +1801,7 @@ sub _cmd_shutdown { my $user = Ravada::Auth::SQL->search_by_id( $uid); - $domain->shutdown(timeout => $timeout, id_domain => $id_domain, user => $user + $domain->shutdown(timeout => $timeout, user => $user , request => $request); } diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index b4832aad0..7265f1f91 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -1027,6 +1027,7 @@ sub _pre_shutdown { my $user = delete $arg{user}; delete $arg{timeout}; + delete $arg{request}; confess "Unknown args ".join(",",sort keys %arg) if keys %arg; @@ -1037,7 +1038,6 @@ sub _pre_shutdown { $self->_pre_shutdown_domain(); if ($self->is_paused) { - my %args = @_; $self->resume(user => $user); } } @@ -1059,7 +1059,7 @@ sub _post_shutdown { } my $req = Ravada::Request->force_shutdown_domain( - name => $self->name + id_domain => $self->id , uid => $arg{user}->id , at => time+$timeout ); diff --git a/lib/Ravada/Domain/KVM.pm b/lib/Ravada/Domain/KVM.pm index 1a88bb3fc..415d0ba89 100644 --- a/lib/Ravada/Domain/KVM.pm +++ b/lib/Ravada/Domain/KVM.pm @@ -576,7 +576,7 @@ Shuts down uncleanly the domain sub force_shutdown{ my $self = shift; - $self->_do_force_shutdown(); + return $self->_do_force_shutdown() if $self->is_active; } sub _do_force_shutdown { diff --git a/lib/Ravada/Domain/Void.pm b/lib/Ravada/Domain/Void.pm index b13a1d78c..5a52daafb 100644 --- a/lib/Ravada/Domain/Void.pm +++ b/lib/Ravada/Domain/Void.pm @@ -60,7 +60,6 @@ sub display { sub is_active { my $self = shift; - return ($self->_value('is_active') or 0); } diff --git a/lib/Ravada/Request.pm b/lib/Ravada/Request.pm index 75a48c841..c50bcd502 100644 --- a/lib/Ravada/Request.pm +++ b/lib/Ravada/Request.pm @@ -421,8 +421,13 @@ sub _new_request { $args{args}->{uid} = $args{args}->{id_owner} if !exists $args{args}->{uid}; $args{at_time} = $args{args}->{at} if exists $args{args}->{at}; - $args{id_domain} = $args{args}->{id_domain} - if exists $args{args}->{id_domain} && ! $args{id_domain}; + my $id_domain_args = $args{args}->{id_domain}; + + if ($id_domain_args) { + confess "ERROR: Different id_domain: ".Dumper(\%args) + if $args{id_domain} && $args{id_domain} ne $id_domain_args; + $args{id_domain} = $id_domain_args; + } $args{args} = encode_json($args{args}); } _init_connector() if !$CONNECTOR || !$$CONNECTOR; diff --git a/rvd_front.pl b/rvd_front.pl index 483bb0d65..c59611a4d 100644 --- a/rvd_front.pl +++ b/rvd_front.pl @@ -1312,7 +1312,7 @@ sub manage_machine { return access_denied($c) if $domain->id_owner != $USER->id && !$USER->is_admin; - Ravada::Request->shutdown_domain(name => $domain->name, uid => $USER->id) if $c->param('shutdown'); + Ravada::Request->shutdown_domain(id_domain => $domain->id, uid => $USER->id) if $c->param('shutdown'); Ravada::Request->start_domain( uid => $USER->id ,name => $domain->name , remote_ip => _remote_ip($c) @@ -1337,7 +1337,7 @@ sub settings_machine { $c->stash(domain => $domain); $c->stash(USER => $USER); - my $req = Ravada::Request->shutdown_domain(name => $domain->name, uid => $USER->id) + my $req = Ravada::Request->shutdown_domain(id_domain => $domain->id, uid => $USER->id) if $c->param('shutdown') && $domain->is_active; $req = Ravada::Request->start_domain( @@ -1433,7 +1433,7 @@ sub shutdown_machine { return login($c) if !_logged_in($c); my ($domain, $type) = _search_requested_machine($c); - my $req = Ravada::Request->shutdown_domain(name => $domain->name, uid => $USER->id); + my $req = Ravada::Request->shutdown_domain(id_domain => $domain->id, uid => $USER->id); return $c->redirect_to('/machines') if $type eq 'html'; return $c->render(json => { req => $req->id }); diff --git a/t/35_request_start.t b/t/35_request_start.t index f82d7c6fe..c64a26903 100644 --- a/t/35_request_start.t +++ b/t/35_request_start.t @@ -111,8 +111,10 @@ sub test_start { ok($req2->status eq 'done',"Expecting request status 'done' , got " .$req2->status); + my $id_domain; { my $domain = $RAVADA->search_domain($name); + $id_domain = $domain->id; $domain->start($USER) if !$domain->is_active(); ok($domain->is_active); @@ -127,7 +129,7 @@ sub test_start { # # stop - my $req3 = Ravada::Request->force_shutdown_domain(name => $name, uid => $USER->id); + my $req3 = Ravada::Request->force_shutdown_domain(id_domain => $id_domain, uid => $USER->id); $RAVADA->process_requests(); wait_request($req3); ok($req3->status eq 'done',"[$vm_name] expecting request done , got " From 254a1152be07ccd2a5a35684e9d8e9a4f2c7e4ac Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Tue, 25 Jul 2017 13:36:48 +0200 Subject: [PATCH 50/52] Force shutdown on tests --- t/vm/70_clone.t | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/t/vm/70_clone.t b/t/vm/70_clone.t index b99402a61..340e3322b 100644 --- a/t/vm/70_clone.t +++ b/t/vm/70_clone.t @@ -72,6 +72,7 @@ sub test_clone { $base->is_public(1); eval { $clone1 = $base->clone(name => $name_clone, user => $USER) }; ok(!$@,"Expecting error='', got='".($@ or '')."'"); + or die Dumper($base->list_requests); ok($clone1,"Expecting new cloned domain from ".$base->name) or return; is($clone1->description,undef); @@ -92,7 +93,7 @@ sub test_clone { sub test_mess_with_bases { my ($vm_name, $base, $clones) = @_; for my $clone (@$clones) { - $clone->shutdown(user => $USER, timeout => 1) if $clone->is_active; + $clone->force_shutdown($USER) if $clone->is_active; ok($clone->id_base,"Expecting clone has id_base , got " .($clone->id_base or '')); $clone->prepare_base($USER); @@ -104,14 +105,13 @@ sub test_mess_with_bases { ok(!$@,"Expecting error: '' , got: ".($@ or '')) or exit; ok($clone->is_active); - $clone->shutdown(user => $USER, timeout => 1) if $clone->is_active; + $clone->force_shutdown($USER) if $clone->is_active; $clone->remove_base($USER); eval { $clone->start($USER); }; ok(!$@,"[$vm_name] Expecting error: '' , got '".($@ or '')."'"); ok($clone->is_active); - $clone->shutdown(user => $USER, timeout => 1); - + $clone->force_shutdown($USER); } } From cbe27cd03b181cd861e795ecc8e55c31d4948810 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Fri, 6 Oct 2017 17:16:25 +0200 Subject: [PATCH 51/52] Force shutdown by id domain --- lib/Ravada/Request.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Ravada/Request.pm b/lib/Ravada/Request.pm index c50bcd502..c349fb6d9 100644 --- a/lib/Ravada/Request.pm +++ b/lib/Ravada/Request.pm @@ -53,7 +53,7 @@ our %VALID_ARG = ( ,resume_domain => {%$args_manage, remote_ip => 1 } ,remove_domain => $args_manage ,shutdown_domain => { name => 2, id_domain => 2, uid => 1, timeout => 2, at => 2 } - ,force_shutdown_domain => { name => 1, uid => 1, at => 2 } + ,force_shutdown_domain => { id_domain => 1, uid => 1, at => 2 } ,screenshot_domain => { id_domain => 1, filename => 2 } ,start_domain => {%$args_manage, remote_ip => 1 } ,rename_domain => { uid => 1, name => 1, id_domain => 1} From da88334a872ef5b3530313d87168f9552c2cfbd5 Mon Sep 17 00:00:00 2001 From: Francesc Guasch Date: Fri, 6 Oct 2017 17:16:36 +0200 Subject: [PATCH 52/52] [#315] fixed list requests, added all_requests --- lib/Ravada/Domain.pm | 20 ++++++++++++++++---- t/vm/20_base.t | 4 ++-- t/vm/70_clone.t | 2 +- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/lib/Ravada/Domain.pm b/lib/Ravada/Domain.pm index 7265f1f91..1cbea15f0 100644 --- a/lib/Ravada/Domain.pm +++ b/lib/Ravada/Domain.pm @@ -266,7 +266,7 @@ sub _allow_shutdown { } elsif($user->can_shutdown_all) { return; } else { - $self->_allowed($user); + $self->_allow_manage_args(user => $user); } } @@ -1033,7 +1033,6 @@ sub _pre_shutdown { if keys %arg; $self->_allow_shutdown(@_); - $self->_allow_manage_args(user => $user); $self->_pre_shutdown_domain(); @@ -1554,19 +1553,22 @@ sub remote_ip { =head2 list_requests -Returns a list of pending requests from the domain +Returns a list of pending requests from the domain. It won't show those requests +scheduled for later. =cut sub list_requests { my $self = shift; + my $all = shift; + my $sth = $$CONNECTOR->dbh->prepare( "SELECT * FROM requests WHERE id_domain = ? AND status <> 'done'" ); $sth->execute($self->id); my @list; while ( my $req_data = $sth->fetchrow_hashref ) { - next if $req_data->{at_time} && $req_data->{at_time} - time > 1; + next if !$all && $req_data->{at_time} && $req_data->{at_time} - time > 1; push @list,($req_data); } $sth->finish; @@ -1574,6 +1576,16 @@ sub list_requests { return map { Ravada::Request->open($_->{id}) } @list; } +=head2 list_all_requests + +Returns a list of pending requests from the domain including those scheduled for later + +=cut + +sub list_all_requests { + return list_requests(@_,'all'); +} + sub _dbh { my $self = shift; _init_connector() if !$CONNECTOR || !$$CONNECTOR; diff --git a/t/vm/20_base.t b/t/vm/20_base.t index 5a080e16b..4fef5be2c 100644 --- a/t/vm/20_base.t +++ b/t/vm/20_base.t @@ -487,11 +487,11 @@ sub test_domain_limit_already_requested { rvd_back->enforce_limits(timeout => 2); if (!$domain->can_hybernate) { - @list_requests = $domain->list_requests; + @list_requests = $domain->list_all_requests(); is(scalar @list_requests,1,"Expecting 1 request ".Dumper(\@list_requests)); rvd_back->enforce_limits(timeout => 2); - @list_requests = $domain->list_requests; + @list_requests = $domain->list_all_requests(); is(scalar @list_requests,1,"Expecting 1 request ".Dumper(\@list_requests)); sleep 3; diff --git a/t/vm/70_clone.t b/t/vm/70_clone.t index 340e3322b..a5f1eadab 100644 --- a/t/vm/70_clone.t +++ b/t/vm/70_clone.t @@ -71,7 +71,7 @@ sub test_clone { # diag("[$vm_name] Cloning from base ".$base->name." to $name_clone"); $base->is_public(1); eval { $clone1 = $base->clone(name => $name_clone, user => $USER) }; - ok(!$@,"Expecting error='', got='".($@ or '')."'"); + ok(!$@,"Expecting error='', got='".($@ or '')."'") or die Dumper($base->list_requests); ok($clone1,"Expecting new cloned domain from ".$base->name) or return;