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

Merge branch 'develop' into 1197_ports

parents 440bb568 756796eb
......@@ -2314,6 +2314,7 @@ sub _execute {
return;
}
$request->status('working','') unless $request->status() eq 'waiting';
$request->start_time(time);
$request->error('');
$request->status('working','');
......@@ -2349,6 +2350,7 @@ sub _do_execute_command {
# local *STDERR = $f_err;
# }
$request->status('working','') unless $request->status() eq 'working';
$request->pid($$);
my $t0 = [gettimeofday];
eval {
......@@ -2358,6 +2360,19 @@ sub _do_execute_command {
my $elapsed = tv_interval($t0,[gettimeofday]);
$request->run_time($elapsed);
$request->error($err) if $err;
if ($err) {
my $user = $request->defined_arg('user');
if ($user) {
warn "sending message to ".$user->id." ".$user->name;
my $subject = $err;
my $message = '';
if (length($subject) > 40 ) {
$message = $subject;
$subject = substr($subject,0,40);
$user->send_message($subject, $message);
}
}
}
if ($err && $err =~ /retry.?$/i) {
my $retry = $request->retry;
if (defined $retry && $retry>0) {
......@@ -2550,6 +2565,7 @@ sub _can_fork {
$req->error($msg);
$req->at_time(time+10);
$req->status('waiting') if $req->status() !~ 'waiting';
$req->at_time(time+10);
return 0;
}
sub _wait_pids {
......@@ -2981,6 +2997,11 @@ sub _cmd_shutdown {
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->shutdown(timeout => $timeout, user => $user
......
......@@ -161,6 +161,24 @@ sub unshown_messages {
}
=head2 send_message
Send a message to this user
$user->send_message($subject, $message)
=cut
sub send_message($self, $subject, $message='') {
_init_connector() if !$$CONNECTOR;
my $sth = $$CONNECTOR->dbh->prepare(
"INSERT INTO messages (id_user, subject, message) "
." VALUES(?, ? , ? )");
$sth->execute($self->id, $subject, $message);
}
=head2 show_message
......
......@@ -1300,6 +1300,7 @@ sub info($self, $user) {
,is_base => $self->is_base
,id_base => $self->id_base
,is_active => $is_active
,is_hibernated => $self->is_hibernated
,spice_password => $self->spice_password
,description => $self->description
,msg_timeout => ( $self->_msg_timeout or undef)
......@@ -1351,6 +1352,7 @@ sub info($self, $user) {
push @cdrom,($disk->{file}) if $disk->{file} && $disk->{file} =~ /\.iso$/;
}
$info->{cdrom} = \@cdrom;
$info->{requests} = $self->list_requests();
return $info;
}
......@@ -2064,6 +2066,11 @@ sub _post_shutdown {
return $self->_do_force_shutdown() if !$self->is_removed && $is_active;
}
Ravada::Request->refresh_machine(
at => time+int($timeout/2)
, uid => Ravada::Utils::user_daemon->id
, id_domain => $self->id
);
my $req = Ravada::Request->force_shutdown_domain(
id_domain => $self->id
,id_vm => $self->_vm->id
......
......@@ -872,7 +872,7 @@ sub list_requests($self, $id_domain_req=undef, $seconds=60) {
, $error, $id_domain, $domain, $date));
while ( $sth->fetch) {
my $epoch_date_changed;
my $epoch_date_changed = time;
if ($date_changed) {
my ($y,$m,$d,$hh,$mm,$ss) = $date_changed =~ /(\d{4})-(\d\d)-(\d\d) (\d+):(\d+):(\d+)/;
if ($y) {
......
......@@ -122,11 +122,13 @@ our %VALID_ARG = (
our %CMD_SEND_MESSAGE = map { $_ => 1 }
qw( create start shutdown prepare_base remove remove_base rename_domain screenshot download
clone
set_base_vm remove_base_vm
domain_autostart hibernate hybernate
change_owner
change_max_memory change_curr_memory
add_hardware remove_hardware set_driver change_hardware
expose remove_expose
set_base_vm
shutdown_node start_node
);
......@@ -523,15 +525,18 @@ sub _new_request {
}
my %args = @_;
$args{status} = 'requested';
$args{status} = 'initializing';
if ($args{name}) {
$args{domain_name} = $args{name};
delete $args{name};
}
my $uid;
if ( ref $args{args} ) {
$args{args}->{uid} = $args{args}->{id_owner}
if !exists $args{args}->{uid};
$uid = $args{args}->{uid} if exists $args{args}->{uid};
$args{at_time} = $args{args}->{at} if exists $args{args}->{at};
my $id_domain_args = $args{args}->{id_domain};
......@@ -572,7 +577,11 @@ sub _new_request {
." WHERE id=?");
$sth->execute($self->{id});
return $self->open($self->{id});
my $request = $self->open($self->{id});
$request->status('requested');
return $request;
}
sub _last_insert_id {
......@@ -668,6 +677,7 @@ sub _send_message {
$uid = $self->args('id_owner') if $self->defined_arg('id_owner');
$uid = $self->args('uid') if !$uid && $self->defined_arg('uid');
return if !$uid;
my $domain_name = $self->defined_arg('name');
......
package Ravada::WebSocket;
use warnings;
use strict;
use Data::Dumper;
use Hash::Util qw( lock_hash unlock_hash);
use Moose;
no warnings "experimental::signatures";
use feature qw(signatures);
has clients => (
is => 'ro'
,isa => 'HashRef'
,default => sub { return {}}
);
has ravada => (
is => 'ro'
,isa => 'Ravada::Front'
,required => 1
);
my %SUB = (
list_alerts => \&_list_alerts
,list_machines => \&_list_machines
,list_requests => \&_list_requests
,machine_info => \&_get_machine_info
);
######################################################################
sub _list_alerts($rvd, $args) {
my $login = $args->{login} or die "Error: no login arg ".Dumper($args);
my $user = Ravada::Auth::SQL->new(name => $login) or die "Error: uknown user $login";
my $ret_old = $args->{ret};
my @ret = map { $_->{time} = time; $_ } $user->unshown_messages();
my @ret2=();
my %new;
for my $alert (@ret) {
my $cmd_machine = $alert->{subject};
$cmd_machine =~ s{(.*?\s.*?)\s+.*}{$1};
$new{$cmd_machine}++;
}
for my $alert (@$ret_old) {
my $cmd_machine = $alert->{subject};
$cmd_machine =~ s{(.*?\s.*?)\s+.*}{$1};
push @ret2,($alert) if time - $alert->{time} < 10
&& $cmd_machine && !$new{$cmd_machine};
}
return [@ret2,@ret];
}
sub _list_machines($rvd, $args) {
my $login = $args->{login} or die "Error: no login arg ".Dumper($args);
my $user = Ravada::Auth::SQL->new(name => $login)
or die "Error: uknown user $login";
return []
unless (
$user->can_list_machines
|| $user->can_list_own_machines()
|| $user->can_list_clones()
|| $user->can_list_clones_from_own_base()
|| $user->is_admin()
);
return $rvd->list_machines($user);
}
sub _list_requests($rvd, $args) {
my $login = $args->{login} or die "Error: no login arg ".Dumper($args);
my $user = Ravada::Auth::SQL->new(name => $login) or die "Error: uknown user $login";
return [] unless $user->is_operator || $user->is_admin;
return $rvd->list_requests;
}
sub _get_machine_info($rvd, $args) {
my ($id_domain) = $args->{channel} =~ m{/(\d+)};
my $domain = $rvd->search_domain_by_id($id_domain) or do {
warn "Error: domain $id_domain not found.";
return {};
};
my $login = $args->{login} or die "Error: no login arg ".Dumper($args);
my $user = Ravada::Auth::SQL->new(name => $login) or die "Error: uknown user $login";
my $info = $domain->info($user);
if ($info->{is_active} && !$info->{ip}) {
Ravada::Request->refresh_machine(id_domain => $info->{id}, uid => $user->id);
}
return $info;
}
sub _different_list($list1, $list2) {
return 1 if scalar(@$list1) != scalar (@$list2);
for my $i (0 .. scalar(@$list1)-1) {
my $h1 = $list1->[$i];
my $h2 = $list2->[$i];
return 1 if _different($h1, $h2);
}
return 0;
}
sub _different_hash($h1,$h2) {
return 1 if keys %$h1 != keys %$h2;
for my $key (keys %$h1) {
next if !defined $h1->{$key} && !defined $h2->{$key};
if (!exists $h2->{$key}
|| !defined $h1->{$key} && defined $h2->{$key}
|| defined $h1->{$key} && !defined $h2->{$key}
|| _different($h1->{$key}, $h2->{$key})) {
return 1;
}
}
return 0;
}
sub _different($var1, $var2) {
return 1 if ref($var1) ne ref($var2);
return _different_list($var1, $var2) if ref($var1) eq 'ARRAY';
return _different_hash($var1, $var2) if ref($var1) eq 'HASH';
return $var1 ne $var2;
}
sub BUILD {
my $self = shift;
Mojo::IOLoop->recurring(1 => sub {
for my $key ( keys %{$self->clients} ) {
my $ws_client = $self->clients->{$key}->{ws};
my $channel = $self->clients->{$key}->{channel};
$channel =~ s{/.*}{};
my $exec = $SUB{$channel} or die "Error: unknown channel $channel";
my $ret = $exec->($self->ravada, $self->clients->{$key});
my $old_ret = $self->clients->{$key}->{ret};
if ( _different($ret, $old_ret )) {
$ws_client->send( { json => $ret } );
$self->clients->{$key}->{ret} = $ret;
}
}
});
}
sub subscribe($self, %args) {
my $ws = $args{ws};
$self->clients->{$ws} = {
ws => $ws
, %args
, ret => []
};
}
sub unsubscribe($self, $ws) {
delete $self->clients->{$ws};
}
1;
......@@ -119,7 +119,6 @@ ravadaApp.directive("solShowMachine", swMach)
$http.get('/pingbackend.json').then(function(response) {
$scope.pingbe_fail = !response.data;
});
$scope.getMachines = function() {
if( $scope.check_netdata && $scope.check_netdata != "0" ) {
var url = $scope.check_netdata;
$scope.check_netdata = 0;
......@@ -138,62 +137,76 @@ ravadaApp.directive("solShowMachine", swMach)
$http.get("/session/monitoring/0");
});
}
if(!$scope.modalOpened){
if ($scope.list_machines_busy) {
return ;
}
$scope.list_machines_busy = true;
$http.get("/requests.json").then(function(response) {
$scope.requests=response.data;
$scope.download_done=false;
$scope.download_working =false;
for (var i = 0; i < $scope.requests.length; i++){
if ( $scope.requests[i].command == 'download') {
if ($scope.requests[i].status == 'done') {
$scope.download_done=true;
} else {
$scope.download_working=true;
$scope.subscribe_all=function(url) {
subscribe_list_machines(url);
subscribe_list_requests(url);
};
subscribe_list_machines= function(url) {
var ws = new WebSocket(url);
ws.onopen = function (event) { ws.send('list_machines') };
ws.onmessage = function (event) {
var data = JSON.parse(event.data);
$scope.$apply(function () {
$scope.list_machines = [];
var mach;
for (var i=0, iLength = data.length; i<iLength; i++){
mach = data[i];
if (!mach.id_base){
$scope.list_machines[mach.id] = mach;
$scope.list_machines[mach.id].childs = [];
}
}
}
}
});
$http.get("/list_machines.json").then(function(response) {
$scope.list_machines_busy = false;
$scope.list_machines = [];
var mach;
for (var i=0, iLength = response.data.length; i<iLength; i++){
mach = response.data[i];
if (!mach.id_base){
$scope.list_machines[mach.id] = mach;
$scope.list_machines[mach.id].childs = [];
}
}
$scope.n_clones = 0;
for (var i=0, iLength = response.data.length; i<iLength; i++){
mach = response.data[i];
if (mach.id_base){
$scope.list_machines[mach.id_base].childs.push(mach);
$scope.n_clones++;
}
}
for (var i = $scope.list_machines.length-1; i >= 0; i--){
if (!$scope.list_machines[i]){
$scope.list_machines.splice(i,1);
}
}
if ($scope.auto_hide_clones) {
$scope.hide_clones = 0;
if ($scope.n_clones > $scope.n_clones_hide ) {
$scope.hide_clones = 1;
}
$scope.n_clones = 0;
for (var i=0, iLength = data.length; i<iLength; i++){
mach = data[i];
if (mach.id_base){
$scope.list_machines[mach.id_base].childs.push(mach);
$scope.n_clones++;
}
}
if ($scope.auto_hide_clones) {
$scope.hide_clones = 0;
if ($scope.n_clones > $scope.n_clones_hide ) {
$scope.hide_clones = 1;
}
}
for (var i = $scope.list_machines.length-1; i >= 0; i--){
if (!$scope.list_machines[i]){
$scope.list_machines.splice(i,1);
}
mach = $scope.list_machines[i];
if (!mach.id_base && typeof $scope.show_clones[mach.id] == 'undefined') {
$scope.show_clones[mach.id] = !$scope.hide_clones;
}
}
});
}
}
,function (error){;
$scope.list_machines_busy = false;
}
subscribe_list_requests = function(url) {
$scope.show_requests = false;
var ws = new WebSocket(url);
ws.onopen = function (event) { ws.send('list_requests') };
ws.onmessage = function (event) {
var data = JSON.parse(event.data);
$scope.$apply(function () {
$scope.requests= data;
$scope.download_done=false;
$scope.download_working =false;
for (var i = 0; i < $scope.requests.length; i++){
if ( $scope.requests[i].command == 'download') {
if ($scope.requests[i].status == 'done') {
$scope.download_done=true;
} else {
$scope.download_working=true;
}
}
}
});
}
);
}
};
$scope.orderParam = ['name'];
$scope.auto_hide_clones = true;
$scope.orderMachineList = function(type1,type2){
......@@ -204,10 +217,17 @@ ravadaApp.directive("solShowMachine", swMach)
else $scope.orderParam = [type1,'-'+type2];
}
$scope.hide_clones = true;
$scope.hideClones = function(){
$scope.hide_clones = !$scope.hide_clones;
$scope.auto_hide_clones = false;
}
$scope.showClones = function(value){
$scope.auto_hide_clones = false;
$scope.hide_clones = !value;
for (var i = $scope.list_machines.length-1; i >= 0; i--){
mach = $scope.list_machines[i];
if (!mach.id_base) {
$scope.show_clones[mach.id] = value;
}
}
}
$scope.request = function(request, args) {
$http.post('/request/'+request+'/'
,JSON.stringify(args)
......@@ -219,7 +239,6 @@ ravadaApp.directive("solShowMachine", swMach)
$scope.action = function(target,action,machineId){
$http.get('/'+target+'/'+action+'/'+machineId+'.json')
.then(function() {
$scope.getMachines();
});
};
$scope.set_autostart= function(machineId, value) {
......@@ -264,8 +283,7 @@ ravadaApp.directive("solShowMachine", swMach)
$scope.rename= {new_name: 'new_name'};
$scope.show_rename = false;
$scope.new_name_duplicated=false;
$scope.getMachines();
$interval($scope.getMachines,3000);
$scope.show_clones = {};
};
function usersPageC($scope, $http, $interval, request) {
......@@ -390,8 +408,6 @@ ravadaApp.directive("solShowMachine", swMach)
$scope.backend = response.data[0];
});
$scope.validate_node_name = function() {
console.log($scope.name);
$http.get('/node/exists/'+$scope.name)
.then(duplicated_callback, unique_callback);
......
......@@ -160,6 +160,31 @@
};
function singleMachinePageC($scope, $http, $interval, request, $location) {
subscribe_machine_info= function(url) {
var ws = new WebSocket(url);
ws.onopen = function(event) { ws.send('machine_info/'+$scope.showmachineId) };
ws.onmessage = function(event) {
var data = JSON.parse(event.data);
$scope.$apply(function () {
$scope.showmachine = data;
});
}
};
subscribe_requests = function(url) {
var ws = new WebSocket(url);
ws.onopen = function(event) { ws.send('list_requests') };
ws.onmessage = function(event) {
var data = JSON.parse(event.data);
$scope.$apply(function () {
$scope.alerts_ws = data;
});
}
};
$scope.subscribe_ws = function(url) {
subscribe_machine_info(url);
subscribe_requests(url);
};
$scope.init = function(id) {
$scope.showmachineId=id;
$http.get('/machine/info/'+$scope.showmachineId+'.json')
......@@ -169,7 +194,6 @@
$scope.new_name=$scope.showmachine.name+"-2";
$scope.validate_new_name($scope.showmachine.name);
}
$scope.refresh_machine();
$scope.init_ldap_access();
$scope.list_ldap_attributes();
$scope.list_interfaces();
......@@ -196,27 +220,10 @@
$http.get('/pingbackend.json').then(function(response) {
$scope.pingbe_fail = !response.data;
});
/* $scope.getSingleMachine = function(){
$http.get("/list_machines.json").then(function(response) {
for (var i=0, iLength=response.data.length; i<iLength; i++) {
if (response.data[i].id == $scope.showmachineId) {
$scope.showmachine = response.data[i];
if (!$scope.new_name) {
$scope.new_name = $scope.showmachine.name;
}
$scope.domain = response.data[i];
return;
}
}
window.location.href = "/admin/machines";
});
};
*/
$scope.machine_info = function(id) {
$http.get('/machine/info/'+$scope.showmachineId+'.json')
.then(function(response) {
$scope.showmachine=response.data;
$scope.list_nodes();
});
};
$scope.remove = function(machineId) {
......@@ -266,7 +273,6 @@
$scope.rename_requested=1;
$http.get('/machine/rename/'+machineId+'/'
+$scope.new_name);
$scope.refresh_machine();
};
$scope.cancel_rename=function(old_name) {
$scope.new_name = old_name;
......@@ -302,22 +308,12 @@
if (! value) {
value_show = false;
}
$scope.add_message("Setting "+$scope.showmachine.name+" "+field+" to "+value_show);