]>
git.proxmox.com Git - pve-common.git/blob - src/PVE/AbstractMigrate.pm
1 package PVE
::AbstractMigrate
;
5 use POSIX
qw(strftime);
9 my ($level, $msg) = @_;
17 my $tstr = strftime
("%b %d %H:%M:%S", localtime);
19 foreach my $line (split (/\n/, $msg)) {
20 if ($level eq 'err') {
21 $res .= "$tstr ERROR: $line\n";
23 $res .= "$tstr $line\n";
31 my ($self, $level, $msg) = @_;
37 print &$msg2text($level, $msg);
41 my ($self, $cmd, %param) = @_;
45 $self->log('info', $line);
48 $self->log('info', "# " . PVE
::Tools
::cmd2string
($cmd));
50 PVE
::Tools
::run_command
($cmd, %param, outfunc
=> $logfunc, errfunc
=> $logfunc);
53 my $run_command_quiet_full = sub {
54 my ($self, $cmd, $logerr, %param) = @_;
59 $log .= &$msg2text('info', $line);;
62 eval { PVE
::Tools
::run_command
($cmd, %param, outfunc
=> $logfunc, errfunc
=> $logfunc); };
64 $self->log('info', "# " . PVE
::Tools
::cmd2string
($cmd));
68 $self->log('err', $err);
76 my ($self, $cmd, %param) = @_;
77 return &$run_command_quiet_full($self, $cmd, 0, %param);
81 my ($self, $cmd, %param) = @_;
82 return &$run_command_quiet_full($self, $cmd, 1, %param);
85 sub get_remote_migration_ip
{
90 my $cmd = [@{$self->{rem_ssh
}}, 'pvecm', 'mtunnel', '--get_migration_ip'];
92 push @$cmd, '--migration_network', $self->{opts
}->{migration_network
}
93 if defined($self->{opts
}->{migration_network
});
95 PVE
::Tools
::run_command
($cmd, outfunc
=> sub {
98 # use non-restrictive regex for ip, its already checked by the remote side
99 if ($line =~ m/^ip: '(\S+)'$/) {
108 my ($self, $func, @param) = @_;
111 local $SIG{INT
} = $SIG{TERM
} = $SIG{QUIT
} = $SIG{HUP
} = sub {
112 $self->{delayed_interrupt
} = 0;
113 die "interrupted by signal\n";
115 local $SIG{PIPE
} = sub {
116 $self->{delayed_interrupt
} = 0;
117 die "interrupted by signal\n";
120 my $di = $self->{delayed_interrupt
};
121 $self->{delayed_interrupt
} = 0;
123 die "interrupted by signal\n" if $di;
125 &$func($self, @param);
129 my @ssh_opts = ('-o', 'BatchMode=yes');
130 my @ssh_cmd = ('/usr/bin/ssh', @ssh_opts);
131 my @scp_cmd = ('/usr/bin/scp', @ssh_opts);
132 my @rsync_opts = ('-aHAX', '--delete', '--numeric-ids');
133 my @rsync_cmd = ('/usr/bin/rsync', @rsync_opts);
136 my ($class, $node, $nodeip, $vmid, $opts) = @_;
138 $class = ref($class) || $class;
141 delayed_interrupt
=> 0,
146 rsync_cmd
=> [ @rsync_cmd ],
147 rem_ssh
=> [ @ssh_cmd, "root\@$nodeip" ],
148 scp_cmd
=> [ @scp_cmd ],
151 $self = bless $self, $class;
153 my $starttime = time();
155 local $ENV{RSYNC_RSH
} = join(' ', @ssh_cmd);
157 local $SIG{INT
} = $SIG{TERM
} = $SIG{QUIT
} = $SIG{HUP
} = $SIG{PIPE
} = sub {
158 $self->log('err', "received interrupt - delayed");
159 $self->{delayed_interrupt
} = 1;
162 local $ENV{RSYNC_RSH
} = join(' ', @ssh_cmd);
164 # lock container during migration
165 eval { $self->lock_vm($self->{vmid
}, sub {
167 $self->{running
} = 0;
168 &$eval_int($self, sub { $self->{running
} = $self->prepare($self->{vmid
}); });
171 # get dedicated migration address from remote node, if set.
172 # as a side effect this checks also if the other node can be accessed
173 # through ssh and that it has quorum
174 my $remote_migration_ip = $self->get_remote_migration_ip();
176 if (defined($remote_migration_ip)) {
177 $nodeip = $remote_migration_ip;
178 $self->{nodeip
} = $remote_migration_ip;
179 $self->{rem_ssh
} = [ @ssh_cmd, "root\@$nodeip" ];
181 $self->log('info', "use dedicated network address for sending " .
182 "migration traffic ($self->{nodeip})");
184 # test if we can connect to new IP
185 my $cmd = [ @{$self->{rem_ssh
}}, '/bin/true' ];
186 eval { $self->cmd_quiet($cmd); };
187 die "Can't connect to destination address ($self->{nodeip}) using " .
188 "public key authentication\n" if $@;
191 &$eval_int($self, sub { $self->phase1($self->{vmid
}); });
194 $self->log('err', $err);
195 eval { $self->phase1_cleanup($self->{vmid
}, $err); };
196 if (my $tmperr = $@) {
197 $self->log('err', $tmperr);
199 eval { $self->final_cleanup($self->{vmid
}); };
200 if (my $tmperr = $@) {
201 $self->log('err', $tmperr);
206 # vm is now owned by other node
207 # Note: there is no VM config file on the local node anymore
209 if ($self->{running
}) {
211 &$eval_int($self, sub { $self->phase2($self->{vmid
}); });
215 $self->log('err', "online migrate failure - $phase2err");
217 eval { $self->phase2_cleanup($self->{vmid
}, $phase2err); };
219 $self->log('err', $err);
225 &$eval_int($self, sub { $self->phase3($self->{vmid
}); });
228 $self->log('err', $phase3err);
231 eval { $self->phase3_cleanup($self->{vmid
}, $phase3err); };
233 $self->log('err', $err);
236 eval { $self->final_cleanup($self->{vmid
}); };
238 $self->log('err', $err);
245 my $delay = time() - $starttime;
246 my $mins = int($delay/60);
247 my $secs = $delay - $mins*60;
248 my $hours = int($mins/60);
249 $mins = $mins - $hours*60;
251 my $duration = sprintf "%02d:%02d:%02d", $hours, $mins, $secs;
254 $self->log('err', "migration aborted (duration $duration): $err");
255 die "migration aborted\n";
258 if ($self->{errors
}) {
259 $self->log('err', "migration finished with problems (duration $duration)");
260 die "migration problems\n"
263 $self->log('info', "migration finished successfully (duration $duration)");
267 my ($self, $vmid, $code, @param) = @_;
269 die "abstract method - implement me";
273 my ($self, $vmid) = @_;
275 die "abstract method - implement me";
280 # transfer all data and move VM config files
282 my ($self, $vmid) = @_;
283 die "abstract method - implement me";
286 # only called if there are errors in phase1
288 my ($self, $vmid, $err) = @_;
289 die "abstract method - implement me";
292 # only called when VM is running and phase1 was successful
294 my ($self, $vmid) = @_;
295 die "abstract method - implement me";
298 # only called when VM is running and phase1 was successful
300 my ($self, $vmid, $err) = @_;
303 # only called when phase1 was successful
305 my ($self, $vmid) = @_;
308 # only called when phase1 was successful
310 my ($self, $vmid, $err) = @_;
313 # final cleanup - always called
315 my ($self, $vmid) = @_;
316 die "abstract method - implement me";