This file is indexed.

/usr/sbin/vzmigrate is in vzctl 4.9.4-5.

This file is owned by root:root, with mode 0o755.

The actual contents of the file can be viewed below.

   1
   2
   3
   4
   5
   6
   7
   8
   9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  38
  39
  40
  41
  42
  43
  44
  45
  46
  47
  48
  49
  50
  51
  52
  53
  54
  55
  56
  57
  58
  59
  60
  61
  62
  63
  64
  65
  66
  67
  68
  69
  70
  71
  72
  73
  74
  75
  76
  77
  78
  79
  80
  81
  82
  83
  84
  85
  86
  87
  88
  89
  90
  91
  92
  93
  94
  95
  96
  97
  98
  99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
#!/bin/bash
# Copyright (C) 2000-2015, Parallels, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#
# vzmigrate is used for container migration to another node

LC_ALL=C
export LC_ALL
LANGUAGE=C
export LANGUAGE

ACT_SCRIPTS_SFX="start stop mount umount premount postumount"
SSH_OPTIONS="-o BatchMode=yes"
SCP_OPTIONS=$SSH_OPTIONS
RSYNC_OPTIONS="-aHAX --delete --numeric-ids"
VZCTL=vzctl

online=0
verbose=0
remove_area=1
keep_dst=0
debug=0
times=0
compact=0
snapshot=0
check_only=0
ignore_cpu=0
ignore_ipv6=0
ssh_mux=0
ssh_mux_pid=
ssh_mux_sock=
suspend_opts=
confdir="/etc/vz/conf"
vzconf="/etc/vz/vz.conf"
tmpdir="/var/tmp"
act_scripts=
PLOOP=no
RSYNC1_OPTIONS=
PLOOP_DEV=
TOP_DELTA=
VVVV=

# Errors:
MIG_ERR_USAGE=1
MIG_ERR_VPS_IS_STOPPED=2
MIG_ERR_CANT_CONNECT=4
MIG_ERR_COPY=6
MIG_ERR_START_VPS=7
MIG_ERR_STOP_SOURCE=8
MIG_ERR_EXISTS=9
MIG_ERR_NOEXIST=10
MIG_ERR_IP_IN_USE=12
MIG_ERR_QUOTA=13
MIG_ERR_CHECKPOINT=$MIG_ERR_STOP_SOURCE
MIG_ERR_MOUNT_VPS=$MIG_ERR_START_VPS
MIG_ERR_RESTORE_VPS=$MIG_ERR_START_VPS
MIG_ERR_OVZ_NOT_RUNNING=14
MIG_ERR_APPLY_CONFIG=15
MIG_ERR_UNSUP_PLOOP=16
MIG_ERR_UNSUP_CPT_VER=17
MIG_ERR_UNSUP_CPU=18
MIG_ERR_CANT_READ_REMOTE_CONFIG=19
MIG_ERR_LOCKED=20

# For local vzctl to work, make sure /usr/sbin is in $PATH
if ! echo ":${PATH}:" | fgrep -q ':/usr/sbin:'; then
	PATH="/usr/sbin:$PATH"
fi

usage() {
	cat << EOF
This program is used for container migration to another node.
Usage:
  vzmigrate [option ...] destination_address <CTID>
Options:
-r, --remove-area yes|no
	Whether to remove container on source host after successful migration.
--ssh=<ssh options>
	Additional options that will be passed to ssh while establishing
	connection to destination host. Please be careful with options
	passed, DO NOT pass destination hostname.
--ssh-mux
	Use ssh connection multiplexing (speeds up live migration).
--rsync=<rsync options>
	Additional options that will be passed to rsync.
--keep-dst
	Do not clean synced destination container private area in case of some
	error. It makes sense to use this option on big container migration to
	avoid re-syncing container private area in case some error
	(on container stop for example) occurs during first migration attempt.
-c, --compact
	Compact a container image before migration. Works for ploop only.
-s, --snapshot
	Create a container snapshot before migration. Works for ploop only.
--live
	Perform live migration: instead of restarting a container, checkpoint
	and restore are used, so there is no container downtime or service
	interruption. Additional steps are performed to minimize the time
	when a container is in suspended state.
--check-only, --dry-run
	Do not perform actual migration, stop after preliminary checks.
	This is used to check if a CT can possibly be migrated. Combine
	with --live to enable more checks for live migration case.
-f, --nodeps[=<check>[,<check> ...]]
	Continue migration, ignoring some or all preliminary check failures.
	Particular checks can be ignored by providing an argument to
	--nodeps option. The following options can be used (comma-separated):
	  * cpu		- ignore cpu capabilities check
	  * ipv6	- ignore ipv6 module check
-v
	Verbose mode. Causes vzmigrate to print debugging messages about
	its progress (including some time statistics). Multiple -v options
	increase the verbosity. The maximum is 4.
-t, --times
	At the end of live migration, output various timings for migration
	stages that affect total suspended CT time.

Examples:
	Online migration of CT #101 to foo.com:
		vzmigrate --live foo.com 101
	Migration of CT #102 to foo.com with downtime:
		vzmigrate foo.com 102
Notes:
	This program uses ssh as a transport layer. You need to put ssh
	public key to destination node and be able to connect without
	entering a password.
EOF
	exit 0
}

bad_usage() {
	cat << EOF 2>&1
Invalid usage$*

For command line syntax, please check
  vzmigrate --help
  man vzmigrate
EOF

	exit $MIG_ERR_USAGE
}

# Logs message
# There are 3 types of messages:
# 0 - error messages (print to stderr)
# 1 - normal messages (print to stdout)
# 2 - debug messages (print to stdout if in verbose mode)
log () {
	if [ $1 -eq 0 ]; then
		shift
		echo "Error: $*" >&2
	elif [ $1 -eq 1 ]; then
		shift
		echo "$*"
	elif [ $verbose -gt 0 ]; then
		shift
		echo "   $@"
	fi
}

ignore_msg() {
	log 1 "Ignoring the above error (migration can fail)..."
}

# Executes command and returns result of execution
# There are 2 types of execution:
# 1 - normal execution (all output will be printed)
# 2 - debug execution (output will be printed if verbose mode is set,
#     in other case stdout and stderr redirected to /dev/null)
logexec () {
	if [ $1 -eq 1 -o $verbose -gt 0 ]; then
		shift
		$@
	else
		shift
		$@ >/dev/null 2>&1
	fi
}

lock_ct() {
	local f
	local pid
	local ret

	f=$LOCKDIR/$VEID.lck

	set -C # noclobber, i.e. open with O_EXCL
	if echo $$ > $f; then
		log 1 "Locked CT $VEID"
		ret=0
	elif ! test -f $f; then
		log 0 "Can't lock CT $VEID"
		exit 1
	else
		pid=$(cat $f)
		log 0 "CT $VEID already locked by PID $pid" 1>&2
		test -n "$pid" && logexec 1 ps -f -p $pid
		ret=1
	fi

	set +C
	return $ret
}

undo_ssh_mux() {
	[ -n "$ssh_mux_pid" ] && kill -TERM $ssh_mux_pid
	[ -f "$ssh_mux_sock" ] && rm -f $ssh_mux_sock
}

undo_lock() {
	local f
	local pid

	f=$LOCKDIR/$VEID.lck
	pid=$(cat $f)
	if [ $pid -ne $$ ] ; then
		log 0 "unexpected PID $pid in $f (expecting $$)"
	else
		rm -f $f
	fi
	undo_ssh_mux
}

undo_conf () {
	$SSH "root@$host" "$VZCTL set $VEID --name '' --save > /dev/null"
	$SSH "root@$host" "rm -f $vpsconf"
	undo_lock
}

undo_act_scripts () {
	if [ -n "$act_scripts" ] ; then
		$SSH "root@$host" "rm -f $act_scripts"
	fi
	undo_conf
}

undo_dump_file() {
	if [ -n "$DUMP_FILE" ] ; then
		$SSH "root@$host" "rm -f $DUMPDIR_REMOTE/$DUMP_FILE"
	fi
	undo_act_scripts
}

undo_private () {
	if [ $keep_dst -eq 0 ]; then
		if [ -L "$VE_PRIVATE_REMOTE" ]; then
			$SSH "root@$host" "rm -rf $(readlink -f $VE_PRIVATE_REMOTE) && rm -f $VE_PRIVATE_REMOTE"
		else
			$SSH "root@$host" "rm -rf $VE_PRIVATE_REMOTE"
		fi
	fi
	undo_dump_file
}

undo_root () {
	$SSH "root@$host" "rm -rf $VE_ROOT_REMOTE"
	undo_private
}

undo_quota_init () {
	[ "${DISK_QUOTA}" = 'no' ] || $SSH "root@$host" "vzquota drop $VEID"
	undo_root
}

undo_quota_on () {
	[ "${DISK_QUOTA}" = 'no' ] || $SSH "root@$host" "vzquota off $VEID"
	undo_quota_init
}

undo_sync () {
	# Root will be destroyed in undo_root
	undo_quota_on
}

undo_suspend () {
	logexec 2 $VZCTL_L chkpnt $VEID --resume
	undo_sync
}

undo_dump () {
	if [ $debug -eq 0 ]; then
		rm -f "$VE_DUMPFILE"
	fi
	undo_suspend
}

undo_copy_dump () {
	$SSH "root@$host" "rm -f $VE_DUMPFILE_REMOTE"
	undo_suspend
}

undo_stop () {
	if [ "$state" = "running" ]; then
		$VZCTL_L start $VEID
	elif [ "$mounted" = "mounted" ]; then
		$VZCTL_L mount $VEID
	fi
	undo_sync
}

undo_source_stage() {
	if [ $online -eq 1 ]; then
		undo_copy_dump
	else
		undo_stop
	fi
}

undo_quota_dump () {
	rm -f "$VE_QUOTADUMP"
	undo_source_stage
}

undo_copy_quota () {
	$SSH "root@$host" "rm -f $VE_QUOTADUMP"
	undo_quota_dump
}

undo_undump () {
	logexec 2 $SSH root@$host $VZCTL restore $VEID --kill
	undo_copy_quota
}

get_status() {
	exist=$3
	mounted=$4
	state=$5
	suspended=$6
}

get_time () {
	awk -v t2=$2 -v t1=$1 'BEGIN{print t2-t1}'
}

get_ploop_info() {
	local dev top img root private

	root=$(readlink -f "$VE_ROOT")
	private=$(readlink -f "$VE_PRIVATE")
	dev=$(awk '$2=="'$root'" {print $1}' /proc/mounts | \
		sed -e 's|^/dev/||' -e 's|p1$||') || return 1
	top=$(cat /sys/block/${dev}/pstate/top) || return 1
	img=$(cat /sys/block/${dev}/pdelta/${top}/image) || return 1
	TOP_DELTA=$(echo $img | sed "s|^${private}/||") || return 1
	PLOOP_DEV="${dev}"
}

ploop_copy_old() {
	log 1 "Falling back to non-feedback ploop copy"

	ploop copy -s $dev -F "$cmd 1>&2" | $cat | \
		$SSH "root@$host" ploop copy -d $delta_dst
}

# Copy top delta with write tracker and CT stop/suspend
ploop_copy() {
	local cmd dev delta_src delta_dst cat size err pid port try

	cmd=$*
	dev=/dev/$PLOOP_DEV
	delta_src=$VE_PRIVATE/$TOP_DELTA
	delta_dst=$VE_PRIVATE_REMOTE/$TOP_DELTA
	# Sanity checks
	test -b $dev || return 1
	test -f $delta_src || return 1

	cat=cat
	# Use nice progress bar with pv if we can
	if test $verbose -gt 0 && pv -V >/dev/null 2>&1; then
		size=$(du -b $delta_src | awk '{print $1}')
		test -n "$size" && cat="pv -s $size"
	fi

	# Remove top delta on dst
	$SSH "root@$host" rm -f $delta_dst

	# Check if remote side supports ploop copy with feedback
	err=$($SSH "root@$host" ploop copy -i0 -f1 -d /dev/null \
			</dev/null >/dev/null 2>&1; echo $?)
	if [ $err -eq 38 ]; then
		log 1 "WARNING: ploop tools on $host is old, please update"
		ploop_copy_old
		return
	fi

	# Set up two-way channel for ploop copy with feedback
	try=0
	while [ $try -lt 5 ]; do
		# Try different ports in the range 1024..32767
		port=$(awk 'BEGIN { srand(); print int(rand()*31744) + 1024 }')
		log 1 "Trying port $port"
		$SSH_NOMUX -L 127.0.0.1:$port:127.0.0.1:$port root@$host \
			"vznnc -l -p $port -f 5 --" \
			"ploop $VVVV copy -d $delta_dst -i5 -f5" \
			>/dev/null </dev/zero &
		pid=$!
		sleep 1

		# check if ssh is still there
		kill -0 $pid 2>/dev/null && break
		let try=try+1
	done

	if [ $try = 5 ]; then
		log 0 "Failed to setup remote ploop copy"
		ploop_copy_old
		return
	fi

	if ! vznnc -c -p $port -f 5 -- \
			ploop $VVVV copy -s $dev -F "$cmd 1>&2" -o5 -f5; then
		log 0 "ploop copy -s $dev failed"
		kill -TERM $pid 2>/dev/null
		return 1
	fi

	return 0
}

print_times() {
	local fmt='  %20s: %6.2f\n'
	local dt_susp_dump dt_pcopy

	[ $times -eq 0 ] && return

	echo
	dt_susp_dump=$(get_time $time_suspend $time_copy_dump)
	if [ "$PLOOP" = "yes" ]; then
		dt_pcopy=$(get_time $time_suspend $time_pcopy)
		dt_susp_dump=$(get_time $dt_pcopy $dt_susp_dump)
	fi
	printf "$fmt" "Suspend + Dump" $dt_susp_dump
	if [ "$PLOOP" = "yes" ]; then
		printf "$fmt" "Pcopy after suspend" $dt_pcopy
	fi
	printf "$fmt" "Copy dump file" $(get_time $time_copy_dump $time_rsync2)
	if [ "$PLOOP" != "yes" ]; then
		printf "$fmt" "Second rsync" $(get_time $time_rsync2 $time_quota)
		printf "$fmt" "2nd level quota" $(get_time $time_quota $time_undump)
	fi
	printf "$fmt" "Undump + Resume" $(get_time $time_undump $time_finish)
	printf "  %20s  ------\n" " "
	printf "$fmt" "Total suspended time" $(get_time $time_suspend $time_finish)
	echo
}

check_cpt_props() {
	local version
	local id

	log 2 "Checking for CPT version compatibility"
	version=$(vzcptcheck version)
	if [ $? -ne 0 -o "$version" = "" ]; then
		log 1 "Warning: can't get local CPT version, skipping check"
	elif ! logexec 1 $SSH root@$host vzcptcheck version $version; then
		log 0 "CPT version check failed on destination node!"
		log 0 "Destination node kernel is too old, please upgrade"
		log 0 "Can't continue live migration"
		undo_lock
		exit $MIG_ERR_UNSUP_CPT_VER
	fi

	test "$state" = "running" && id=$VEID

	log 2 "Checking for CPU flags compatibility"
	CPU_CAPS=$($SSH root@$host vzcptcheck caps)
	if [ $? -ne 0 -o "$CPU_CAPS" = "" ]; then
		log 1 "Warning: can't get remote CPU caps, skipping check"
	elif ! logexec 1 vzcptcheck caps $id $CPU_CAPS; then
		log 0 "CPU capabilities check failed!"
		log 0 "Destination node CPU is not compatible"
		if [ $ignore_cpu -eq 1 ]; then
			ignore_msg
		else
			log 0 "Can't continue live migration"
			undo_lock
			exit $MIG_ERR_UNSUP_CPU
		fi
	fi
}

parse_nodeps() {
	local opt arg args

	opt=$1
	args=$(echo $2 | sed -e 's/^=//' -e 's/,/ /g')

	if [ -z "$args" ]; then
		ignore_cpu=1
		ignore_ipv6=1
		return
	fi

	for arg in $args; do
		case $arg in
			cpu|cpu_check)
				ignore_cpu=1
				;;
			ipv6)
				ignore_ipv6=1
				;;
			all)
				ignore_cpu=1
				ignore_ipv6=1
				;;
			*)
				bad_usage ": unknown $opt argument: $arg"
				;;
		esac
	done
}

[ $# = 0 ] && usage

OPTS=$(getopt -n 'vzmigrate' -o vr:csf::th \
	--longoptions live,online,remove-area:,keep-dst \
	--longoptions compact,snapshot,check-only,dry-run \
	--longoptions nodeps::,ssh:,rsync:,times,ssh-mux,help,usage \
	-- "$@")

[ $? -eq 0 ] || bad_usage

eval set -- "$OPTS"

while true; do
	case "$1" in
	--live|--online)
		online=1
		;;
	-v)
		verbose=$((verbose+1)) # can just be 'let verbose++' in bash
		;;
	--remove-area|-r)
		opt=$1
		shift
		if [ "$1" = "yes" ]; then
			remove_area=1
		elif [ "$1" = "no" ]; then
			remove_area=0
		else
			bad_usage ": bad argument for $opt: $1"
		fi
		;;
	--keep-dst)
		keep_dst=1
		;;
	-c|--compact)
		compact=1
		;;
	-s|--snapshot)
		snapshot=1
		;;
	--check-only|--dry-run)
		check_only=1
		;;
	-f|--nodeps)
		opt=$1
		shift
		parse_nodeps $opt $1
		;;
	--ssh)
		shift
		SSH_OPTIONS="$SSH_OPTIONS $1"
		SCP_OPTIONS="`echo $SSH_OPTIONS | sed 's/-p/-P/1'`"
		;;
	--rsync)
		shift
		RSYNC_OPTIONS="$RSYNC_OPTIONS $1"
		;;
	--times|-t)
		times=1
		;;
	--ssh-mux)
		ssh_mux=1
		;;
	-h|--help|--usage)
		usage
		;;
	--)
		shift
		break
		;;
	*)
		bad_usage ": unknown option $1"
		;;
	esac
	shift
done

if [ $verbose -gt 0 ]; then
	times=1
fi
if [ $verbose -gt 1 ]; then
	VVVV="-vvvv"	# for ploop copy
	RSYNC_OPTIONS="$RSYNC_OPTIONS -v"
	VZCTL="$VZCTL --verbose"
fi
if [ $verbose -gt 2 ]; then
	RSYNC_OPTIONS="$RSYNC_OPTIONS -v"
	VZCTL="$VZCTL --verbose"
fi
if [ $verbose -gt 3 ]; then
	RSYNC_OPTIONS="$RSYNC_OPTIONS -v"
	VZCTL="$VZCTL --verbose"
	SSH_OPTIONS="$SSH_OPTIONS -v"
	SCP_OPTIONS="$SCP_OPTIONS -v"
fi

[ $# -lt 2 ] && bad_usage ": not enough parameters"
[ $# -gt 2 ] && bad_usage ": too many parameters: $@"

host=$1
shift
VEID=$1
shift

# Support CT names as well
if echo $VEID | egrep -qv '^[[:digit:]]+$'; then
	VEID=$(vzlist -o ctid -H $VEID | tr -d ' ')
	if [ -z "$VEID" ]; then
		# Error message is printed by vzlist to stderr
		exit $MIG_ERR_NOEXIST
	fi
fi
vpsconf="$confdir/$VEID.conf"

SSH_NOMUX="ssh $SSH_OPTIONS"
if [ $ssh_mux -eq 1 ]; then
	ssh_mux_sock=/tmp/.vzmigrate-ssh-$$-$host-$VEID
	SSH_OPTIONS="$SSH_OPTIONS -o ControlPath=$ssh_mux_sock"
	SCP_OPTIONS="$SCP_OPTIONS -o ControlPath=$ssh_mux_sock"
fi
SSH="ssh $SSH_OPTIONS"
SCP="scp $SCP_OPTIONS"

RSYNC="rsync $RSYNC_OPTIONS"
export RSYNC_RSH="$SSH"
VZCTL_L="$VZCTL --skiplock"

if [ ! -r "$vzconf" ]; then
	log 0 "Can't read global config file $vzconf"
	exit $MIG_ERR_NOEXIST
fi

log 2 "Loading $vzconf"
. "$vzconf"
# Lock the container so no one else can fuss with it
lock_ct || exit $MIG_ERR_LOCKED

get_status $($VZCTL_L --quiet status $VEID)
if [ "$exist" = "deleted" ]; then
	log 0 "CT #$VEID doesn't exist"
	undo_lock
	exit $MIG_ERR_NOEXIST
fi

if [ $online -eq 1 -a "$state" != "running" ]; then
	log 0 "Can't perform live migration of a stopped container"
	undo_lock
	exit $MIG_ERR_VPS_IS_STOPPED
fi

S="Starting"
M="migration"
[ $check_only -eq 1 ] && S="Checking"
[ $online -eq 1 ] && M="live migration"
log 1 "$S $M of CT $VEID to $host"
unset S M

# Set up master SSH channel
if [ $ssh_mux -eq 1 ]; then
	$SSH -M root@$host 'while :; do echo; sleep 5; done' \
		</dev/null >/dev/null 2>&1 &
	ssh_mux_pid=$!
fi

# Try to connect to destination
if ! logexec 2 $SSH root@$host /bin/true; then
	log 0 "Can't connect to destination address using public key"
	log 0 "Please put your public key to destination node"
	undo_lock
	exit $MIG_ERR_CANT_CONNECT
fi

# Check if OpenVZ is running
if ! logexec 2 $SSH root@$host /etc/init.d/vz status ; then
	log 0 "OpenVZ is not running on the target machine"
	log 0 "Can't continue migration"
	undo_lock
	exit $MIG_ERR_OVZ_NOT_RUNNING
fi

# Check if CPT modules are loaded for live migration
if [ $online -eq 1 ]; then
	if [ ! -f /proc/cpt ]; then
		log 0 "vzcpt module is not loaded on the source node"
		log 0 "Can't continue live migration"
		undo_lock
		exit $MIG_ERR_OVZ_NOT_RUNNING
	fi
	if ! logexec 2 $SSH root@$host "test -f /proc/rst";
	then
		log 0 "vzrst module is not loaded on the destination node"
		log 0 "Can't continue live migration"
		undo_lock
		exit $MIG_ERR_OVZ_NOT_RUNNING
	fi
fi

dst_exist=$($SSH "root@$host" "$VZCTL --quiet status $VEID" | awk '{print $3}')
if [ "$dst_exist" = "exist" ]; then
	log 0 "CT #$VEID already exists on destination node"
	undo_lock
	exit $MIG_ERR_EXISTS
fi

if [ $online -eq 1 -o "$suspended" = "suspended" ]; then
	if ! which vzcptcheck >/dev/null; then
		log 1 "Warning: no vzcptcheck binary on local node,"
		log 1 "skipping CPT version and CPU caps checks"
	elif ! logexec 1 $SSH root@$host which vzcptcheck >/dev/null; then
		log 1 "Warning: no vzcptcheck binary on destination node,"
		log 1 "skipping CPT version and CPU caps checks"
	else
		check_cpt_props
	fi
	# Check ipv6 kernel module
	if test -d /sys/module/ipv6; then
		if ! $SSH "root@$host" test -d /sys/module/ipv6; then
			log 0 "Module ipv6 is not loaded on the destination node"
			if [ $ignore_ipv6 -eq 1 ]; then
				ignore_msg
			else
				undo_lock
				exit $MIG_ERR_OVZ_NOT_RUNNING
			fi
		fi
	fi
fi

log 2 "Loading $vpsconf"
. "$vpsconf"
VE_QUOTADUMP="$tmpdir/quotadump.$VEID"

log 2 "Getting remote VE_ROOT and VE_PRIVATE"
eval $($SSH root@$host "sh -c 'VEID='$VEID'; . /etc/vz/vz.conf && \
	echo VE_ROOT_REMOTE=\$VE_ROOT && \
	echo VE_PRIVATE_REMOTE=\$VE_PRIVATE && \
	echo DUMPDIR_REMOTE=\$DUMPDIR'")
if [ -z "$VE_ROOT_REMOTE" -o -z "$VE_PRIVATE_REMOTE" ]; then
	log 0 "Failed to get remote VE_ROOT/VE_PRIVATE"
	undo_lock
	exit $MIG_ERR_CANT_READ_REMOTE_CONFIG
fi

if [ $VE_PRIVATE != $VE_PRIVATE_REMOTE ]; then
	log 1 "Remote VE_PRIVATE path differs from local $VEID.conf"
	log 1 "Using $VE_PRIVATE_REMOTE for syncing"
fi
if [ $VE_ROOT != $VE_ROOT_REMOTE ]; then
	log 1 "Remote VE_ROOT path differs from local $VEID.conf"
	log 1 "Using $VE_ROOT_REMOTE for syncing"
fi

test -z "$DUMPDIR" && DUMPDIR=/var/lib/vz/dump
test -z "$DUMPDIR_REMOTE" && DUMPDIR=/var/lib/vz/dump
VE_DUMPFILE_SFX="tmp-dump.$VEID"
VE_DUMPFILE="$DUMPDIR/$VE_DUMPFILE_SFX"
VE_DUMPFILE_REMOTE="$DUMPDIR_REMOTE/$VE_DUMPFILE_SFX"

DDXML=$VE_PRIVATE/root.hdd/DiskDescriptor.xml
if [ -f $DDXML ]; then
	PLOOP=yes
	# Disable vzquota operations for ploop CT
	DISK_QUOTA=no
	DDXML_REMOTE=$VE_PRIVATE_REMOTE/root.hdd/DiskDescriptor.xml
fi

if [ "$PLOOP" = "yes" ]; then
	log 2 "Checking if ploop is supported on destination node"
	if ! logexec 2 $SSH "root@$host" "ploop getdev >/dev/null" ; then
		log 0 "Destination node does not support ploop, can't migrate"
		undo_lock
		exit $MIG_ERR_UNSUP_PLOOP
	fi
else
	# non-ploop case: can use sparse rsync, can't do compact/snapshot
	RSYNC1_OPTIONS="--sparse"
	compact=0
	snapshot=0
fi

# Check that IP_ADDRESSes are not in use on dest
# remove extra spaces and netmasks
IP_X=$(echo $IP_ADDRESS " " | sed 's@/[0-9][0-9]*[[:space:]]@ @g')
# Only do the check if we have IP(s)
if echo $IP_X | grep -q '[0-9a-f]'; then
	log 2 "Checking IPs on destination node: $IP_X"
	# remove extra spaces, replace spaces with |
	IP_X=$(echo $IP_X | sed 's/ /|/g')
	# do check
	if [ $($SSH "root@$host" "grep -cwE \"$IP_X\" /proc/vz/veip") -gt 0 ]; then
		log 0 "IP address(es) already in use on destination node"
		undo_lock
		exit $MIG_ERR_IP_IN_USE
	fi
fi

if [ $check_only -eq 1 ]; then
	undo_lock
	exit 0
fi

log 1 "Preparing remote node"

log 2 "Copying config file"
if ! logexec 2 $SCP $vpsconf root@$host:$vpsconf ; then
	log 0 "Failed to copy config file"
	undo_lock
	exit $MIG_ERR_COPY
fi

logexec 2 $SSH root@$host $VZCTL set $VEID --private $VE_PRIVATE_REMOTE --root $VE_ROOT_REMOTE --save

logexec 2 $SSH root@$host $VZCTL set $VEID --applyconfig_map name --save
RET=$?
# vzctl return code 20 or 21 in case of unrecognized option
if [ $RET != 20 ] && [ $RET != 21 ] && [ $RET != 0 ]; then
	log 0 "Failed to apply config on destination node"
	undo_conf
	exit $MIG_ERR_APPLY_CONFIG
fi

for sfx in $ACT_SCRIPTS_SFX; do
	file="$confdir/$VEID.$sfx"
	if [ -f "$file" ]; then
		act_scripts="$act_scripts $file"
	fi
done
if [ -n "$act_scripts" ]; then
	log 2 "Copying action scripts"
	if ! logexec 2 $SCP $act_scripts root@$host:$confdir ; then
		log 0 "Failed to copy action scripts"
		undo_conf
		exit $MIG_ERR_COPY
	fi
fi

if [ "$suspended" = "suspended" ]; then
	DUMP_FILE=Dump.$VEID
	log 2 "Copying dump file"
	if ! logexec 2 $SCP $DUMPDIR/$DUMP_FILE root@$host:$DUMPDIR_REMOTE/ ; then
		log 0 "Failed to copy dump file"
		undo_act_scripts
		exit $MIG_ERR_COPY
	fi
fi

log 2 "Creating remote container root dir"
if ! $SSH "root@$host" "mkdir -p $VE_ROOT_REMOTE"; then
	log 0 "Failed to make container root directory"
	undo_dump_file
	exit $MIG_ERR_COPY
fi

log 2 "Creating remote container private dir"

# if VE_PRIVATE is a symlink, create VE_PRIVATE_REMOTE as a symlink
if [ -L "$VE_PRIVATE" ]; then
	VE_PRIVATE_TARGET=$(readlink -f "$VE_PRIVATE")

	if ! $SSH "root@$host" "mkdir -p $VE_PRIVATE_TARGET && ln -s $VE_PRIVATE_TARGET $VE_PRIVATE_REMOTE"; then
		log 0 "Failed to make+symlink private area directory"
		undo_private
		exit $MIG_ERR_COPY
	fi
fi

if ! $SSH "root@$host" "mkdir -p $VE_PRIVATE_REMOTE"; then
	log 0 "Failed to make container private area directory"
	undo_private
	exit $MIG_ERR_COPY
fi

if [ "${DISK_QUOTA}" != "no" ]; then
	log 1 "Initializing remote quota"

	log 2 "Quota init"
	if ! $SSH "root@$host" "$VZCTL quotainit $VEID"; then
		log 0 "Failed to initialize quota"
		undo_root
		exit $MIG_ERR_QUOTA
	fi

	log 2 "Turning remote quota on"
	if ! $SSH "root@$host" "$VZCTL quotaon $VEID"; then
		log 0 "Failed to turn quota on"
		undo_quota_init
		exit $MIG_ERR_QUOTA
	fi
fi

if [ "$compact" -eq 1 ]; then
	log 1 "Compacting container image"
	if ! logexec 2 $VZCTL_L compact $VEID ; then
		log 0 "Failed to compact container image"
		undo_root
	fi
fi

if [ "$snapshot" -eq 1 ]; then
	log 1 "Creating a container snapshot"
	if ! logexec 2 $VZCTL_L snapshot $VEID ; then
		log 0 "Failed to snapshot a container"
		undo_root
	fi
fi

if [ "$PLOOP" = "yes" -a "$state" = "running" ]; then
	# Online ploop migration: exclude top delta
	if ! get_ploop_info; then
		log 0 "Can't get ploop information"
		undo_root
		exit $MIG_ERR_COPY
	fi
	RSYNC1_OPTIONS="--exclude $TOP_DELTA"
fi

log 1 "Syncing private"
$RSYNC $RSYNC1_OPTIONS "$VE_PRIVATE/" "root@$host:$VE_PRIVATE_REMOTE/"
RET=$?
# Ignore rsync error 24 "Partial transfer due to vanished source files"
if [ $RET != 24 ] && [ $RET != 0 ]; then
	log 0 "Failed to sync container private areas"
	undo_quota_on
	exit $MIG_ERR_COPY
fi

if [ "$PLOOP" = "yes" ]; then
	TOP_UUID='{5fbaabe3-6958-40ff-92a7-860e329aab41}'
	vzfsync_files=$($SSH "root@$host" ploop snapshot-list \
		-u $TOP_UUID -H -o fname $DDXML_REMOTE | sed 1d)
	if [ -n "$vzfsync_files" ]; then
		if $SSH root@$host "vzfsync >/dev/null 2>&1"; then
			log 2 "Calling vzfsync on ploop deltas"
			if ! logexec 1 $SSH root@$host "vzfsync \
				--datasync --dontneed $vzfsync_files"
			then
				log 1 "Error from vzfsync, but moving on"
			fi
		else
			log 1 "Warning: no vzfsync on $host," \
				"skipping deltas fsync"
			log 1 "Please upgrade vzctl on $host"
		fi
	fi
fi

if [ $online -eq 1 ]; then

	log 1 "Live migrating container..."

	if [ -n "$CPU_CAPS" ]; then
		suspend_opts="$suspend_opts --flags $CPU_CAPS"
	fi

	if [ "$PLOOP" != "yes" ]; then
		time_suspend=$(date +%s.%N)
		log 2 "Suspending container"
		if ! logexec 2 $VZCTL_L chkpnt $VEID --suspend $suspend_opts; then
			log 0 "Failed to suspend container"
			undo_sync
			exit $MIG_ERR_CHECKPOINT
		fi
	else
		log 2 "Copying top ploop delta with CT suspend"
		TMPF=$(mktemp)
		if ! ploop_copy "date '+%s.%N' > $TMPF; $VZCTL_L chkpnt $VEID --suspend $suspend_opts"; then
			log 0 "Failed to copy top ploop delta"
			$VZCTL_L chkpnt $VEID --resume 2>/dev/null
			rm -f $TMPF
			undo_sync
			exit $MIG_ERR_COPY
		fi
		time_pcopy=$(date +%s.%N)
		time_suspend=$(cat $TMPF)
		rm $TMPF
	fi

	log 2 "Dumping container"
	if ! logexec 2 $VZCTL_L chkpnt $VEID --dump --dumpfile $VE_DUMPFILE ; then
		log 0 "Failed to dump container"
		undo_suspend
		exit $MIG_ERR_CHECKPOINT
	fi

	log 2 "Copying dumpfile"
	time_copy_dump=$(date +%s.%N)
	if ! logexec 2 $SCP $VE_DUMPFILE root@$host:$VE_DUMPFILE_REMOTE ; then
		log 0 "Failed to copy dump"
		undo_dump
		exit $MIG_ERR_COPY
	fi
else
	if [ "$state" = "running" ]; then
		if [ "$PLOOP" != "yes" ]; then
			log 1 "Stopping container"
			if ! logexec 2 $VZCTL_L stop $VEID ; then
				log 0 "Failed to stop container"
				undo_sync
				exit $MIG_ERR_STOP_SOURCE
			fi
		else
			log 1 "Copying top ploop delta with CT stop"
			if ! ploop_copy "$VZCTL_L stop $VEID --skip-umount"; then
				log 0 "Failed to copy top ploop delta"
				$VZCTL_L start $VEID 2>/dev/null
				undo_sync
				exit $MIG_ERR_COPY
			fi
			log 1 "Unmounting container"
			if ! logexec 2 $VZCTL_L umount $VEID ; then
				log 0 "Failed to umount container"
				$VZCTL_L start $VEID 2>/dev/null
				undo_sync
				exit $MIG_ERR_STOP_SOURCE
			fi
		fi
	elif [ "$mounted" = "mounted" ]; then
		log 1 "Unmounting container"
		if ! logexec 2 $VZCTL_L umount $VEID ; then
			log 0 "Failed to umount container"
			undo_sync
			exit $MIG_ERR_STOP_SOURCE
		fi
	fi
fi

time_rsync2=$(date +%s.%N)
if [ "$state" = "running" -a "$PLOOP" != "yes" ]; then
	log 2 "Syncing private (2nd pass)"
	if ! $RSYNC "$VE_PRIVATE/" "root@$host:$VE_PRIVATE_REMOTE/"; then
		log 0 "Failed to sync container private areas"
		undo_source_stage
		exit $MIG_ERR_COPY
	fi
fi

time_quota=$(date +%s.%N)
if [ "${DISK_QUOTA}" != "no" ]; then
	log 1 "Syncing 2nd level quota"

	log 2 "Dumping 2nd level quota"
	if ! vzdqdump $VEID -U -G -T -F > "$VE_QUOTADUMP"; then
		log 0 "Failed to dump 2nd level quota"
		undo_quota_dump
		exit $MIG_ERR_QUOTA
	fi

	log 2 "Copying 2nd level quota"
	if ! logexec 2 $SCP $VE_QUOTADUMP root@$host:$VE_QUOTADUMP ; then
		log 0 "Failed to copy 2nd level quota dump"
		undo_quota_dump
		exit $MIG_ERR_COPY
	fi

	log 2 "Loading 2nd level quota"
	if ! $SSH "root@$host" "(vzdqload $VEID -U -G -T -F < $VE_QUOTADUMP &&
			vzquota reload2 $VEID)"; then
		log 0 "Failed to load 2nd level quota"
		undo_copy_quota
		exit $MIG_ERR_QUOTA
	fi
fi

if [ $online -eq 1 ]; then
	log 2 "Undumping container"
	time_undump=$(date +%s.%N)
	if ! logexec 2 $SSH root@$host $VZCTL restore $VEID --undump \
			--dumpfile $VE_DUMPFILE_REMOTE --skip_arpdetect ; then
		log 0 "Failed to undump container"
		undo_copy_quota
		exit $MIG_ERR_RESTORE_VPS
	fi

	log 2 "Resuming container"
	if ! logexec 2 $SSH root@$host $VZCTL restore $VEID --resume ; then
		log 0 "Failed to resume container"
		undo_undump
		exit $MIG_ERR_RESTORE_VPS
	fi
	time_finish=$(date +%s.%N)
	print_times
	log 1 "Cleaning up"

	log 2 "Killing container"
	logexec 2 $VZCTL_L chkpnt $VEID --kill
	logexec 2 $VZCTL_L umount $VEID

	log 2 "Removing dumpfiles"
	rm -f "$VE_DUMPFILE"
	$SSH "root@$host" "rm -f $VE_DUMPFILE_REMOTE"
else
	if [ "$state" = "running" ]; then
		log 1 "Starting container"
		if ! logexec 2 $SSH root@$host $VZCTL start $VEID ; then
			log 0 "Failed to start container"
			undo_copy_quota
			exit $MIG_ERR_START_VPS
		fi
	elif [ "$mounted" = "mounted" ]; then
		log 1 "Mounting container"
		if ! logexec 2 $SSH root@$host $VZCTL mount $VEID ; then
			log 0 "Failed to mount container"
			undo_copy_quota
			exit $MIG_ERR_MOUNT_VPS
		fi
	elif [ "${DISK_QUOTA}" != "no" ]; then
		log 1 "Turning quota off"
		if ! logexec 2 $SSH root@$host vzquota off $VEID ; then
			log 0 "failed to turn quota off"
			undo_copy_quota
			exit $MIG_ERR_QUOTA
		fi
	fi

	log 1 "Cleaning up"
fi

if [ $remove_area -eq 1 ]; then
	log 2 "Destroying container"
	logexec 2 $VZCTL_L destroy $VEID
else
	# Move config as veid.migrated to allow backward migration
	mv -f $vpsconf $vpsconf.migrated
fi

undo_lock
exit 0