Commit d8c0e8d2 authored by frankiejol's avatar frankiejol
Browse files

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

parents 1a63bdad 0a916d6b
......@@ -3709,6 +3709,60 @@ sub _cmd_set_time($self, $request) {
die "$@ , retry.\n" if $@;
}
sub _migrate_base($self, $domain, $node, $uid, $request) {
my $base = Ravada::Domain->open($domain->id_base);
return if $base->base_in_vm($node->id);
my $req_base = Ravada::Request->set_base_vm(
id_domain => $base->id
, id_vm => $node->id
, uid => $uid
);
$request->after_request($req_base);
die "Base ".$base->name." still not prepared in node ".$node->name.". Retry\n";
}
sub _cmd_migrate($self, $request) {
my $uid = $request->args('uid');
my $id_domain = $request->args('id_domain') or die "ERROR: Missing id_domain";
my $user = Ravada::Auth::SQL->search_by_id($uid);
my $domain = $self->search_domain_by_id($id_domain);
die "Error: user ".$user->name." not allowed to migrate domain ".$domain->name
unless $user->is_operator;
my $node = Ravada::VM->open($request->args('id_node'));
$self->_migrate_base($domain, $node, $uid, $request) if $domain->id_base;
if ($domain->is_active) {
if ($request->defined_arg('shutdown')) {
my @timeout;
@timeout = ( timeout => $request->defined_arg('shutdown_timeout') )
if $request->defined_arg('shutdown_timeout');
my $req_shutdown = Ravada::Request->shutdown_domain(
uid => $uid
,id_domain => $id_domain
,@timeout
);
$request->after_request($req_shutdown->id);
$request->retry(10) if !defined $request->retry();
die "Virtual Machine ".$domain->name." ".$request->retry." is active. Shutting down. Retry.\n";
}
}
$domain->migrate($node, $request);
my @remote_ip;
@remote_ip = ( remote_ip => $request->defined_arg('remote_ip'))
if $request->defined_arg('remote_ip');
$domain->start(user => $user, @remote_ip)
if $request->defined_arg('start');
}
sub _clean_requests($self, $command, $request=undef, $status='requested') {
my $query = "DELETE FROM requests "
." WHERE command=? "
......@@ -4007,6 +4061,7 @@ sub _req_method {
,shutdown_node => \&_cmd_shutdown_node
,start_node => \&_cmd_start_node
,connect_node => \&_cmd_connect_node
,migrate => \&_cmd_migrate
#users
,post_login => \&_cmd_post_login
......
......@@ -438,8 +438,6 @@ sub _start_checks($self, @args) {
$self->_balance_vm();
}
$self->rsync(request => $request) if !$self->is_volatile && !$self->_vm->is_local();
} elsif (!$self->is_local) {
$self->_set_vm($vm_local, 1);
}
$self->_check_free_vm_memory();
#TODO: remove them and make it more general now we have nodes
......@@ -476,6 +474,7 @@ sub _search_already_started($self, $fast = 0) {
next;
}
next if !$domain;
$vm->_add_instance_db($domain->id);
if ( $domain->is_active || $domain->is_hibernated ) {
$self->_set_vm($vm,'force');
$started{$vm->id}++;
......@@ -1556,6 +1555,7 @@ sub info($self, $user) {
,run_timeout => $self->run_timeout
,autostart => $self->autostart
,volatile_clones => $self->volatile_clones
,id_vm => $self->_data('id_vm')
};
for (qw(comment screenshot id_owner shutdown_disconnected)) {
$info->{$_} = $self->_data($_);
......@@ -3906,7 +3906,7 @@ sub rsync($self, @args) {
if ($self->is_base) {
push @files_base,($self->list_files_base);
}
$files = [ $self->list_volumes( device => 'disk'), @files_base ];
$files = [ $self->list_volumes(), @files_base ];
}
$request->status("working") if $request;
......@@ -3970,28 +3970,29 @@ sub _pre_migrate($self, $node, $request = undef) {
$self->_check_equal_storage_pools($node) if $self->_vm->is_active;
$self->_internal_autostart(0);
return if !$self->id_base;
$self->check_status();
confess "ERROR: Active domains can't be migrated" if $self->is_active;
my $base = Ravada::Domain->open($self->id_base);
confess "ERROR: base ".$base->name." not prepared in node ".$node->name
if ( $self->id_base ) {
my $base = Ravada::Domain->open($self->id_base);
confess "ERROR: base ".$base->name." not prepared in node ".$node->name
if !$base->base_in_vm($node->id);
confess "ERROR: base id ".$self->id_base." not found." if !$base;
confess "ERROR: base id ".$self->id_base." not found." if !$base;
for my $file ( $base->list_files_base ) {
next if $node->file_exists($file);
warn "Warning: file not found $file in ".$node->name;
Ravada::Request->set_base_vm(
uid => Ravada::Utils::user_daemon->id
,id_domain => $base->id
,id_vm => $node->id
);
return;
}
for my $file ( $base->list_files_base ) {
next if $node->file_exists($file);
warn "Warning: file not found $file in ".$node->name;
Ravada::Request->set_base_vm(
uid => Ravada::Utils::user_daemon->id
,id_domain => $base->id
,id_vm => $node->id
);
return;
}
$self->_set_base_vm_db($node->id,0) unless $node->is_local;
$self->_set_base_vm_db($node->id,0) unless $node->is_local;
}
$node->_add_instance_db($self->id);
}
......
......@@ -110,6 +110,9 @@ our %VALID_ARG = (
,shutdown_node => { id_node => 1, at => 2 }
,start_node => { id_node => 1, at => 2 }
,connect_node => { backend => 2, hostname => 2, id_node =>2, timeout => 2 }
,migrate => { uid => 1, id_node => 1, id_domain => 1, start => 2, remote_ip => 2
,shutdown => 2, shutdown_timeout => 2
}
#users
,post_login => { user => 1, locale => 2 }
......@@ -161,6 +164,7 @@ our %COMMAND = (
,commands => ['prepare_base','remove_base','set_base_vm','rebase_volumes'
, 'remove_base_vm'
, 'screenshot'
, 'migrate'
]
,priority => 6
}
......
......@@ -226,7 +226,7 @@
});
}
};
$scope.new_node_start = true;
subscribe_nodes = function(url, type) {
var ws = new WebSocket(url);
ws.onopen = function(event) { ws.send('list_nodes/'+type) };
......@@ -234,6 +234,19 @@
var data = JSON.parse(event.data);
$scope.$apply(function () {
$scope.nodes = data;
for (var i = 0; i < $scope.nodes.length; i++) {
if ($scope.new_node) {
if ($scope.new_node.id == $scope.nodes[i].id) {
$scope.new_node = $scope.nodes[i];
return
}
} else {
if ($scope.nodes[i].id == $scope.showmachine.id_vm) {
$scope.new_node = $scope.nodes[i];
return;
}
}
}
});
}
};
......
......@@ -3,6 +3,9 @@
use warnings;
use strict;
no warnings "experimental::signatures";
use feature qw(signatures);
use Data::Dumper;
use Getopt::Long;
use POSIX ":sys_wait_h";
......@@ -39,6 +42,7 @@ my $URL_ISOS;
my $ALL;
my $HIBERNATED;
my $DISCONNECTED;
my $ACTIVE;
my $LIST;
......@@ -48,6 +52,7 @@ my $SHUTDOWN_DOMAIN;
my $REMOVE_DOMAIN;
my $REBASE;
my $RUN_REQUEST;
my $MIGRATE;
my $IMPORT_DOMAIN_OWNER;
......@@ -80,12 +85,14 @@ my $USAGE = "$0 "
." --hibernate machine\n"
." --shutdown machine\n"
." --remove machine\n"
." --migrate node machine1 machine2 ... machineN\n"
."\n"
."Operations modifiers:\n"
." --all : execute on all virtual machines\n"
." For hibernate, it is executed on all the actives\n"
." --hibernated: execute on hibernated machines\n"
." --disconnected: execute on disconnected machines\n"
." --active: execute on active running machines\n"
."\n"
;
......@@ -98,6 +105,7 @@ GetOptions ( help => \$help
,verbose => \$VERBOSE
,rebase => \$REBASE
,'no-fork'=> \$NOFORK
,'active'=> \$ACTIVE
,'start=s' => \$START_DOMAIN
,'config=s'=> \$FILE_CONFIG
,'hibernated'=> \$HIBERNATED
......@@ -119,6 +127,8 @@ GetOptions ( help => \$help
,'add-locale-repository=s' => \$ADD_LOCALE_REPOSITORY
,'run-request=s' => \$RUN_REQUEST
,'migrate=s' => \$MIGRATE
) or exit;
$START = 1 if $DEBUG || $FILE_CONFIG || $NOFORK;
......@@ -154,6 +164,8 @@ $Ravada::DEBUG=1 if $DEBUG;
$Ravada::VERBOSE=1 if $VERBOSE;
$Ravada::CAN_FORK=0 if $NOFORK;
my $RVD_BACK;
###################################################################
###################################################################
......@@ -631,6 +643,65 @@ sub run_request {
."\n";
}
sub rvd_back {
return $RVD_BACK if $RVD_BACK;
$RVD_BACK = Ravada->new(%CONFIG);
return $RVD_BACK;
}
sub list_active_machines {
my @domains = rvd_back->list_domains(active => 1);
if (!@domains) {
die "No active domains\n";
}
return @domains;
}
sub migrate($node_name) {
my $vms = rvd_back->vm();
my ($node ) = grep{ $_->name eq $node_name } @$vms
or die "Error: Node $node_name not found\n"
.Dumper([ map {$_->name} @$vms]);
$node->start() if !$node->is_active;
my @machines;
if ( $ACTIVE ) {
@machines = list_active_machines();
} else {
@machines = @ARGV;
}
if (!scalar(@machines)) {
die "Error: supply machines to migrate:\n"
." rvd_back --migrate=node --active\n"
." rvd_back --migrate=node machine1 machine2 machine3\n";
}
for my $machine (@machines) {
my ($domain, $name, $id_domain);
if (!ref($machine)) {
$domain = rvd_back->search_domain($machine) or do {
warn "Error: machine $machine not found\n";
next;
};
} else {
$domain = $machine;
}
$name = $domain->name;
$id_domain = $domain->id;
if ($domain->_data('id_vm') == $node->id) {
warn "Warning: machine $name already in node $node_name\n";
next;
}
warn "migrate $node_name $name\n";
Ravada::Request->migrate(id_node => $node->id, uid => Ravada::Utils::user_daemon->id
,id_domain => $id_domain
,start => $domain->is_active
,shutdown => 1
);
}
}
sub DESTROY {
}
......@@ -663,6 +734,8 @@ shutdown_domain($SHUTDOWN_DOMAIN, $ALL, $HIBERNATED)
add_locale_repository($ADD_LOCALE_REPOSITORY) if $ADD_LOCALE_REPOSITORY;
run_request($RUN_REQUEST) if $RUN_REQUEST;
migrate($MIGRATE) if $MIGRATE;
}
......
......@@ -830,6 +830,7 @@ sub wait_request {
." ".($req->error or '')) if $debug && (time%5 == 0);
sleep 1;
$done_all = 0;
sleep 1;
} elsif (!$done{$req->id}) {
$t0 = time;
$done{$req->{id}}++;
......@@ -852,7 +853,8 @@ sub wait_request {
next if $skip{$req->command};
if ($req->status ne 'done') {
$done_all = 0;
diag("Waiting for request ".$req->id." ".$req->command);
diag("Waiting for request ".$req->id." ".$req->command)
if $debug && (time%5 == 0);
last;
}
}
......
......@@ -1072,6 +1072,98 @@ sub test_fill_memory($vm, $node, $migrate) {
_remove_clones($base);
}
sub test_migrate($vm, $node) {
my $domain = create_domain($vm);
$domain->migrate($node);
is($domain->_data('id_vm'), $node->id);
is($domain->_vm->id, $node->id);
$domain->start(user_admin);
is($domain->_data('id_vm'), $node->id);
is($domain->_vm->id, $node->id);
is($domain->is_active,1);
my $domain2 = Ravada::Domain->open($domain->id);
is($domain2->is_active,1);
is($domain2->_data('id_vm'), $node->id);
is($domain2->_vm->id, $node->id);
$domain->shutdown_now(user_admin);
start_domain_internal($domain);
my $domain3 = Ravada::Domain->open($domain->id);
$domain3->start(user_admin);
is($domain3->is_active,1);
is($domain3->_data('id_vm'), $node->id);
is($domain3->_vm->id, $node->id);
$domain->remove(user_admin);
}
sub test_check_instances($vm, $node) {
my $domain = create_domain($vm);
$domain->migrate($node);
start_domain_internal($domain);
is($domain->_data('id_vm'), $node->id );
is($domain->_vm->id, $node->id);
my @instances = $domain->list_instances();
is(scalar(@instances),2);
my $sth = connector->dbh->prepare("DELETE FROM domain_instances WHERE id_domain=?");
$sth->execute($domain->id);
my @instances2 = $domain->list_instances();
is(scalar(@instances2),0);
$domain->_data( id_vm => $vm->id );
$domain->_vm($vm);
$domain->start(user_admin);
is($domain->_data('id_vm'), $node->id );
is($domain->_vm->id, $node->id);
my @instances3 = $domain->list_instances();
is(scalar(@instances3),2, "Expecting 2 instances of ".$domain->name);
$domain->remove(user_admin);
}
sub test_migrate_req($vm, $node) {
my $domain = create_domain($vm);
$domain->start(user_admin);
my $req = Ravada::Request->migrate(
id_domain => $domain->id
, id_node => $node->id
, uid => user_admin->id
, start => 1
, shutdown => 1
, shutdown_timeout => 10
, remote_ip => '1.2.2.34'
, retry => 10
);
for ( 1 .. 30 ) {
wait_request( debug => 1, check_error => 0);
is($req->status,'done');
last if !$req->error;
diag($req->status);
diag($req->error." try : ".$req->retry);
sleep 1;
}
is($req->error,'') or exit;
my $domain3 = Ravada::Domain->open($domain->id);
is($domain3->is_active,1);
is($domain3->_data('id_vm'), $node->id);
is($domain3->_vm->id, $node->id);
$domain->remove(user_admin);
}
##################################################################################
clean();
......@@ -1112,6 +1204,10 @@ for my $vm_name ( vm_names() ) {
start_node($node);
test_check_instances($vm, $node);
test_migrate($vm, $node);
test_migrate_req($vm, $node);
test_set_vm_fail($vm, $node);
test_volatile_req_clone($vm, $node);
......
......@@ -280,7 +280,9 @@
<div ng-show="{{machine.is_base}} && !{{machine.is_locked}}"
ng-cloak><span class="badge badge-pill badge-light"><%=l 'This Machine is a base' %></span></div>
</td>
<td class="lgMachNode"></td>
<td class="lgMachToggle"><span
ng-hide="machine.is_base"
class="badge badge-info" title="<%=l 'Node'%>">{{machine.node}}</span></td>
</tr>
<tr ng-show="show_clones[machine.id] && machine.childs_loading ">
<td> <%=l 'Loading ...' %> </td>
......
......@@ -9,6 +9,62 @@
{{showmachine.ip}}
</div>
</div>
<div class="row" ng-show="!nodes">
<div class="col-lg-3 mt-2">
Loading ..
</div>
</div>
<div class="row" ng-show="!showmachine.is_base && nodes.length>1">
<div class="col-lg-3 mt-2">
Node
</div>
<div class="col-lg-3 mt-2">
<select ng-model="new_node"
ng-disabled="pending_request && pending_request.status != 'done'"
ng-options="node.name for node in nodes | orderBy : 'name'">
>
</select>
</div>
</div>
<div class="row"
ng-show="new_node && new_node.id != showmachine.id_vm
&& (!pending_request || pending_request.status == 'done')"
>
<div class="col-lg-3 mt-2">
</div>
<div class="col-lg-9 mt-2 alert alert-warning">
<div>
<input type="checkbox" ng-model="new_node_start"/>
<label for="start"><%=l 'Start after migration' %></label>
</div>
<div ng-show="showmachine.is_active">
This virtual machine is running. It must be shut down before
migrate.
</div>
<div>
Are you sure you want to migrate {{showmachine.name}}
to {{new_node.name}} ?
</div>
<div>
<button type="button" class="btn btn-primary"
ng-click="request('migrate', {
'id_domain': showmachine.id
,'id_node': new_node.id
,'shutdown': 1
,'shutdown_timeout': 20
,'start': new_node_start
,'retry': 10
}); showmachine.id_vm=new_node.id;"
><%=l 'Yes' %> </button>
<button type="button" class="btn btn-danger"
ng-click="new_node=undefined"
><%=l 'No' %></button>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-3 mt-2">
<label class="control-label" for="ram"><%=l 'Max memory (MB)' %></label>
......
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