Commit 0f51b80d authored by Francesc Guasch's avatar Francesc Guasch
Browse files

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

parents f7c56afc 7e639b4d
......@@ -1183,9 +1183,10 @@ sub _add_grants($self) {
$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.");
$self->_add_grant('start_limit',0,"can have their own limit on started machines.", 1);
}
sub _add_grant($self, $grant, $allowed, $description) {
sub _add_grant($self, $grant, $allowed, $description, $is_int = 0) {
my $sth = $CONNECTOR->dbh->prepare(
"SELECT id, description FROM grant_types WHERE name=?"
);
......@@ -1202,9 +1203,9 @@ sub _add_grant($self, $grant, $allowed, $description) {
}
return if $id;
$sth = $CONNECTOR->dbh->prepare("INSERT INTO grant_types (name, description)"
." VALUES (?,?)");
$sth->execute($grant, $description);
$sth = $CONNECTOR->dbh->prepare("INSERT INTO grant_types (name, description, is_int)"
." VALUES (?,?,?)");
$sth->execute($grant, $description, $is_int);
$sth->finish;
$sth = $CONNECTOR->dbh->prepare("SELECT id FROM grant_types WHERE name=?");
......@@ -1253,7 +1254,7 @@ sub _enable_grants($self) {
,'shutdown', 'shutdown_all', 'shutdown_clone'
,'reboot', 'reboot_all', 'reboot_clones'
,'screenshot'
,'start_many'
,'start_limit', 'start_many'
);
my $sth = $CONNECTOR->dbh->prepare("SELECT id,name FROM grant_types");
......@@ -1766,6 +1767,8 @@ sub _upgrade_tables {
$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');
$self->_upgrade_table('grant_types', 'is_int', 'int DEFAULT 0');
}
sub _upgrade_timestamps($self) {
......@@ -4640,13 +4643,12 @@ sub _cmd_enforce_limits($self, $request=undef) {
}
sub _enforce_limits_active($self, $request) {
confess if !$request;
if (my $id_recent = $request->done_recently(30)) {
die "Command ".$request->command." run recently by $id_recent.\n";
}
my $timeout = ($request->defined_arg('timeout') or 10);
my $start_limit = $self->setting('/backend/start_limit');
my $start_limit_default = $self->setting('/backend/start_limit');
my %domains;
for my $domain ($self->list_domains( active => 1 )) {
......@@ -4654,8 +4656,11 @@ sub _enforce_limits_active($self, $request) {
$domain->client_status();
}
for my $id_user(keys %domains) {
next if scalar @{$domains{$id_user}} <= $start_limit;
my $user = Ravada::Auth::SQL->search_by_id($id_user);
my %grants = $user->grants();
my $start_limit = (defined($grants{'start_limit'}) && $grants{'start_limit'} > 0) ? $grants{'start_limit'} : $start_limit_default;
next if scalar @{$domains{$id_user}} <= $start_limit;
next if $user->is_admin;
next if $user->can_start_many;
......
......@@ -613,7 +613,6 @@ sub can_do($self, $grant) {
if $grant !~ /^[a-z_]+$/;
return $self->{_grant}->{$grant} if defined $self->{_grant}->{$grant};
confess "Unknown permission '$grant'. Maybe you are using an old release.\n"
."Try removing the table grant_types and start rvd_back again:\n"
."mysql> drop table grant_types;\n"
......@@ -678,7 +677,7 @@ sub _load_grants($self) {
my $sth;
eval { $sth= $$CON->dbh->prepare(
"SELECT gt.name, gu.allowed, gt.enabled"
"SELECT gt.name, gu.allowed, gt.enabled, gt.is_int"
." FROM grant_types gt LEFT JOIN grants_user gu "
." ON gt.id = gu.id_grant "
." AND gu.id_user=?"
......@@ -686,8 +685,8 @@ sub _load_grants($self) {
$sth->execute($self->id);
};
confess $@ if $@;
my ($name, $allowed, $enabled);
$sth->bind_columns(\($name, $allowed, $enabled));
my ($name, $allowed, $enabled, $is_int);
$sth->bind_columns(\($name, $allowed, $enabled, $is_int));
while ($sth->fetch) {
my $grant_alias = $self->_grant_alias($name);
......
......@@ -300,25 +300,30 @@ ravadaApp.directive("solShowMachine", swMach)
}
}
$scope.request = function(request, args) {
$http.post('/request/'+request+'/'
,JSON.stringify(args)
).then(function(response) {
$scope.request = function(request, args) {
$http.post('/request/'+request+'/'
,JSON.stringify(args)
).then(function(response) {
if(response.status == 300 ) {
console.error('Response error', response.status);
window.location.reload();
}
});
};
});
};
$scope.action = function(target,action,machineId){
$http.get('/'+target+'/'+action+'/'+machineId+'.json')
.then(function(response) {
if(response.status == 300 ) {
console.error('Reponse error', response.status);
window.location.reload();
}
});
if (action === 'view') {
window.location.assign('/machine/view/' + machineId + '.html');
}
else {
$http.get('/'+target+'/'+action+'/'+machineId+'.json')
.then(function(response) {
if(response.status == 300 ) {
console.error('Reponse error', response.status);
window.location.reload();
}
});
}
};
$scope.set_autostart= function(machineId, value) {
$http.get("/machine/autostart/"+machineId+"/"+value);
......
......@@ -96,9 +96,42 @@
})
);
};
$scope.action = function(machine, action) {
$scope.confirming_stop_data = null;
$scope.confirmingStopCancelled = function() {
$scope.confirming_stop_data = null;
};
$scope.confirmingStopDone = function() {
$scope.action($scope.confirming_stop_data.machine, $scope.confirming_stop_data.action, true);
$scope.confirming_stop_data = null;
};
$scope.checkMaxMachines = function(action,machine) {
$http.get('/execution_machines_limit')
.then(function(data) {
if ((data.data.can_start_many) || (data.data.running_domains.indexOf(machine.id) >= 0) || (data.data.start_limit > data.data.running_domains.length)) {
$scope.action(machine, action, true);
}
else {
$scope.confirming_stop_data = { action: action, machine: machine };
}
}, function(data,status) {
console.error('Repos error', status, data);
window.location.reload();
});
};
$scope.action = function(machine, action, confirmed) {
machine.action = false;
if ( action == 'restore' ) {
if (action == 'start') {
if ((! confirmed) && (! machine.is_active)) {
$scope.checkMaxMachines(action, machine);
} else {
window.location.assign('/machine/clone/' + machine.id + '.html');
}
} else if ( action == 'restore' ) {
$scope.host_restore = machine.id_clone;
$scope.host_shutdown = 0;
$scope.host_force_shutdown = 0;
......@@ -221,12 +254,17 @@
};
$scope.action = function(target,action,machineId,params){
$http.get('/'+target+'/'+action+'/'+machineId+'.json'+'?'+this.getQueryStringFromObject(params))
.then(function() {
}, function(data,status) {
console.error('Repos error', status, data);
window.location.reload();
});
if (action === 'view') {
window.location.assign('/machine/view/' + machineId + '.html');
}
else {
$http.get('/'+target+'/'+action+'/'+machineId+'.json'+'?'+this.getQueryStringFromObject(params))
.then(function() {
}, function(data,status) {
console.error('Repos error', status, data);
window.location.reload();
});
}
};
subscribe_requests = function(url) {
......
......@@ -891,6 +891,12 @@ get '/machine/compact/(#id_domain)' => sub($c) {
return $c->render(json => { request => $req->id });
};
get '/execution_machines_limit' => sub {
my $c = shift;
return get_execution_machines_limit_per_current_user($c);
};
get '/node/exists/#name' => sub {
my $c = shift;
my $name = $c->stash('name');
......@@ -1067,14 +1073,15 @@ any '/admin/user/(:id).(:type)' => sub {
my %grant;
for my $param_name (@{$c->req->params->names}) {
if ( $param_name =~ /^perm_(.*)/ ) {
$grant{$1} = 1;
my $max = defined($c->req->params->param('max_perm_' . $1)) ? $c->req->params->param('max_perm_' . $1) : 1;
$grant{$1} = $max > 1 ? $max : 1;
} elsif ($param_name =~ /^off_perm_(.*)/) {
$grant{$1} = 0 if !exists $grant{$1};
}
}
for my $perm (keys %grant) {
if ( $grant{$perm} ) {
$USER->grant($user, $perm);
$USER->grant($user, $perm, $grant{$perm});
} else {
$USER->revoke($user, $perm);
}
......@@ -2722,7 +2729,22 @@ sub resume_machine {
return $c->render(json => { req => $req->id });
}
sub get_execution_machines_limit_per_current_user {
my $c = shift;
return login($c) if !_logged_in($c);
my %grants = $USER->grants();
my $start_limit = ((exists($grants{'start_limit'})) && ($grants{'start_limit'} > 0)) ? $grants{'start_limit'} : $RAVADA->settings_global()->{'backend'}->{'start_limit'}->{'value'};
my $can_start_many = $USER->can_start_many ? 1 : 0;
my @running_domains;
foreach my $domain (@{$RAVADA->list_domains( id_owner => $USER->id )})
{
push(@running_domains, $domain->{'id'}) if ($domain->{'is_active'});
}
return $c->render(json => { can_start_many => $can_start_many, start_limit => $start_limit, running_domains => \@running_domains });
}
sub list_requests {
my $c = shift;
......
......@@ -20,6 +20,9 @@ INSERT INTO grant_types(name,description) VALUES('change_settings_all',"can chan
INSERT INTO grant_types(name,description) VALUES('remove_clone_all',"can remove any clone.");
INSERT INTO grant_types(name,description) VALUES('hibernate_clone_all',"can hibernate any clone.");
/* Special users should be allowed these */
INSERT INTO grant_types(name,description, is_int) VALUES('start_limit',"can have their own limit on started machines.", 1); /* the value in grants_user will be the maximum number of concurrent machines instead of a boolean */
/* admins should be allowed these */
INSERT INTO grant_types(name,description) VALUES('clone_all',"can clone any virtual machine.");
INSERT INTO grant_types(name,description) VALUES('remove_all',"can remove any virtual machine.");
......
......@@ -3,6 +3,7 @@ CREATE TABLE `grant_types` (
`name` char(32) NOT NULL,
`description` varchar(255) NOT NULL,
`enabled` int default NULL,
`is_int` int default 0,
UNIQUE(`name`),
UNIQUE(`description`),
PRIMARY KEY (`id`)
......
......@@ -3,5 +3,6 @@ CREATE TABLE `grant_types` (
, `name` char(32) NOT NULL
, `description` varchar(255) NOT NULL
, `enabled` integer default NULL
, `is_int` integer default 0
, UNIQUE(`name`)
);
......@@ -275,12 +275,11 @@
title="<%=l 'ShutDown' %>">
<i class="fa fa-power-off"></i>
</button>
<a type="button" class="btn btn-primary btn-sm"
ng-show="machine.can_view"
ng-href="/machine/view/{{machine.id}}.html"
<button type="button" class="btn btn-primary btn-sm"
ng-click="action('machine','view',machine.id)"
title="<%=l 'View' %>">
<i class="fa fa-desktop"></i>
</a>
</button>
</div>
<div ng-show="machine.is_locked" ng-cloak>Machine <a href="/request/{{machine.is_locked}}.html"><%=l 'locked' %></a></div>
<div ng-show="machine.is_base && !machine.is_locked"
......
......@@ -19,15 +19,16 @@
<div class="card-header" id="step1" >
<h3 class="card-title">
<a class="btn btn-link"
href="/machine/clone/{{machine.id}}.html"
href=""
ng-click="action(machine, 'start'); false"
role="button">{{machine.name}}</a>
<i ng-show="!machine.is_public"><i class="far fa-eye-slash fa-xs" title="<%=l 'not public' %>"></i></i>
</h3>
<div class="container">
<a ng-show="machine.screenshot" href="/machine/clone/{{machine.id}}.html"><img
<a ng-show="machine.screenshot" ng-click="action(machine, 'start')"><img
ng-src="data:image/png;base64,{{machine.screenshot}}" alt="{{machine.name}}" class="img-thumbnail" width="260"
></a>
<a ng-show="!machine.screenshot" href="/machine/clone/{{machine.id}}.html"><img
<a ng-show="!machine.screenshot" ng-click="action(machine, 'start')"><img
src="/img/default_screenshot.png" class="screenshot-default img-thumbnail"
alt="{{machine.name}}" width="260"
></a>
......@@ -38,9 +39,22 @@
</div>
</div>
<div class="card-body" id="step2">
<div ng-show="(confirming_stop_data) && (confirming_stop_data.machine === machine)" class="row alert alert-warning" role="alert">
<div>
<span><%=l 'One of the machines that are currently running will be shutdown.' %></span>
<br>
<span><%=l 'Continue?' %></span>
<br>
</div>
<div class="pull-right">
<input type="button" class="btn btn-danger btn-xs" value="<%=l 'Yes' %>" ng-click="confirmingStopDone()">
<input type="button" class="btn btn-primary btn-xs" value="<%=l 'No' %>" ng-click="confirmingStopCancelled()">
</div>
<br>
</div>
<div class="row">
<a type="button" class="btn btn-success mr-2" ng-hide="machine.action"
href="/machine/clone/{{machine.id}}.html"><strong><i class="fa fa-play" aria-hidden="true"></i>&nbsp;<%=l 'Start' %></strong></a>
<button class="btn btn-success mr-2" ng-hide="machine.action"
ng-click="action(machine,'start')"><strong><i class="fa fa-play" aria-hidden="true"></i>&nbsp;<%=l 'Start' %></strong></button>
<div class="dropdown">
<button ng-show="machine.is_active && !machine.action" class="btn btn-secondary dropdown-toggle"
ng-click="$parent.refresh=20" type="button" id="dropdownMenuButton"
......
......@@ -2,12 +2,15 @@
<div class="card-body">
<form method="post">
% for my $perm ($_user->list_all_permissions) {
% my $checked = '';
% $checked = 'checked' if $user->can_do($perm->{name});
% my $can_do = $user->can_do($perm->{name});
% my $checked = $can_do ? 'checked' : '';
<input type="checkbox" <%= $checked %>
name="perm_<%= $perm->{name} %>">
<input type="hidden" name="off_perm_<%= $perm->{name} %>" value="off">
<label for="perm_<%= $perm->{name} %>"><%= $perm->{name} %>: <%=l($perm->{description}) %></label><br/>
% if ($perm->{is_int} != 0) {
<input type="number" name="max_perm_<%= $perm->{name} %>" min="1" value="<%= $can_do %>" max="999" style="margin-left: 15px; margin-bottom: 10px"><br>
% }
% }
<button type="reset" class="btn btn-outline-secondary" onclick = "location='/admin/users'"><%=l 'Cancel' %></button>
<input type="submit" class="btn btn-primary" name="grant" value="<%=l 'Submit' %>">
......
......@@ -11,16 +11,15 @@
</div>
<div ng-show="showmachine.can_view">
<a type="button" class="btn btn-primary btn-sm"
ng-href="/machine/view/{{showmachine.id}}.html"
<button type="button" class="btn btn-primary btn-sm"
ng-click="action('machine','view',showmachine.id)"
title="<%=l 'View' %>">
<i class="fa fa-desktop"></i>
</a>
<i class="fa fa-desktop"></i>
</button>
<span><%=l 'View' %></span>
<br><br>
</div>
<div ng-show="showmachine.can_hibernate">
<button type="button" class="btn btn-warning btn-sm"
ng-click="action('machine','hibernate',showmachine.id)"
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment