]>
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 if ($line =~ m/^ip: '($PVE::Tools::IPRE)'$/) {
107 my ($self, $func, @param) = @_;
110 local $SIG{INT
} = $SIG{TERM
} = $SIG{QUIT
} = $SIG{HUP
} = sub {
111 $self->{delayed_interrupt
} = 0;
112 die "interrupted by signal\n";
114 local $SIG{PIPE
} = sub {
115 $self->{delayed_interrupt
} = 0;
116 die "interrupted by signal\n";
119 my $di = $self->{delayed_interrupt
};
120 $self->{delayed_interrupt
} = 0;
122 die "interrupted by signal\n" if $di;
124 &$func($self, @param);
128 my @ssh_opts = ('-o', 'BatchMode=yes');
129 my @ssh_cmd = ('/usr/bin/ssh', @ssh_opts);
130 my @scp_cmd = ('/usr/bin/scp', @ssh_opts);
131 my @rsync_opts = ('-aHAX', '--delete', '--numeric-ids');
132 my @rsync_cmd = ('/usr/bin/rsync', @rsync_opts);
135 my ($class, $node, $nodeip, $vmid, $opts) = @_;
137 $class = ref($class) || $class;
140 delayed_interrupt
=> 0,
145 rsync_cmd
=> [ @rsync_cmd ],
146 rem_ssh
=> [ @ssh_cmd, "root\@$nodeip" ],
147 scp_cmd
=> [ @scp_cmd ],
150 $self = bless $self, $class;
152 my $starttime = time();
154 local $ENV{RSYNC_RSH
} = join(' ', @ssh_cmd);
156 local $SIG{INT
} = $SIG{TERM
} = $SIG{QUIT
} = $SIG{HUP
} = $SIG{PIPE
} = sub {
157 $self->log('err', "received interrupt - delayed");
158 $self->{delayed_interrupt
} = 1;
161 local $ENV{RSYNC_RSH
} = join(' ', @ssh_cmd);
163 # lock container during migration
164 eval { $self->lock_vm($self->{vmid
}, sub {
166 $self->{running
} = 0;
167 &$eval_int($self, sub { $self->{running
} = $self->prepare($self->{vmid
}); });
170 # get dedicated migration address from remote node, if set.
171 # as a side effect this checks also if the other node can be accessed
172 # through ssh and that it has quorum
173 my $remote_migration_ip = $self->get_remote_migration_ip();
175 if (defined($remote_migration_ip)) {
176 $nodeip = $remote_migration_ip;
177 $self->{nodeip
} = $remote_migration_ip;
178 $self->{rem_ssh
} = [ @ssh_cmd, "root\@$nodeip" ];
180 $self->log('info', "use dedicated network address for sending " .
181 "migration traffic ($self->{nodeip})");
183 # test if we can connect to new IP
184 my $cmd = [ @{$self->{rem_ssh
}}, '/bin/true' ];
185 eval { $self->cmd_quiet($cmd); };
186 die "Can't connect to destination address ($self->{nodeip}) using " .
187 "public key authentication\n" if $@;
190 &$eval_int($self, sub { $self->phase1($self->{vmid
}); });
193 $self->log('err', $err);
194 eval { $self->phase1_cleanup($self->{vmid
}, $err); };
195 if (my $tmperr = $@) {
196 $self->log('err', $tmperr);
198 eval { $self->final_cleanup($self->{vmid
}); };
199 if (my $tmperr = $@) {
200 $self->log('err', $tmperr);
205 # vm is now owned by other node
206 # Note: there is no VM config file on the local node anymore
208 if ($self->{running
}) {
210 &$eval_int($self, sub { $self->phase2($self->{vmid
}); });
214 $self->log('err', "online migrate failure - $phase2err");
216 eval { $self->phase2_cleanup($self->{vmid
}, $phase2err); };
218 $self->log('err', $err);
224 &$eval_int($self, sub { $self->phase3($self->{vmid
}); });
227 $self->log('err', $phase3err);
230 eval { $self->phase3_cleanup($self->{vmid
}, $phase3err); };
232 $self->log('err', $err);
235 eval { $self->final_cleanup($self->{vmid
}); };
237 $self->log('err', $err);
244 my $delay = time() - $starttime;
245 my $mins = int($delay/60);
246 my $secs = $delay - $mins*60;
247 my $hours = int($mins/60);
248 $mins = $mins - $hours*60;
250 my $duration = sprintf "%02d:%02d:%02d", $hours, $mins, $secs;
253 $self->log('err', "migration aborted (duration $duration): $err");
254 die "migration aborted\n";
257 if ($self->{errors
}) {
258 $self->log('err', "migration finished with problems (duration $duration)");
259 die "migration problems\n"
262 $self->log('info', "migration finished successfully (duration $duration)");
266 my ($self, $vmid, $code, @param) = @_;
268 die "abstract method - implement me";
272 my ($self, $vmid) = @_;
274 die "abstract method - implement me";
279 # transfer all data and move VM config files
281 my ($self, $vmid) = @_;
282 die "abstract method - implement me";
285 # only called if there are errors in phase1
287 my ($self, $vmid, $err) = @_;
288 die "abstract method - implement me";
291 # only called when VM is running and phase1 was successful
293 my ($self, $vmid) = @_;
294 die "abstract method - implement me";
297 # only called when VM is running and phase1 was successful
299 my ($self, $vmid, $err) = @_;
302 # only called when phase1 was successful
304 my ($self, $vmid) = @_;
307 # only called when phase1 was successful
309 my ($self, $vmid, $err) = @_;
312 # final cleanup - always called
314 my ($self, $vmid) = @_;
315 die "abstract method - implement me";