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

Refactor frontend web service (#1212)

* refactor(frontend): websockets admin and list bases
* feature(frontend): warn admin about configure proxy
* refactor(frontend): show/hide requests

issue #1196
parent de197da0
......@@ -2410,11 +2410,10 @@ sub _do_execute_command {
$err =~ s/(.*?)retry.?/$1/i;
$request->error($err) if $err;
}
} else {
}
$request->status('done')
if $request->status() ne 'done'
&& $request->status() !~ /^retry/i;
}
}
sub _cmd_manage_pools($self, $request) {
......
......@@ -585,14 +585,21 @@ Returns a reference to a list of the ISOs known by the system
=cut
sub iso_file ($self, $vm_type) {
my $cache = $self->_cache_get("list_isos");
return $cache if $cache;
my $req = Ravada::Request->list_isos(
vm_type => $vm_type
);
return [] if !$req;
$self->wait_request($req);
return [] if $req->status ne 'done';
my $isos = decode_json($req->output());
$self->_cache_store("list_isos",$isos);
return $isos;
}
......@@ -706,8 +713,6 @@ Return true if alive, false otherwise.
sub ping_backend {
my $self = shift;
return 1 if $self->_ping_backend_localhost();
my $req = Ravada::Request->ping_backend();
$self->wait_request($req, 2);
......@@ -1098,6 +1103,22 @@ sub add_node($self,%arg) {
return $req->id;
}
sub _cache_store($self, $key, $value, $timeout=60) {
$self->{cache}->{$key} = [ $value, time+$timeout ];
}
sub _cache_get($self, $key) {
delete $self->{cache}->{$key}
if exists $self->{cache}->{$key}
&& $self->{cache}->{$key}->[1] < time;
return if !exists $self->{cache}->{$key};
return $self->{cache}->{$key}->[0];
}
sub list_network_interfaces($self, %args) {
my $vm_type = delete $args{vm_type}or confess "Error: missing vm_type";
......
......@@ -553,7 +553,7 @@ sub _new_request {
$args{args} = encode_json($args{args});
}
_init_connector() if !$CONNECTOR || !$$CONNECTOR;
if ($args{command} =~ /^(clone|manage_pools)$/) {
if ($args{command} =~ /^(clone|manage_pools|list_isos)$/) {
if ( _duplicated_request($args{command}, $args{args})
|| ( $args{command} ne 'clone' && done_recently(undef, 60, $args{command}))) {
# warn "Warning: duplicated request for $args{command} $args{args}";
......@@ -678,6 +678,11 @@ sub _send_message {
$uid = $self->args('id_owner') if $self->defined_arg('id_owner');
$uid = $self->args('uid') if !$uid && $self->defined_arg('uid');
if (!$uid) {
my $user = $self->defined_arg('user');
$uid = $user->id if $user;
}
return if !$uid;
my $domain_name = $self->defined_arg('name');
......
......@@ -10,6 +10,7 @@ use Moose;
no warnings "experimental::signatures";
use feature qw(signatures);
my $DEBUG=0;
has clients => (
is => 'ro'
......@@ -25,9 +26,14 @@ has ravada => (
my %SUB = (
list_alerts => \&_list_alerts
,list_isos => \&_list_isos
,list_machines => \&_list_machines
,list_machines_user => \&_list_machines_user
,list_bases_anonymous => \&_list_bases_anonymous
,list_requests => \&_list_requests
,machine_info => \&_get_machine_info
,ping_backend => \&_ping_backend
,request => \&_request
);
######################################################################
......@@ -59,6 +65,19 @@ sub _list_alerts($rvd, $args) {
return [@ret2,@ret];
}
sub _list_isos($rvd, $args) {
my ($type) = $args->{channel} =~ m{/(.*)};
$type = 'KVM' if !defined $type;
return $rvd->iso_file($type);
}
sub _request($rvd, $args) {
my ($id_request) = $args->{channel} =~ m{/(.*)};
my $req = Ravada::Request->open($id_request);
return {status => $req->status, error => $req->error};
}
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)
......@@ -74,6 +93,20 @@ sub _list_machines($rvd, $args) {
return $rvd->list_machines($user);
}
sub _list_machines_user($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 $rvd->list_machines_user($user)
}
sub _list_bases_anonymous($rvd, $args) {
my $remote_ip = $args->{remote_ip} or die "Error: no remote_ip arg ".Dumper($args);
return $rvd->list_bases_anonymous($remote_ip);
}
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";
......@@ -100,6 +133,18 @@ sub _get_machine_info($rvd, $args) {
return $info;
}
sub _ping_backend($rvd, $args) {
my $requests = $rvd->list_requests(undef, 120, 1);
return 1 if !scalar(@$requests);
warn Dumper($requests);
my @requests2 = grep { $_->{status} ne 'requested' } @$requests;
return 0 if !scalar(@requests2) && grep { $_->{command} eq 'ping_backend'} @$requests;
warn "***".Dumper(map { [ $_->{command} => $_->{status} ] } @requests2);
return scalar (@requests2);
}
sub _different_list($list1, $list2) {
return 1 if scalar(@$list1) != scalar (@$list2);
for my $i (0 .. scalar(@$list1)-1) {
......@@ -143,6 +188,7 @@ sub BUILD {
my $ret = $exec->($self->ravada, $self->clients->{$key});
my $old_ret = $self->clients->{$key}->{ret};
if ( _different($ret, $old_ret )) {
warn "WS: send $channel" if $DEBUG;
$ws_client->send( { json => $ret } );
$self->clients->{$key}->{ret} = $ret;
}
......@@ -153,6 +199,9 @@ sub BUILD {
sub subscribe($self, %args) {
my $ws = $args{ws};
my %args2 = %args;
delete $args2{ws};
warn "Subscribe ".Dumper(\%args2) if $DEBUG;
$self->clients->{$ws} = {
ws => $ws
, %args
......
......@@ -115,11 +115,10 @@ ravadaApp.directive("solShowMachine", swMach)
});
};
function machinesPageC($scope, $http, $interval, request, listMach) {
function machinesPageC($scope, $http, $interval, $timeout, request, listMach) {
$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,88 @@ 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) {
ws_connected = false;
$timeout(function() {
if (!ws_connected) {
$scope.ws_fail = true;
}
}, 5 * 1000 );
var ws = new WebSocket(url);
ws.onopen = function (event) {
ws_connected = true ;
$scope.ws_fail = false;
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 = [];
}
}
$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;
}
}
});
}
});
$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;
}
}
}
,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 +229,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 +251,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 +295,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 +420,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);
......
......@@ -107,16 +107,28 @@
}
};
ws_connected = false;
$timeout(function() {
if (!ws_connected) {
$scope.ws_fail = true;
}
}, 5 * 1000 );
$scope.list_machines_user = function() {
var seconds = 1000;
if ($scope.refresh <= 0) {
var url = '/list_machines_user.json';
if ($scope.anonymous) {
url = '/list_bases_anonymous.json';
}
$http.get(url).then(function(response) {
$scope.machines = response.data;
$scope.subscribe_list_machines_user = function(url) {
var channel = 'list_machines_user';
if ($scope.anonymous) {
channel = 'list_bases_anonymous';
}
var ws = new WebSocket(url);
ws.onopen = function(event) {
$scope.ws_fail = false;
ws_connected = true;
ws.send(channel);
};
ws.onmessage = function(event) {
var data = JSON.parse(event.data);
$scope.$apply(function () {
$scope.machines = data;
$scope.public_bases = 0;
$scope.private_bases = 0;
for (var i = 0; i < $scope.machines.length; i++) {
......@@ -126,29 +138,27 @@
$scope.private_bases++;
}
}
}, function error(response) {
console.log(response.status);
});
$scope.refresh = 5;
} else {
$scope.refresh--;
}
$timeout(function() {
$scope.list_machines_user();
}, seconds);
};
$url_list = "/list_bases.json";
if ( typeof $_anonymous !== 'undefined' && $_anonymous ) {
$url_list = "/list_bases_anonymous.json";
}
$http.get($url_list).then(function(response) {
$scope.list_bases= response.data;
});
subscribe_ping_backend= function(url) {
var ws = new WebSocket(url);
ws.onopen = function(event) { ws.send('ping_backend') };
ws.onmessage = function(event) {
var data = JSON.parse(event.data);
$scope.$apply(function () {
$scope.pingbe_fail = !data;
});
}
};
$scope.subscribe_ws = function(url) {
subscribe_list_machines_user(url);
subscribe_ping_backend(url);
};
$http.get('/pingbackend.json').then(function(response) {
$scope.pingbe_fail = !response.data;
});
$scope.only_public = false;
$scope.toggle_only_public=function() {
......@@ -160,8 +170,51 @@
};
function singleMachinePageC($scope, $http, $interval, request, $location) {
$scope.init = function(id) {
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;
});
}
};
subscribe_isos = function(url) {
var ws = new WebSocket(url);
ws.onopen = function(event) { ws.send('list_isos') };
ws.onmessage = function(event) {
var data = JSON.parse(event.data);
$scope.$apply(function () {
$scope.list_isos = data;
});
}
};
subscribe_ws = function(url) {
subscribe_machine_info(url);
subscribe_requests(url);
subscribe_isos(url);
};
var url_ws;
$scope.init = function(id, url) {
url_ws = url;
$scope.showmachineId=id;
subscribe_ws(url_ws);
$http.get('/machine/info/'+$scope.showmachineId+'.json')
.then(function(response) {
$scope.showmachine=response.data;
......@@ -169,15 +222,15 @@
$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();
list_interfaces();
$scope.hardware_types = Object.keys(response.data.hardware);
$scope.copy_ram = $scope.showmachine.max_mem / 1024 / 1024;
});
};
$scope.list_interfaces = function() {
list_interfaces = function() {
if (! $scope.network_nats) {
$http.get('/network/interfaces/'+$scope.showmachine.type+'/nat')
.then(function(response) {
......@@ -196,27 +249,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) {
......@@ -261,12 +297,29 @@
}
};
subscribe_request = function(id_request, action) {
var ws = new WebSocket(url_ws);
ws.onopen = function(event) { ws.send('request/'+id_request) };
ws.onmessage = function(event) {
var data = JSON.parse(event.data);
action(data);
}
};
$scope.rename = function(machineId, old_name) {
if ($scope.new_name_duplicated || $scope.new_name_invalid) return;
$scope.rename_requested=1;
$scope.rename_request= { 'status': 'requested' };
$http.get('/machine/rename/'+machineId+'/'
+$scope.new_name);
$scope.refresh_machine();
+$scope.new_name).then(function(response) {
subscribe_request(response.data.req, function(data) {
$scope.$apply(function () {
$scope.rename_request=data;
});
});
});
};
$scope.cancel_rename=function(old_name) {
$scope.new_name = old_name;
......@@ -302,22 +355,12 @@