Commit bcf1e0a1 authored by Francesc Guasch's avatar Francesc Guasch
Browse files

Merge branch 'develop' of https://github.com/UPC/ravada into develop

parents e6336b50 fba5f10a
......@@ -210,7 +210,13 @@ A PR can only be merged into master by a maintainer if:
Any maintainer is allowed to merge a PR if all of these conditions are
met.
### 11 Reset my fork to upstream
## 11 Reset my fork to upstream
You may want to ditch everything in your fork
### 11.1 Reset develop branch
If you want to get even with main develop branch.
```sh
git remote add upstream https://github.com/UPC/ravada
......@@ -220,3 +226,28 @@ git reset --hard upstream/develop
git push origin develop --force
```
### 11.2 Work in a new fresh branch
We create a new branch called *feature/cool_thing* and make it exactly like UPC/develop branch:
First we add the upstream remote source and fetch it. If you added this remote before you will get an error *fatal: remote upstream already exists.*. Don't worry and run the `git fetch upstream` anyway so it downloads the UPC source.
```sh
git remote add upstream https://github.com/UPC/ravada
git fetch upstream
```
Now we create a new branch:
```sh
git checkout -b feature/cool_thing
```
Reset this branch, now it will be an exact replica of upstream UPC/develop:
```sh
git reset --hard upstream/develop
git push --set-upstream origin feature/cool_thing
```
Now work on your code, test it so it is great. Then commit, push and create a *pull request*.
......@@ -25,6 +25,7 @@ services:
- ravada_network
#By default download from dockerhub
image: ravada/front
env_file: .env
#If you want to local build
#build: dockers/front/.
restart: unless-stopped
......@@ -50,6 +51,7 @@ services:
- ravada_network
#By default download from dockerhub
image: ravada/back
env_file: .env
#If you want to local build
#build: dockers/back/.
privileged: true
......
......@@ -1049,6 +1049,7 @@ sub _alias_grants($self) {
my %alias= (
remove_clone => 'remove_clones'
,shutdown_clone => 'shutdown_clones'
,reboot_clone => 'reboot_clones'
);
my $sth_old = $CONNECTOR->dbh->prepare("SELECT id FROM grant_types_alias"
......@@ -1070,6 +1071,9 @@ sub _add_grants($self) {
$self->_add_grant('rename_all', 0,"Can rename any virtual machine.");
$self->_add_grant('rename_clones', 0,"Can rename clones from virtual machines owned by the user.");
$self->_add_grant('shutdown', 1,"Can shutdown own virtual machines.");
$self->_add_grant('reboot', 1,"Can reboot own virtual machines.");
$self->_add_grant('reboot_all', 0,"Can reboot all virtual machines.");
$self->_add_grant('reboot_clones', 0,"Can reboot clones own virtual machines.");
$self->_add_grant('screenshot', 1,"Can get a screenshot of own virtual machines.");
$self->_add_grant('start_many',0,"Can have more than one machine started.");
$self->_add_grant('expose_ports',0,"Can expose virtual machine ports.");
......@@ -1141,6 +1145,7 @@ sub _enable_grants($self) {
,'remove', 'remove_all', 'remove_clone', 'remove_clone_all'
,'screenshot'
,'shutdown', 'shutdown_all', 'shutdown_clone'
,'reboot', 'reboot_all', 'reboot_clones'
,'screenshot'
,'start_many'
);
......@@ -1635,6 +1640,7 @@ sub _upgrade_tables {
$self->_upgrade_table('domain_ports', 'internal_ip','char(200)');
$self->_upgrade_table('domain_ports', 'restricted','int(1) DEFAULT 0');
$self->_upgrade_table('domain_ports', 'is_active','int(1) DEFAULT 0');
$self->_upgrade_table('messages','date_changed','timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
}
......@@ -3580,6 +3586,72 @@ sub _cmd_force_shutdown {
}
sub _cmd_reboot {
my $self = shift;
my $request = shift;
my $uid = $request->args('uid');
my $name = $request->defined_arg('name');
my $id_domain = $request->defined_arg('id_domain');
my $timeout = ($request->args('timeout') or 60);
my $id_vm = $request->defined_arg('id_vm');
confess "ERROR: Missing id_domain or name" if !$id_domain && !$name;
my $domain;
if ($name) {
if ($id_vm) {
my $vm = Ravada::VM->open($id_vm);
$domain = $vm->search_domain($name);
} else {
$domain = $self->search_domain($name);
}
die "Unknown domain '$name'\n" if !$domain;
}
if ($id_domain) {
my $domain2 = Ravada::Domain->open(id => $id_domain, id_vm => $id_vm);
die "ERROR: Domain $id_domain is ".$domain2->name." not $name."
if $domain && $domain->name ne $domain2->name;
$domain = $domain2;
die "Unknown domain '$id_domain'\n" if !$domain
}
Ravada::Request->refresh_machine(
uid => $uid
,id_domain => $id_domain
,after_request => $request->id
);
my $user = Ravada::Auth::SQL->search_by_id( $uid);
$domain->reboot(timeout => $timeout, user => $user
, request => $request);
}
sub _cmd_force_reboot {
my $self = shift;
my $request = shift;
my $uid = $request->args('uid');
my $id_domain = $request->args('id_domain');
my $id_vm = $request->defined_arg('id_vm');
my $domain;
if ($id_vm) {
my $vm = Ravada::VM->open($id_vm);
$domain = $vm->search_domain_by_id($id_domain);
} else {
$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->force_reboot($user,$request);
}
sub _cmd_list_vm_types {
my $self = shift;
my $request = shift;
......@@ -3703,11 +3775,27 @@ sub _cmd_refresh_machine($self, $request) {
my $domain = Ravada::Domain->open($id_domain) or confess "Error: domain $id_domain not found";
$domain->check_status();
$domain->list_volumes_info();
$self->_remove_unnecessary_downs($domain) if !$domain->is_active;
my $is_active = $domain->is_active;
$self->_remove_unnecessary_downs($domain) if !$is_active;
$domain->info($user);
Ravada::Request->refresh_machine_ports(id_domain => $domain->id, uid => $user->id)
if $is_active && $domain->ip;
}
sub _cmd_refresh_machine_ports($self, $request) {
my $id_domain = $request->args('id_domain');
my $uid = $request->args('uid');
my $user = Ravada::Auth::SQL->search_by_id($uid);
my $domain = Ravada::Domain->open($id_domain) or confess "Error: domain $id_domain not found";
die "USER $uid not authorized to refresh machine ports for domain ".$domain->name
unless $domain->_data('id_owner') == $user->id || $user->is_operator;
$domain->refresh_ports($request);
}
sub _cmd_change_owner($self, $request) {
my $uid = $request->args('uid');
my $id_domain = $request->args('id_domain');
......@@ -4230,6 +4318,7 @@ sub _req_method {
,cleanup => \&_cmd_cleanup
,download => \&_cmd_download
,shutdown => \&_cmd_shutdown
,reboot => \&_cmd_reboot
,hybernate => \&_cmd_hybernate
,set_driver => \&_cmd_set_driver
,screenshot => \&_cmd_screenshot
......@@ -4250,11 +4339,13 @@ sub _req_method {
,list_vm_types => \&_cmd_list_vm_types
,enforce_limits => \&_cmd_enforce_limits
,force_shutdown => \&_cmd_force_shutdown
,force_reboot => \&_cmd_force_reboot
,rebase => \&_cmd_rebase
,refresh_storage => \&_cmd_refresh_storage
,check_storage => \&_cmd_check_storage
,refresh_machine => \&_cmd_refresh_machine
,refresh_machine_ports => \&_cmd_refresh_machine_ports
,domain_autostart=> \&_cmd_domain_autostart
,change_owner => \&_cmd_change_owner
,add_hardware => \&_cmd_add_hardware
......@@ -4549,6 +4640,13 @@ sub _cmd_remove_expose($self, $request) {
sub _cmd_open_exposed_ports($self, $request) {
my $domain = Ravada::Domain->open($request->id_domain);
$domain->open_exposed_ports();
Ravada::Request->refresh_machine_ports(
uid => $request->args('uid'),
,id_domain => $domain->id
,retry => 10
);
}
=head2 set_debug_value
......
......@@ -632,7 +632,7 @@ Returns if the user is allowed to perform a privileged action in a virtual machi
=cut
sub can_do_domain($self, $grant, $domain) {
my %valid_grant = map { $_ => 1 } qw(change_settings shutdown rename);
my %valid_grant = map { $_ => 1 } qw(change_settings shutdown reboot rename);
confess "Invalid grant here '$grant'" if !$valid_grant{$grant};
return 0 if !$self->can_do($grant) && !$self->_domain_id_base($domain);
......@@ -735,6 +735,7 @@ sub grant_user_permissions($self,$user) {
$self->grant($user, 'remove');
$self->grant($user, 'shutdown');
$self->grant($user, 'screenshot');
$self->grant($user, 'reboot');
}
=head2 grant_operator_permissions
......
......@@ -28,6 +28,7 @@ use Ravada::Domain::Driver;
use Ravada::Utils;
our $TIMEOUT_SHUTDOWN = 20;
our $TIMEOUT_REBOOT = 20;
our $CONNECTOR;
our $MIN_FREE_MEMORY = 1024*1024;
......@@ -56,6 +57,10 @@ requires 'shutdown';
requires 'shutdown_now';
requires 'force_shutdown';
requires '_do_force_shutdown';
requires 'reboot';
requires 'reboot_now';
requires 'force_reboot';
requires '_do_force_reboot';
requires 'pause';
requires 'resume';
......@@ -107,6 +112,12 @@ has 'timeout_shutdown' => (
,default => $TIMEOUT_SHUTDOWN
);
has 'timeout_reboot' => (
isa => 'Int'
,is => 'ro'
,default => $TIMEOUT_REBOOT
);
has 'readonly' => (
isa => 'Int'
,is => 'ro'
......@@ -178,6 +189,12 @@ after 'shutdown' => \&_post_shutdown;
around 'shutdown_now' => \&_around_shutdown_now;
around 'force_shutdown' => \&_around_shutdown_now;
before 'reboot' => \&_allow_shutdown;
after 'reboot' => \&_post_reboot;
around 'reboot_now' => \&_around_reboot_now;
around 'force_reboot' => \&_around_reboot_now;
before 'remove_base' => \&_pre_remove_base;
after 'remove_base' => \&_post_remove_base;
after 'spinoff' => \&_post_spinoff;
......@@ -2254,6 +2271,8 @@ sub _post_spinoff($self) {
sub _pre_shutdown_domain {}
sub _pre_reboot_domain {}
sub _post_remove_base_domain {}
sub _remove_base_db {
......@@ -2487,6 +2506,7 @@ sub _post_shutdown {
if ( $self->_vm->is_active ) {
$self->_remove_iptables();
$self->_close_exposed_port();
$self->_set_ports_down();
}
my $is_active = $self->is_active;
......@@ -2555,6 +2575,13 @@ sub _post_shutdown {
&& !$is_active;
}
sub _post_reboot {
my $self = shift;
$self->_data(status => 'rebooted');
$self->_remove_iptables();
$self->_close_exposed_port();
}
sub _around_is_active($orig, $self) {
if (!$self->_vm) {
......@@ -2610,6 +2637,20 @@ sub _around_shutdown_now {
$self->_post_shutdown(user => $user) if $self->is_known();
}
sub _around_reboot_now {
my $orig = shift;
my $self = shift;
my $user = shift;
$self->_vm->connect;
$self->list_disks;
$self->_pre_shutdown(user => $user);
if ($self->is_active) {
$self->$orig($user);
}
$self->_post_shutdown(user => $user) if $self->is_known();
}
sub _around_name($orig,$self) {
return $self->{_name} if $self->{_name};
......@@ -2679,7 +2720,7 @@ sub expose($self, @args) {
$internal_port = delete $args{internal_port} if exists $args{internal_port};
delete $args{internal_ip};
# remove old fields
for (qw(public_ip active description)) {
for (qw(public_ip active description is_active)) {
delete $args{$_};
}
......@@ -3159,6 +3200,11 @@ sub _timeout_shutdown($self, $value) {
return $TIMEOUT_SHUTDOWN;
}
sub _timeout_reboot($self, $value) {
$TIMEOUT_REBOOT = $value if defined $value;
return $TIMEOUT_REBOOT;
}
sub _post_start {
my $self = shift;
my %arg;
......@@ -5464,4 +5510,49 @@ sub purge($self, $request=undef) {
$self->_data( 'has_backups' => 0 );
}
sub _check_port($self, $port, $ip=$self->ip, $request=undef) {
my ($out, $err) = $self->_vm->run_command("nc","-z","-v",$ip,$port);
$request->error($err) if $err;
return 1 if $err =~ /succeeded!/;
return 0 if $err =~ /failed/;
warn $err;
return 0;
}
sub _set_ports_down($self) {
my $sth = $$CONNECTOR->dbh->prepare(
"UPDATE domain_ports set is_active=0 "
." WHERE id_domain=?"
);
$sth->execute($self->id);
}
sub refresh_ports($self, $request=undef) {
my $sth_update = $$CONNECTOR->dbh->prepare("UPDATE domain_ports "
." SET is_active=? "
." WHERE id_domain=? AND id=?"
);
my $is_active = $self->is_active();
my $ip;
$ip = $self->ip if $is_active;
my $port_down = 0;
my $msg = '';
for my $port ($self->list_ports) {
my $is_port_active;
if ($is_active && $ip) {
$is_port_active = $self->_check_port($port->{internal_port}, $ip, $request);
} else {
$is_port_active = 0;
}
$port_down++;
$sth_update->execute($is_port_active, $self->id, $port->{id});
$msg .= " , " if $msg;
$msg .= " $port->{internal_port} $is_port_active";
}
die "Virtual machine ".$self->name." is not up. retry.\n"if !$ip;
die "Virtual machine ".$self->name." $ip has ports down: $msg. retry.\n"
if $port_down;
}
1;
......@@ -49,6 +49,7 @@ has readonly => (
##################################################
#
our $TIMEOUT_SHUTDOWN = 60;
our $TIMEOUT_REBOOT = 60;
our $OUT;
our %SET_DRIVER_SUB = (
......@@ -862,6 +863,57 @@ sub _do_force_shutdown {
warn $@ if $@;
}
=head2 reboot
Stops the domain
=cut
sub reboot {
my $self = shift;
my %args = @_;
my $req = $args{req};
if (!$self->is_active) {
$req->status("done") if $req;
$req->error("Domain is down") if $req;
return;
}
return $self->_do_force_shutdown() if $args{force};
return $self->_do_reboot();
}
sub force_reboot {
my $self = shift;
return $self->_do_force_reboot() if $self->is_active;
}
sub _do_force_reboot {
my $self = shift;
return if !$self->domain->is_active;
eval { $self->domain->reset() };
warn $@ if $@;
}
sub _do_reboot {
my $self = shift;
return if !$self->domain->is_active;
eval { $self->domain->reboot() };
die $@ if $@;
}
=head2 reboot_now
Reboots uncleanly the domain
=cut
sub reboot_now {
my $self = shift;
return $self->_do_reboot() if $self->is_active;
}
=head2 pause
......
......@@ -234,6 +234,26 @@ sub shutdown_now {
return $self->shutdown(user => $user);
}
sub reboot {
my $self = shift;
$self->_store(is_active => 0);
}
sub force_reboot {
return reboot_now(@_);
}
sub _do_force_reboot {
my $self = shift;
return $self->_store(is_active => 0);
}
sub reboot_now {
my $self = shift;
my $user = shift;
return $self->reboot(user => $user);
}
sub start($self, @args) {
my %args;
%args = @args if scalar(@args) % 2 == 0;
......@@ -827,4 +847,10 @@ sub change_hardware($self, $hardware, $index, $data) {
sub dettach($self,$user) {
# no need to do anything to dettach mock volumes
}
sub _check_port($self,@args) {
return 1 if $self->is_active;
return 0;
}
1;
......@@ -221,20 +221,25 @@ sub list_machines($self, $user, @filter) {
sub _init_available_actions($user, $m) {
eval { $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;
$m->{can_start} = 0;
$m->{can_start} = 1 if $m->{id_owner} == $user->id || $user->is_admin;
$m->{can_view} = 0;
$m->{can_view} = 1 if $m->{id_owner} == $user->id || $user->is_admin;
$m->{can_reboot} = $m->{can_shutdown} && $m->{can_start};
$m->{can_manage} = ( $user->can_manage_machine($m->{id}) or 0);
eval {
$m->{can_change_settings} = ( $user->can_change_settings($m->{id}) or 0);
};
die $@ if $@ && $@ !~ /Unknown domain/;
$m->{can_view} = 0;
$m->{can_view} = 1 if $m->{id_owner} == $user->id || $user->is_admin;
$m->{can_hibernate} = 0;
$m->{can_hibernate} = 1 if $user->can_shutdown($m->{id}) && !$m->{is_volatile};
$m->{can_manage} = ( $user->can_manage_machine($m->{id}) or 0);
eval {
$m->{can_change_settings} = ( $user->can_change_settings($m->{id}) or 0);
};
#may have been deleted just now
next if $@ && $@ =~ /Unknown domain/;
die $@ if $@;
$m->{can_hibernate} = 0;
$m->{can_hibernate} = 1 if $user->can_shutdown($m->{id})
&& !$m->{is_volatile};
}
sub _around_list_machines($orig, $self, $user, @filter) {
......@@ -1021,8 +1026,10 @@ sub list_requests($self, $id_domain_req=undef, $seconds=60) {
|| $command eq 'manage_pools'
;
next if ( $command eq 'force_shutdown'
|| $command eq 'force_reboot'
|| $command eq 'start'
|| $command eq 'shutdown'
|| $command eq 'reboot'
|| $command eq 'hibernate'
)
&& time - $epoch_date_changed > 5
......
......@@ -68,6 +68,7 @@ sub open($self, $id) {
sub autostart($self ) { return $self->_data('autostart') }
sub _do_force_shutdown { confess "TODO" }
sub _do_force_reboot { confess "TODO" }
sub add_volume { confess "TODO" }
sub remove_volume { confess "TODO" }
sub clean_swap_volumes { confess "TODO" }
......@@ -92,6 +93,8 @@ sub display_file_tls($self, $user) {
sub force_shutdown { confess "TODO" }
sub force_reboot { confess "TODO" }
sub get_info($self) {
my $info = $self->_data('info');
return {} if !$info;
......@@ -177,6 +180,8 @@ sub set_max_mem { confess "TODO" }
sub set_memory { confess "TODO" }
sub shutdown { confess "TODO" }
sub shutdown_now { confess "TODO" }
sub reboot { confess "TODO" }
sub reboot_now { confess "TODO" }
sub spinoff { confess "TODO" }
sub start { confess "TODO" }
......
......@@ -66,6 +66,9 @@ our %VALID_ARG = (
,shutdown_domain => { name => 2, id_domain => 2, uid => 1, timeout => 2, at => 2
, id_vm => 2 }
,force_shutdown_domain => { id_domain => 1, uid => 1, at => 2, id_vm => 2 }
,reboot_domain => { name => 2, id_domain => 2, uid => 1, timeout => 2, at => 2
, id_vm => 2 }
,force_reboot_domain => { id_domain => 1, uid => 1, at => 2, id_vm => 2 }
,screenshot => { id_domain => 1 }
,domain_autostart => { id_domain => 1 , uid => 1, value => 2 }
,copy_screenshot => { id_domain => 1 }
......@@ -99,6 +102,7 @@ our %VALID_ARG = (
,change_hardware => {uid => 1, id_domain => 1, hardware => 1, index => 2, data => 1 }
,enforce_limits => { timeout => 2, _force => 2 }
,refresh_machine => { id_domain => 1, uid => 1 }
,refresh_machine_ports => { id_domain => 1, uid => 1 }
,rebase => { uid => 1, id_base => 1, id_domain => 1 }
,set_time => { uid => 1, id_domain => 1 }
,rsync_back => { uid => 1, id_domain => 1, id_node => 1 }
......@@ -132,7 +136,7 @@ our %VALID_ARG = (
);
our %CMD_SEND_MESSAGE = map { $_ => 1 }
qw( create start shutdown force_shutdown prepare_base remove remove_base rename_domain screenshot download
qw( create start shutdown force_shutdown reboot prepare_base remove remove_base rename_domain screenshot download
clone
set_base_vm remove_base_vm
domain_autostart hibernate hybernate
......@@ -140,7 +144,7 @@ our %CMD_SEND_MESSAGE = map { $_ => 1 }
add_hardware remove_hardware set_driver change_hardware
expose remove_expose