X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=pve-zsync;h=2503117235bbc5bb3540ba69147aa33c77faf63d;hb=dfd3d834246a5c58d5bda69bec112ba970f51a78;hp=d517ece39fef9902e3550d142b1a7f509900996a;hpb=4818684706fdbbbe9914fa826106064827ff96e9;p=pve-zsync.git diff --git a/pve-zsync b/pve-zsync index d517ece..2503117 100644 --- a/pve-zsync +++ b/pve-zsync @@ -7,7 +7,6 @@ use Fcntl qw(:flock SEEK_END); use Getopt::Long qw(GetOptionsFromArray); use File::Copy qw(move); use File::Path qw(make_path); -use Switch; use JSON; use IO::File; use String::ShellQuote 'shell_quote'; @@ -52,6 +51,11 @@ check_bin ('zfs'); check_bin ('ssh'); check_bin ('scp'); +$SIG{TERM} = $SIG{QUIT} = $SIG{PIPE} = $SIG{HUP} = $SIG{KILL} = $SIG{INT} = + sub { + die "Signal aborting sync\n"; + }; + sub check_bin { my ($bin) = @_; @@ -66,21 +70,33 @@ sub check_bin { } sub cut_target_width { - my ($target, $max) = @_; + my ($path, $maxlen) = @_; + $path =~ s@/+@/@g; - return $target if (length($target) <= $max); - my @spl = split('/', $target); + return $path if length($path) <= $maxlen; - my $count = length($spl[@spl-1]); - return "..\/".substr($spl[@spl-1],($count-$max)+3 , $count) if $count > $max; + return '..'.substr($path, -$maxlen+2) if $path !~ m@/@; - $count += length($spl[0]) if @spl > 1; - return substr($spl[0], 0, $max-4-length($spl[@spl-1]))."\/..\/".$spl[@spl-1] if $count > $max; + $path =~ s@/([^/]+/?)$@@; + my $tail = $1; + + if (length($tail)+3 == $maxlen) { + return "../$tail"; + } elsif (length($tail)+2 >= $maxlen) { + return '..'.substr($tail, -$maxlen+2) + } - my $rest = 1; - $rest = $max-$count if ($max-$count > 0); + $path =~ s@(/[^/]+)(?:/|$)@@; + my $head = $1; + my $both = length($head) + length($tail); + my $remaining = $maxlen-$both-4; # -4 for "/../" - return "$spl[0]".substr($target, length($spl[0]), $rest)."..\/".$spl[@spl-1]; + if ($remaining < 0) { + return substr($head, 0, $maxlen - length($tail) - 3) . "../$tail"; # -3 for "../" + } + + substr($path, ($remaining/2), (length($path)-$remaining), '..'); + return "$head/" . $path . "/$tail"; } sub lock { @@ -107,16 +123,19 @@ sub check_pool_exists { my ($target) = @_; my $cmd = []; - push @$cmd, 'ssh', "root\@$target->{ip}", '--', if $target->{ip}; + + if ($target->{ip}) { + push @$cmd, 'ssh', "root\@$target->{ip}", '--'; + } push @$cmd, 'zfs', 'list', '-H', '--', $target->{all}; eval { run_cmd($cmd); }; if ($@) { - return 1; + return 0; } - return undef; + return 1; } sub parse_target { @@ -425,7 +444,7 @@ sub list { $list .= sprintf("%-25s", cut_target_width($name, 25)); $list .= sprintf("%-10s", $states->{$source}->{$name}->{state}); $list .= sprintf("%-20s", $states->{$source}->{$name}->{lsync}); - $list .= sprintf("%-6s", $states->{$source}->{$name}->{vm_type}); + $list .= sprintf("%-6s", defined($states->{$source}->{$name}->{vm_type}) ? $states->{$source}->{$name}->{vm_type} : "undef"); $list .= sprintf("%-5s\n", $cfg->{$source}->{$name}->{method}); } } @@ -440,6 +459,8 @@ sub vm_exists { my $res = undef; + return undef if !defined($target->{vmid}); + eval { $res = run_cmd([@cmd, 'ls', "$QEMU_CONF/$target->{vmid}.conf"]) }; return "qemu" if $res; @@ -472,22 +493,22 @@ sub init { run_cmd(['ssh-copy-id', '-i', '/root/.ssh/id_rsa.pub', "root\@$ip"]); } - die "Pool $dest->{all} does not exists\n" if check_pool_exists($dest); - - my $check = check_pool_exists($source->{path}, $source->{ip}) if !$source->{vmid} && $source->{path}; + die "Pool $dest->{all} does not exists\n" if !check_pool_exists($dest); - die "Pool $source->{path} does not exists\n" if undef($check); + if (!defined($source->{vmid})) { + die "Pool $source->{all} does not exists\n" if !check_pool_exists($source); + } my $vm_type = vm_exists($source); $job->{vm_type} = $vm_type; $source->{vm_type} = $vm_type; - die "VM $source->{vmid} doesn't exist\n" if $param->{vmid} && !$vm_type; + die "VM $source->{vmid} doesn't exist\n" if $source->{vmid} && !$vm_type; die "Config already exists\n" if $cfg->{$job->{source}}->{$job->{name}}; #check if vm has zfs disks if not die; - get_disks($source, 1) if $source->{vmid}; + get_disks($source) if $source->{vmid}; update_cron($job); update_state($job); @@ -627,7 +648,7 @@ sub snapshot_get{ while ($raw && $raw =~ s/^(.*?)(\n|$)//) { $line = $1; - if ($line =~ m/(rep_$name.*)$/) { + if ($line =~ m/(rep_\Q${name}\E_\d{4}-\d{2}-\d{2}_\d{2}:\d{2}:\d{2})$/) { $last_snap = $1 if (!$last_snap); $old_snap = $1; @@ -703,7 +724,7 @@ sub write_cron { } sub get_disks { - my ($target, $get_err) = @_; + my ($target) = @_; my $cmd = []; push @$cmd, 'ssh', "root\@$target->{ip}", '--', if $target->{ip}; @@ -718,7 +739,7 @@ sub get_disks { my $res = run_cmd($cmd); - my $disks = parse_disks($res, $target->{ip}, $target->{vm_type}, $get_err); + my $disks = parse_disks($res, $target->{ip}, $target->{vm_type}); return $disks; } @@ -741,31 +762,38 @@ sub run_cmd { } sub parse_disks { - my ($text, $ip, $vm_type, $get_err) = @_; + my ($text, $ip, $vm_type) = @_; my $disks; my $num = 0; while ($text && $text =~ s/^(.*?)(\n|$)//) { my $line = $1; - my $error = $vm_type eq 'qemu' ? 1 : 0 ; - next if $line =~ /cdrom|none/; + next if $line =~ /media=cdrom/; next if $line !~ m/^(?:((?:virtio|ide|scsi|sata|mp)\d+)|rootfs): /; #QEMU if backup is not set include in sync - next if $vm_type eq 'qemu && ($line =~ m/backup=(?i:0|no|off|false)/)'; + next if $vm_type eq 'qemu' && ($line =~ m/backup=(?i:0|no|off|false)/); #LXC if backup is not set do no in sync - $error = ($line =~ m/backup=(?i:1|yes|on|true)/) if $vm_type eq 'lxc'; + next if $vm_type eq 'lxc' && ($line =~ m/^mp\d:/) && ($line !~ m/backup=(?i:1|yes|on|true)/); my $disk = undef; my $stor = undef; - if($line =~ m/^(?:((?:virtio|ide|scsi|sata|mp)\d+)|rootfs): ([^:]+:)([A-Za-z0-9\-]+),(.*)$/) { - $disk = $3; - $stor = $2; - } else { - print "Disk: \"$line\" will not include in pve-sync\n" if $get_err || $error; + if($line =~ m/^(?:(?:(?:virtio|ide|scsi|sata|mp)\d+)|rootfs): (.*)$/) { + my @parameter = split(/,/,$1); + + foreach my $opt (@parameter) { + if ($opt =~ m/^(?:file=|volume=)?([^:]+:)([A-Za-z0-9\-]+)$/){ + $disk = $2; + $stor = $1; + last; + } + } + } + if (!defined($disk) || !defined($stor)) { + print "Disk: \"$line\" has no valid zfs dataset format and will be skipped\n"; next; } @@ -869,7 +897,7 @@ sub send_image { my $cmd = []; - push @$cmd, 'ssh', "root\@$source->{ip}", '--' if $source->{ip}; + push @$cmd, 'ssh', '-o', 'BatchMode=yes', "root\@$source->{ip}", '--' if $source->{ip}; push @$cmd, 'zfs', 'send'; push @$cmd, '-v' if $param->{verbose}; @@ -886,298 +914,354 @@ sub send_image { $target =~ s!/+!/!g; push @$cmd, \'|'; - push @$cmd, 'ssh', "root\@$dest->{ip}", '--' if $dest->{ip}; - push @$cmd, 'zfs', 'recv', '-F', '--'; - push @$cmd, "$target"; + push @$cmd, 'ssh', '-o', 'BatchMode=yes', "root\@$dest->{ip}", '--' if $dest->{ip}; + push @$cmd, 'zfs', 'recv', '-F', '--'; + push @$cmd, "$target"; - eval { - run_cmd($cmd) - }; + eval { + run_cmd($cmd) + }; - if (my $erro = $@) { - snapshot_destroy($source, undef, $param->{method}, $source->{new_snap}); - die $erro; - }; - } + if (my $erro = $@) { + snapshot_destroy($source, undef, $param->{method}, $source->{new_snap}); + die $erro; + }; +} - sub send_config{ - my ($source, $dest, $method) = @_; +sub send_config{ + my ($source, $dest, $method) = @_; - my $source_target = $source->{vm_type} eq 'qemu' ? "$QEMU_CONF/$source->{vmid}.conf": "$LXC_CONF/$source->{vmid}.conf"; - my $dest_target_new ="$source->{vmid}.conf.$source->{vm_type}.$source->{new_snap}"; + my $source_target = $source->{vm_type} eq 'qemu' ? "$QEMU_CONF/$source->{vmid}.conf": "$LXC_CONF/$source->{vmid}.conf"; + my $dest_target_new ="$source->{vmid}.conf.$source->{vm_type}.$source->{new_snap}"; - my $config_dir = $dest->{last_part} ? "${CONFIG_PATH}/$dest->{last_part}" : $CONFIG_PATH; + my $config_dir = $dest->{last_part} ? "${CONFIG_PATH}/$dest->{last_part}" : $CONFIG_PATH; - $dest_target_new = $config_dir.'/'.$dest_target_new; + $dest_target_new = $config_dir.'/'.$dest_target_new; - if ($method eq 'ssh'){ - if ($dest->{ip} && $source->{ip}) { - run_cmd(['ssh', "root\@$dest->{ip}", '--', 'mkdir', '-p', '--', $config_dir]); - run_cmd(['scp', '--', "root\@[$source->{ip}]:$source_target", "root\@[$dest->{ip}]:$dest_target_new"]); - } elsif ($dest->{ip}) { - run_cmd(['ssh', "root\@$dest->{ip}", '--', 'mkdir', '-p', '--', $config_dir]); - run_cmd(['scp', '--', $source_target, "root\@[$dest->{ip}]:$dest_target_new"]); - } elsif ($source->{ip}) { - run_cmd(['mkdir', '-p', '--', $config_dir]); - run_cmd(['scp', '--', "root\@$source->{ip}:$source_target", $dest_target_new]); - } + if ($method eq 'ssh'){ + if ($dest->{ip} && $source->{ip}) { + run_cmd(['ssh', "root\@$dest->{ip}", '--', 'mkdir', '-p', '--', $config_dir]); + run_cmd(['scp', '--', "root\@[$source->{ip}]:$source_target", "root\@[$dest->{ip}]:$dest_target_new"]); + } elsif ($dest->{ip}) { + run_cmd(['ssh', "root\@$dest->{ip}", '--', 'mkdir', '-p', '--', $config_dir]); + run_cmd(['scp', '--', $source_target, "root\@[$dest->{ip}]:$dest_target_new"]); + } elsif ($source->{ip}) { + run_cmd(['mkdir', '-p', '--', $config_dir]); + run_cmd(['scp', '--', "root\@[$source->{ip}]:$source_target", $dest_target_new]); + } - if ($source->{destroy}){ - my $dest_target_old ="${config_dir}/$source->{vmid}.conf.$source->{vm_type}.$source->{old_snap}"; - if($dest->{ip}){ - run_cmd(['ssh', "root\@$dest->{ip}", '--', 'rm', '-f', '--', $dest_target_old]); - } else { - run_cmd(['rm', '-f', '--', $dest_target_old]); - } + if ($source->{destroy}){ + my $dest_target_old ="${config_dir}/$source->{vmid}.conf.$source->{vm_type}.$source->{old_snap}"; + if($dest->{ip}){ + run_cmd(['ssh', "root\@$dest->{ip}", '--', 'rm', '-f', '--', $dest_target_old]); + } else { + run_cmd(['rm', '-f', '--', $dest_target_old]); } - } elsif ($method eq 'local') { - run_cmd(['mkdir', '-p', '--', $config_dir]); - run_cmd(['cp', $source_target, $dest_target_new]); } + } elsif ($method eq 'local') { + run_cmd(['mkdir', '-p', '--', $config_dir]); + run_cmd(['cp', $source_target, $dest_target_new]); } +} - sub get_date { - my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time); - my $datestamp = sprintf ("%04d-%02d-%02d_%02d:%02d:%02d", $year+1900, $mon+1, $mday, $hour, $min, $sec); +sub get_date { + my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time); + my $datestamp = sprintf ("%04d-%02d-%02d_%02d:%02d:%02d", $year+1900, $mon+1, $mday, $hour, $min, $sec); - return $datestamp; - } + return $datestamp; +} - sub status { - my $cfg = read_cron(); +sub status { + my $cfg = read_cron(); - my $status_list = sprintf("%-25s%-25s%-10s\n", "SOURCE", "NAME", "STATUS"); + my $status_list = sprintf("%-25s%-25s%-10s\n", "SOURCE", "NAME", "STATUS"); - my $states = read_state(); + my $states = read_state(); - foreach my $source (sort keys%{$cfg}) { - foreach my $sync_name (sort keys%{$cfg->{$source}}) { - $status_list .= sprintf("%-25s", cut_target_width($source, 25)); - $status_list .= sprintf("%-25s", cut_target_width($sync_name, 25)); - $status_list .= "$states->{$source}->{$sync_name}->{state}\n"; - } + foreach my $source (sort keys%{$cfg}) { + foreach my $sync_name (sort keys%{$cfg->{$source}}) { + $status_list .= sprintf("%-25s", cut_target_width($source, 25)); + $status_list .= sprintf("%-25s", cut_target_width($sync_name, 25)); + $status_list .= "$states->{$source}->{$sync_name}->{state}\n"; } - - return $status_list; } - sub enable_job { - my ($param) = @_; + return $status_list; +} - my $job = get_job($param); - $job->{state} = "ok"; - update_state($job); - update_cron($job); - } +sub enable_job { + my ($param) = @_; - sub disable_job { - my ($param) = @_; + my $job = get_job($param); + $job->{state} = "ok"; + update_state($job); + update_cron($job); +} - my $job = get_job($param); - $job->{state} = "stopped"; - update_state($job); - update_cron($job); - } +sub disable_job { + my ($param) = @_; - my $command = $ARGV[0]; + my $job = get_job($param); + $job->{state} = "stopped"; + update_state($job); + update_cron($job); +} - my $commands = {'destroy' => 1, - 'create' => 1, - 'sync' => 1, - 'list' => 1, - 'status' => 1, - 'help' => 1, - 'enable' => 1, - 'disable' => 1}; +my $command = $ARGV[0]; - if (!$command || !$commands->{$command}) { - usage(); - die "\n"; - } +my $commands = {'destroy' => 1, + 'create' => 1, + 'sync' => 1, + 'list' => 1, + 'status' => 1, + 'help' => 1, + 'enable' => 1, + 'disable' => 1}; - my $help_sync = "$PROGNAME sync -dest -source [OPTIONS]\n -\twill sync one time\n -\t-dest\tstring\n -\t\tthe destination target is like [IP:][/Path]\n -\t-limit\tinteger\n -\t\tmax sync speed in kBytes/s, default unlimited\n -\t-maxsnap\tinteger\n -\t\thow much snapshots will be kept before get erased, default 1/n -\t-name\tstring\n -\t\tname of the sync job, if not set it is default. -\tIt is only necessary if scheduler allready contains this source.\n -\t-source\tstring\n -\t\tthe source can be an or [IP:][/Path]\n"; - - my $help_create = "$PROGNAME create -dest -source [OPTIONS]/n -\tCreate a sync Job\n -\t-dest\tstring\n -\t\tthe destination target is like [IP]:[/Path]\n -\t-limit\tinteger\n -\t\tmax sync speed in kBytes/s, default unlimited\n -\t-maxsnap\tstring\n -\t\thow much snapshots will be kept before get erased, default 1\n -\t-name\tstring\n -\t\tname of the sync job, if not set it is default\n -\t-skip\tboolean\n -\t\tif this flag is set it will skip the first sync\n -\t-source\tstring\n -\t\tthe source can be an or [IP:][/Path]\n"; - - my $help_destroy = "$PROGNAME destroy -source [OPTIONS]\n -\tremove a sync Job from the scheduler\n -\t-name\tstring\n -\t\tname of the sync job, if not set it is default\n -\t-source\tstring\n -\t\tthe source can be an or [IP:][/Path]\n"; - - my $help_help = "$PROGNAME help [OPTIONS]\n -\tGet help about specified command.\n -\t\tstring\n -\t\tCommand name\n -\t-verbose\tboolean\n -\t\tVerbose output format.\n"; - - my $help_list = "$PROGNAME list\n -\tGet a List of all scheduled Sync Jobs\n"; - - my $help_status = "$PROGNAME status\n -\tGet the status of all scheduled Sync Jobs\n"; - - my $help_enable = "$PROGNAME enable -source [OPTIONS]\n -\tenable a syncjob and reset error\n -\t-name\tstring\n -\t\tname of the sync job, if not set it is default\n -\t-source\tstring\n -\t\tthe source can be an or [IP:][/Path]\n"; - - my $help_disable = "$PROGNAME disable -source [OPTIONS]\n -\tpause a syncjob\n -\t-name\tstring\n -\t\tname of the sync job, if not set it is default\n -\t-source\tstring\n -\t\tthe source can be an or [IP:][/Path]\n"; - - sub help { - my ($command) = @_; - - switch($command){ - case 'help' - { - die "$help_help\n"; - } - case 'sync' - { - die "$help_sync\n"; - } - case 'destroy' - { - die "$help_destroy\n"; - } - case 'create' - { - die "$help_create\n"; - } - case 'list' - { - die "$help_list\n"; - } - case 'status' - { - die "$help_status\n"; - } - case 'enable' - { - die "$help_enable\n"; - } - case 'disable' - { - die "$help_enable\n"; - } - } +if (!$command || !$commands->{$command}) { + usage(); + die "\n"; +} - } +my $help_sync = < -source [OPTIONS]\n - my @arg = @ARGV; - my $param = parse_argv(@arg); + will sync one time + -dest string + + the destination target is like [IP:][/Path] + + -limit integer + + max sync speed in kBytes/s, default unlimited + + -maxsnap integer + + how much snapshots will be kept before get erased, default 1 + + -name string + + name of the sync job, if not set it is default. + It is only necessary if scheduler allready contains this source. + + -source string + + the source can be an or [IP:][/Path] + + -verbose boolean + + print out the sync progress. +EOF + +my $help_create = < -source [OPTIONS] + + Create a sync Job + + -dest string + + the destination target is like [IP]:[/Path] + + -limit integer + + max sync speed in kBytes/s, default unlimited + + -maxsnap string + + how much snapshots will be kept before get erased, default 1 + + -name string + + name of the sync job, if not set it is default + + -skip boolean + + if this flag is set it will skip the first sync + + -source string + + the source can be an or [IP:][/Path] +EOF + +my $help_destroy = < [OPTIONS] + + remove a sync Job from the scheduler + + -name string + + name of the sync job, if not set it is default + + -source string + + the source can be an or [IP:][/Path] +EOF + +my $help_help = < [OPTIONS] + + Get help about specified command. + + string + + Command name + + -verbose boolean + + Verbose output format. +EOF + +my $help_list = < [OPTIONS] + + enable a syncjob and reset error + + -name string + + name of the sync job, if not set it is default + + -source string + + the source can be an or [IP:][/Path] +EOF + +my $help_disable = < [OPTIONS] + + pause a sync job + + -name string + + name of the sync job, if not set it is default + + -source string + + the source can be an or [IP:][/Path] +EOF + +sub help { + my ($command) = @_; + + if ($command eq 'help') { + die "$help_help\n"; + + } elsif ($command eq 'sync') { + die "$help_sync\n"; + + } elsif ($command eq 'destroy') { + die "$help_destroy\n"; + + } elsif ($command eq 'create') { + die "$help_create\n"; + + } elsif ($command eq 'list') { + die "$help_list\n"; + + } elsif ($command eq 'status') { + die "$help_status\n"; + + } elsif ($command eq 'enable') { + die "$help_enable\n"; + + } elsif ($command eq 'disable') { + die "$help_disable\n"; - switch($command) { - case "destroy" - { - die "$help_destroy\n" if !$param->{source}; - check_target($param->{source}); - destroy_job($param); - } - case "sync" - { - die "$help_sync\n" if !$param->{source} || !$param->{dest}; - check_target($param->{source}); - check_target($param->{dest}); - sync($param); - } - case "create" - { - die "$help_create\n" if !$param->{source} || !$param->{dest}; - check_target($param->{source}); - check_target($param->{dest}); - init($param); - } - case "status" - { - print status(); - } - case "list" - { - print list(); - } - case "help" - { - my $help_command = $ARGV[1]; - if ($help_command && $commands->{$help_command}) { - print help($help_command); - } - if ($param->{verbose} == 1){ - exec("man $PROGNAME"); - } else { - usage(1); - } - } - case "enable" - { - die "$help_enable\n" if !$param->{source}; - check_target($param->{source}); - enable_job($param); - } - case "disable" - { - die "$help_disable\n" if !$param->{source}; - check_target($param->{source}); - disable_job($param); - } } - sub usage { - my ($help) = @_; - - print("ERROR:\tno command specified\n") if !$help; - print("USAGE:\t$PROGNAME [ARGS] [OPTIONS]\n"); - print("\t$PROGNAME help [] [OPTIONS]\n\n"); - print("\t$PROGNAME create -dest -source [OPTIONS]\n"); - print("\t$PROGNAME destroy -source [OPTIONS]\n"); - print("\t$PROGNAME disable -source [OPTIONS]\n"); - print("\t$PROGNAME enable -source [OPTIONS]\n"); - print("\t$PROGNAME list\n"); - print("\t$PROGNAME status\n"); - print("\t$PROGNAME sync -dest -source [OPTIONS]\n"); +} + +my @arg = @ARGV; +my $param = parse_argv(@arg); + +if ($command eq 'destroy') { + die "$help_destroy\n" if !$param->{source}; + + check_target($param->{source}); + destroy_job($param); + +} elsif ($command eq 'sync') { + die "$help_sync\n" if !$param->{source} || !$param->{dest}; + + check_target($param->{source}); + check_target($param->{dest}); + sync($param); + +} elsif ($command eq 'create') { + die "$help_create\n" if !$param->{source} || !$param->{dest}; + + check_target($param->{source}); + check_target($param->{dest}); + init($param); + +} elsif ($command eq 'status') { + print status(); + +} elsif ($command eq 'list') { + print list(); + +} elsif ($command eq 'help') { + my $help_command = $ARGV[1]; + + if ($help_command && $commands->{$help_command}) { + print help($help_command); + } + if ($param->{verbose}) { + exec("man $PROGNAME"); + + } else { + usage(1); - sub check_target { - my ($target) = @_; - parse_target($target); } +} elsif ($command eq 'enable') { + die "$help_enable\n" if !$param->{source}; + + check_target($param->{source}); + enable_job($param); + +} elsif ($command eq 'disable') { + die "$help_disable\n" if !$param->{source}; + + check_target($param->{source}); + disable_job($param); + +} + +sub usage { + my ($help) = @_; + + print("ERROR:\tno command specified\n") if !$help; + print("USAGE:\t$PROGNAME [ARGS] [OPTIONS]\n"); + print("\t$PROGNAME help [] [OPTIONS]\n\n"); + print("\t$PROGNAME create -dest -source [OPTIONS]\n"); + print("\t$PROGNAME destroy -source [OPTIONS]\n"); + print("\t$PROGNAME disable -source [OPTIONS]\n"); + print("\t$PROGNAME enable -source [OPTIONS]\n"); + print("\t$PROGNAME list\n"); + print("\t$PROGNAME status\n"); + print("\t$PROGNAME sync -dest -source [OPTIONS]\n"); +} + +sub check_target { + my ($target) = @_; + parse_target($target); +} + __END__ =head1 NAME @@ -1296,6 +1380,10 @@ pve-zsync sync -dest -source [OPTIONS] the source can be an or [IP:][/Path] + -verbose boolean + + print out the sync progress. + =head1 DESCRIPTION This Tool helps you to sync your VM or directory which stored on ZFS between 2 servers.