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

Feature/692 memory ng (#820)

* being able to change max_memory

* being able to change max_memory

* 692 test

* [#692]Added options in frontend - need fixes

* [#692]Fix frontend isues when showing info

* [#692]Fix frontend isues when showing info

* [#692]Improved ui

* [#692]Improved ui fix

* [#692]Improved ui fix

* fix

* wip(frontend): improving machine settings

issue #692

* test(hardware): add and remove mock hardware

issue #692

* feat(frontend): responsieve management of hardware drivers

issue #692

* feat(frontend): normalize feedback and refresh

issue #692

* feat(frontend): show requests status

issue #692

* fix(hardware): sometimes changes didn't refresh

issue #692

* test(backend): check if machine needs restart

* test(backend): more checks for add and remove hardware

issue #692

* test(backend): polished some tests

issue #692

* feat(frontend): better feedback when chaning hardware

issue #692

* feat(frontend): show if machine needs restart

issue #692
parent 33d4e08f
......@@ -1063,6 +1063,7 @@ sub _upgrade_tables {
$self->_upgrade_table('domains','client_status','varchar(32)');
$self->_upgrade_table('domains','client_status_time_checked','int NOT NULL default 0');
$self->_upgrade_table('domains','needs_restart','int not null default 0');
$self->_upgrade_table('domains_network','allowed','int not null default 1');
$self->_upgrade_table('grant_types','enabled','int not null default 1');
......@@ -1335,6 +1336,8 @@ sub create_domain {
};
$request->error($error) if $error;
}
} elsif ($@) {
die $@;
}
return $domain;
}
......@@ -2257,8 +2260,6 @@ sub _cmd_add_hardware {
my $user = Ravada::Auth::SQL->search_by_id($uid);
$hardware = (split(/_/,$hardware))[-1];
$domain->set_controller($hardware, $number);
}
......@@ -2374,6 +2375,7 @@ sub _cmd_set_driver {
if $domain->id_owner != $user->id && !$user->is_admin;
$domain->set_driver_id($request->args('id_option'));
$domain->needs_restart(1) if $domain->is_active;
}
sub _cmd_refresh_storage($self, $request=undef) {
......
......@@ -82,6 +82,11 @@ requires 'hybernate';
requires 'hibernate';
requires 'get_driver';
requires 'get_controller_by_name';
requires 'list_controllers';
requires 'set_controller';
requires 'remove_controller';
##########################################################
has 'domain' => (
......@@ -180,11 +185,16 @@ after 'screenshot' => \&_post_screenshot;
after '_select_domain_db' => \&_post_select_domain_db;
around 'get_info' => \&_around_get_info;
around 'set_max_mem' => \&_around_set_mem;
around 'set_memory' => \&_around_set_mem;
around 'is_active' => \&_around_is_active;
around 'autostart' => \&_around_autostart;
after 'set_controller' => \&_post_change_controller;
after 'remove_controller' => \&_post_change_controller;
##################################################
#
......@@ -518,6 +528,16 @@ sub _around_get_info($orig, $self) {
return $info;
}
sub _around_set_mem($orig, $self, $value) {
my $ret = $self->$orig($value);
if ($self->is_known) {
my $info = decode_json($self->_data('info'));
$info->{memory} = $value;
$self->_data(info => encode_json($info))
}
return $ret;
}
##################################################################################3
sub _init_connector {
......@@ -842,6 +862,7 @@ sub info($self, $user) {
,description => $self->description
,msg_timeout => ( $self->_msg_timeout or undef)
,has_clones => ( $self->has_clones or undef)
,needs_restart => ( $self->needs_restart or 0)
};
if (!$info->{description} && $self->id_base) {
my $base = Ravada::Front::Domain->open($self->id_base);
......@@ -853,6 +874,7 @@ sub info($self, $user) {
$info->{display_ip} = $local_ip;
$info->{display_port} = $local_port;
}
$info->{hardware} = $self->get_controllers();
return $info;
}
......@@ -1405,6 +1427,9 @@ sub _post_shutdown {
);
}
$self->_remove_temporary_machine(@_);
$self->needs_restart(0) if $self->is_known()
&& $self->needs_restart()
&& !$self->is_active;
}
sub _around_is_active($orig, $self) {
......@@ -1419,11 +1444,7 @@ sub _around_is_active($orig, $self) {
$status = 'hibernated' if !$is_active && !$self->is_removed && $self->is_hibernated;
$self->_data(status => $status);
eval {
$self->display(Ravada::Utils::user_daemon()) if $is_active;
};
warn "around_is_active display $@" if $@;
$self->needs_restart(0) if $self->needs_restart() && !$is_active;
return $is_active;
}
......@@ -1582,6 +1603,7 @@ sub _add_iptable {
my $user = $args{user} or confess "ERROR: Missing user";
my $uid = $user->id;
return if !$self->is_active;
my $display = $self->display($user);
my ($local_port) = $display =~ m{\w+://.*:(\d+)};
$self->_remove_iptables( port => $local_port );
......@@ -1934,12 +1956,36 @@ sub _post_rename {
$sth->finish;
}
=head2 get_controller
sub get_controller {}
Calls the method to get the specified controller info
sub set_controller {}
Attributes:
name -> name of the controller type
=cut
sub get_controller {
my $self = shift;
my $name = shift;
my $sub = $self->get_controller_by_name($name);
# my $sub = $GET_CONTROLLER_SUB{$name};
die "I can't get controller $name for domain ".$self->name
if !$sub;
return $sub->($self);
}
sub get_controllers($self) {
my $info;
my %controllers = $self->list_controllers();
for my $name ( sort keys %controllers ) {
$info->{$name} = [$self->get_controller($name)];
}
return $info;
}
sub remove_controller {}
=head2 drivers
List the drivers available for a domain. It may filter for a given type.
......@@ -2081,6 +2127,28 @@ Returns all the drivers if not passwed
=cut
=head2 get_driver_id
Gets the value of a driver
Argument: name
my $driver = $domain->get_driver('video');
=cut
sub get_driver_id($self, $name) {
my $value = $self->get_driver($name);
my $driver_type = $self->drivers($name) or confess "ERROR: Unknown drivers"
." of type '$name'";
for my $option ($driver_type->get_options) {
return $option->{id} if $option->{value} eq $value;
}
return;
}
sub _dbh {
my $self = shift;
_init_connector() if !$CONNECTOR || !$$CONNECTOR;
......@@ -2105,16 +2173,11 @@ Sets a domain option:
=cut
sub set_option($self, $option, $value) {
if ($option eq 'description') {
warn "$option -> $value\n";
$self->description($value);
} elsif ($option eq 'run_timeout') {
$self->run_timeout($value);
} elsif ($option eq 'volatile_clones') {
$self->volatile_clones($value);
} else {
confess "ERROR: Unknown option '$option'";
}
my %valid_option = map { $_ => 1 } qw( description run_timeout volatile_clones id_owner);
die "ERROR: Invalid option '$option'"
if !$valid_option{$option};
return $self->_data($option, $value);
}
=head2 type
......@@ -2257,4 +2320,12 @@ sub _client_connection_status($self, $force=undef) {
return 'disconnected';
}
sub needs_restart($self, $value=undef) {
return $self->_data('needs_restart',$value);
}
sub _post_change_controller {
my $self = shift;
$self->needs_restart(1) if $self->is_active;
}
1;
......@@ -123,14 +123,21 @@ sub list_disks {
return @disks;
}
sub xml_description($self) {
sub xml_description($self, $inactive=0) {
return $self->_data_extra('xml') if !$self->domain && $self->is_known;
confess "ERROR: KVM domain not available" if !$self->domain;
my $xml;
eval {
$xml = $self->domain->get_xml_description();
$self->_data_extra('xml', $xml) if $self->is_known;
my @flags;
@flags = ( Sys::Virt::Domain::XML_INACTIVE ) if $inactive;
$xml = $self->domain->get_xml_description(@flags);
$self->_data_extra('xml', $xml) if $self->is_known
&& ( $inactive
|| !$self->_data_extra('xml')
|| !$self->is_active
);
};
confess $@ if $@ && $@ !~ /libvirt error code: 42/;
if ( $@ ) {
......@@ -139,6 +146,10 @@ sub xml_description($self) {
return $xml;
}
sub xml_description_inactive($self) {
return $self->xml_description(1);
}
=head2 remove_disks
Remove the volume files of the domain
......@@ -527,7 +538,7 @@ sub display {
my ($address) = $graph->getAttribute('listen');
$address = $self->_vm->nat_ip if $self->_vm->nat_ip;
die "ERROR: Machine ".$self->name." is not active\n"
confess "ERROR: Machine ".$self->name." is not active\n"
if !$port && !$self->is_active;
die "Unable to get port for domain ".$self->name." ".$graph->toString
if !$port;
......@@ -1350,7 +1361,7 @@ sub set_driver {
if !$sub;
my $ret = $sub->($self,@_);
$self->xml_description();
$self->xml_description_inactive();
return $ret;
}
......@@ -1543,11 +1554,13 @@ sub set_controller($self, $name, $numero) {
die "I can't get controller $name for domain ".$self->name
if !$sub;
return $sub->($self,$numero);
my $ret = $sub->($self,$numero);
$self->xml_description_inactive();
return $ret;
}
#The only '$tipo' suported right now is 'spicevmc'
sub _set_controller_usb($self,$numero, $tipo="spicevmc") {
my $doc = XML::LibXML->load_xml(string => $self->xml_description);
my $doc = XML::LibXML->load_xml(string => $self->xml_description_inactive);
my ($devices) = $doc->findnodes('/domain/devices');
my $count = 0;
......@@ -1578,16 +1591,17 @@ sub remove_controller($self, $name, $index=0) {
die "I can't get controller $name for domain ".$self->name
if !$sub;
return $sub->($self, $index);
my $ret = $sub->($self, $index);
$self->xml_description_inactive();
return $ret;
}
sub _remove_controller_usb($self, $index) {
my $doc = XML::LibXML->load_xml(string => $self->xml_description);
my $doc = XML::LibXML->load_xml(string => $self->xml_description_inactive);
my ($devices) = $doc->findnodes('/domain/devices');
my $ind=0;
for my $controller ($devices->findnodes('redirdev')) {
if ($controller->getAttribute('bus') eq 'usb'){
$ind++;
if( $ind==$index ){
$devices->removeChild($controller);
$self->_vm->connect if !$self->_vm->vm;
......@@ -1595,8 +1609,11 @@ sub _remove_controller_usb($self, $index) {
$self->domain($new_domain);
return;
}
$ind++;
}
}
die "ERROR: USB controller ".($index+1)." not removed, only ".($ind)." found\n";
}
=head2 pre_remove
......
......@@ -362,6 +362,10 @@ sub _set_default_info {
,state => 'UNKNOWN'
};
$self->_store(info => $info);
my %controllers = $self->list_controllers;
for my $name ( sort keys %controllers) {
$self->set_controller($name,2);
}
}
......@@ -482,4 +486,36 @@ sub autostart {
}
return $self->_value('autostart');
}
sub set_controller {
my ($self, $name, $number) = @_;
my $hardware = $self->_value('hardware');
my $list = ( $hardware->{$name} or [] );
if ($number > $#$list) {
for ( $#$list+1 .. $number-1 ) {
push @$list,("foo ".($_+1));
}
} else {
$#$list = $number-1;
}
$hardware->{$name} = $list;
$self->_store(hardware => $hardware );
}
sub remove_controller {
my ($self, $name, $index) = @_;
my $hardware = $self->_value('hardware');
my $list = ( $hardware->{$name} or [] );
my @list2 ;
for ( 0 .. $#$list ) {
next if $_ == $index;
push @list2, ( $list->[$_]);
}
$hardware->{$name} = \@list2;
$self->_store(hardware => $hardware );
}
1;
......@@ -722,16 +722,15 @@ Returns a list of ruquests : ( id , domain_name, status, error )
=cut
sub list_requests {
my $self = shift;
sub list_requests($self, $id_domain_req=undef, $seconds=120) {
my @now = localtime(time-120);
my @now = localtime(time-$seconds);
$now[4]++;
for (0 .. 4) {
$now[$_] = "0".$now[$_] if length($now[$_])<2;
}
my $time_recent = ($now[5]+=1900)."-".$now[4]."-".$now[3]
." ".$now[2].":".$now[1].":00";
." ".$now[2].":".$now[1].":".$now[0];
my $sth = $CONNECTOR->dbh->prepare(
"SELECT requests.id, command, args, date_changed, requests.status"
." ,requests.error, id_domain ,domains.name as domain"
......@@ -740,7 +739,7 @@ sub list_requests {
." ON requests.id_domain = domains.id"
." WHERE "
." requests.status <> 'done' "
." OR ( command = 'download' AND date_changed >= ?) "
." OR ( date_changed >= ?) "
." ORDER BY date_changed DESC LIMIT 10"
);
$sth->execute($time_recent);
......@@ -757,6 +756,7 @@ sub list_requests {
|| $command eq 'screenshot'
|| $command eq 'hibernate'
|| $command eq 'ping_backend';
next if $id_domain_req && $id_domain != $id_domain_req;
my $args;
$args = decode_json($j_args) if $j_args;
......@@ -772,6 +772,7 @@ sub list_requests {
,domain => $domain
,date => $date
,message => $message
,error => "$time_recent ".$error
};
}
$sth->finish;
......
......@@ -123,5 +123,9 @@ sub spinoff_volumes { confess "TODO" }
sub start { confess "TODO" }
sub get_driver {}
sub get_controller_by_name { }
sub list_controllers {}
sub set_controller {}
sub remove_controller {}
1;
......@@ -6,6 +6,9 @@ use XML::LibXML;
extends 'Ravada::Front::Domain';
no warnings "experimental::signatures";
use feature qw(signatures);
our %GET_CONTROLLER_SUB = (
usb => \&_get_controller_usb
);
......@@ -21,27 +24,18 @@ our %GET_DRIVER_SUB = (
,streaming => \&_get_driver_streaming
);
=head2 get_controller
Calls the method to get the specified controller info
Attributes:
name -> name of the controller type
=cut
sub get_controller {
my $self = shift;
my $name = shift;
my $sub = $GET_CONTROLLER_SUB{$name};
die "I can't get controller $name for domain ".$self->name
if !$sub;
sub get_controller_by_name($self, $name) {
return $GET_CONTROLLER_SUB{$name};
}
return $sub->($self);
sub list_controllers($self) {
return %GET_CONTROLLER_SUB;
}
sub _get_controller_usb {
my $self = shift;
$self->xml_description if !$self->readonly();
my $doc = XML::LibXML->load_xml(string => $self->_data_extra('xml'));
my @ret;
......@@ -66,16 +60,14 @@ Argument: name
=cut
sub get_driver {
my $self = shift;
my $name = shift;
sub get_driver($self, $name) {
my $sub = $GET_DRIVER_SUB{$name};
die "I can't get driver $name for domain ".$self->name
confess "I can't get driver $name for domain ".$self->name
if !$sub;
$self->xml_description if ref($self) !~ /Front/;
$self->xml_description_inactive if ref($self) !~ /Front/;
return $sub->($self);
}
......
......@@ -7,6 +7,10 @@ extends 'Ravada::Front::Domain';
my $DIR_TMP = "/var/tmp/rvd_void";
our %GET_CONTROLLER_SUB = (
'mock' => \&_get_controller_mock
);
sub get_driver {
my $self = shift;
my $name = shift;
......@@ -34,4 +38,20 @@ sub _config_file {
return "$DIR_TMP/".$self->name.".yml";
}
sub list_controllers {
return %GET_CONTROLLER_SUB;
}
sub get_controller_by_name {
my ($self, $name) = @_;
return $GET_CONTROLLER_SUB{$name};
}
sub _get_controller_mock {
my $self = shift;
my $hardware = $self->_value('hardware');
return if !exists $hardware->{mock};
return @{$hardware->{mock}};
}
1;
......@@ -75,6 +75,8 @@ our %VALID_ARG = (
our %CMD_SEND_MESSAGE = map { $_ => 1 }
qw( create start shutdown prepare_base remove remove_base rename_domain screenshot download
autostart_domain hibernate hybernate
change_max_memory change_curr_memory
add_hardware remove_hardware set_driver
);
our $CONNECTOR;
......
......@@ -148,6 +148,16 @@
};
function singleMachinePageC($scope, $http, $interval, request, $location) {
$scope.init = function(id) {
$scope.showmachineId=id;
$http.get('/machine/info/'+$scope.showmachineId+'.json')
.then(function(response) {
$scope.showmachine=response.data;
$scope.new_name=$scope.showmachine.name+"-2";
$scope.validate_new_name($scope.showmachine.name);
$scope.refresh_machine();
});
};
$scope.domain_remove = 0;
$scope.new_name_invalid = false;
$http.get('/pingbackend.json').then(function(response) {
......@@ -215,9 +225,7 @@
$scope.rename_requested=1;
$http.get('/machine/rename/'+machineId+'/'
+$scope.new_name);
$scope.message_rename = 1;
// TODO check previous rename returned ok
window.location.href = "/admin/machines";
$scope.refresh_machine();
};
$scope.cancel_rename=function(old_name) {
$scope.new_name = old_name;
......@@ -249,12 +257,57 @@
else value=0;
$http.get("/machine/public/"+machineId+"/"+value);
};
//On load code
$scope.showmachineId = window.location.pathname.split("/")[3].split(".")[0] || -1 ;
$http.get('/machine/info/'+$scope.showmachineId+'.json').then(function(response) {
$scope.showmachine=response.data;
});
// $scope.showmachineId = window.location.pathname.split("/")[3].split(".")[0] || -1 ;
$scope.refresh_machine = function() {
if(!$scope.showmachine) { return }
$http.get('/machine/requests/'+$scope.showmachine.id+'.json').then(function(response) {
$scope.requests = response.data;
var pending = 0;
for (var i in response.data) {
if(response.data[i].status != 'done') {
pending++;
}
}
$scope.pending_requests = pending;
if ($scope.requests.length) {
setTimeout(function () {
$scope.refresh_machine();
}, 2000);
}
if( pending < $scope.pending_before) {
if($scope.showmachine) {
$scope.init($scope.showmachine.id);
}
setTimeout(function () {
$scope.init($scope.showmachine.id);
}, 2000);
}
console.log("pending: "+$scope.pending_before+" - "+pending);
$scope.pending_before = pending;
});
};
$scope.add_hardware = function(hardware, number) {
$http.get('/machine/hardware/add/'
+$scope.showmachine.id+'/'+hardware+'/'+number).then(function(response) {
$scope.pending_before++;
if (!$scope.requests || !$scope.requests.length) {
$scope.refresh_machine();
}
});
};
$scope.remove_hardware = function(hardware, index) {
$http.get('/machine/hardware/remove/'
+$scope.showmachine.id+'/'+hardware+'/'+index).then(function(response) {
$scope.pending_before++;