]> git.proxmox.com Git - pve-zsync.git/blobdiff - pve-zsync
to ensure the speed limit and some other features serialize the script
[pve-zsync.git] / pve-zsync
index 4a0d2d82793f2573869eefe0282afa251686fa37..512793ff5de77fdcfb7610d837f4b04dec709afe 100644 (file)
--- a/pve-zsync
+++ b/pve-zsync
@@ -8,6 +8,7 @@ my $VMCONFIG = '/var/lib/'.$PROGNAME.'/';
 my $PATH = "/usr/sbin/";
 my $QEMU_CONF = '/etc/pve/local/qemu-server/';
 my $DEBUG = 0;
+my $LOCKFILE = $VMCONFIG.$PROGNAME.'lock';
 
 use strict;
 use warnings;
@@ -67,12 +68,11 @@ sub unlock {
 sub check_config {
     my ($source, $name, $cfg) = @_;
 
-    if ($source->{vmid}  && $cfg->{$source->{vmid}}->{$name}->{locked}){
-       return "active" if $cfg->{$source->{vmid}}->{$name}->{locked} eq 'yes';
-       return "exist" if $cfg->{$source->{vmid}}->{$name}->{locked} eq 'no';
-    } elsif ($cfg->{$source->{abs_path}}->{$name}->{locked}) {
-       return "active" if $cfg->{$source->{abs_path}}->{$name}->{locked} eq 'yes';
-       return "exist" if $cfg->{$source->{abs_path}}->{$name}->{locked} eq 'no';
+    my $id = $source->{vmid} ? $source->{vmid} : $source->{abs_path};
+    my $status = $cfg->{$id}->{$name}->{status};
+
+    if ($cfg->{$id}->{$name}->{status}){
+       return $status;
     }
 
     return undef;
@@ -121,8 +121,6 @@ sub read_from_config {
 
     my $text = <$fh>;
 
-    unlock($fh);
-
     close($fh);
 
     my $cfg = encode_config($text);
@@ -133,6 +131,7 @@ sub read_from_config {
 sub decode_config {
     my ($cfg) = @_;
     my $raw = '';
+
     foreach my $source (sort keys%{$cfg}){
        foreach my $sync_name (sort keys%{$cfg->{$source}}){
            $raw .= "$source: $sync_name\n";
@@ -150,7 +149,6 @@ sub encode_config {
     my $source;
     my $check = 0;
     my $sync_name;
-
     while ($raw && $raw =~ s/^(.*?)(\n|$)//) {
        my $line = $1;
 
@@ -169,9 +167,9 @@ sub encode_config {
                $cfg->{$source}->{$sync_name}->{$par} = $value;
                die "error in Config: SourceIP value doubled\n" if ($check & 2);
                $check += 2;
-           } elsif ($par eq 'locked') {
+           } elsif ($par eq 'status') {
                $cfg->{$source}->{$sync_name}->{$par} = $value;
-               die "error in Config: Locked value doubled\n" if ($check & 4);
+               die "error in Config: Status value doubled\n" if ($check & 4);
                $check += 4;
            } elsif ($par eq 'method') {
                $cfg -> {$source}->{$sync_name}->{$par} = $value;
@@ -262,13 +260,21 @@ sub list {
     my $cfg = read_from_config("$CONFIG_PATH$CONFIG");
 
     my $list = sprintf("%-25s%-15s%-7s%-20s%-10s%-5s\n" , "SOURCE", "NAME", "ACTIVE", "LAST SYNC", "INTERVAL", "TYPE");
-    
+
     foreach my $source (sort keys%{$cfg}){
        foreach my $sync_name (sort keys%{$cfg->{$source}}){
            my $source_name = $source;
            $source_name = $cfg->{$source}->{$sync_name}->{source_ip}.":".$source if $cfg->{$source}->{$sync_name}->{source_ip};
            $list .= sprintf("%-25s%-15s", cut_to_width($source_name,25), cut_to_width($sync_name,15));
-           $list .= sprintf("%-7s",$cfg->{$source}->{$sync_name}->{locked});
+
+           my $active = "";
+           if($cfg->{$source}->{$sync_name}->{status} eq 'syncing'){
+               $active = "yes";
+           } else {
+               $active = "no";
+           }
+
+           $list .= sprintf("%-7s", $active);
            $list .= sprintf("%-20s",$cfg->{$source}->{$sync_name}->{lsync});
            $list .= sprintf("%-10s",$cfg->{$source}->{$sync_name}->{interval});
            $list .= sprintf("%-5s\n",$cfg->{$source}->{$sync_name}->{method});
@@ -294,6 +300,8 @@ sub vm_exists {
 sub init {
     my ($param) = @_;
 
+    open(my $lock_fh, ">", $LOCKFILE) || die "cannot open Lock File: $LOCKFILE\n";
+    lock($lock_fh);
     my $cfg = read_from_config;
 
     my $vm = {};
@@ -309,7 +317,7 @@ sub init {
     $vm->{$name}->{dest_path} = $dest->{path} if $dest->{path};
 
     $param->{method} = "local" if !$dest->{ip} && !$source->{ip};
-    $vm->{$name}->{locked} = "no";
+    $vm->{$name}->{status} = "ok";
     $vm->{$name}->{interval} = $interval;
     $vm->{$name}->{method} = $param->{method} ? $param->{method} : "ssh";
     $vm->{$name}->{limit} = $param->{limit} if $param->{limit};
@@ -332,20 +340,18 @@ sub init {
     my $add_job = sub {
        my ($vm, $name) = @_;
        my $source = "";
-
        if ($vm->{$name}->{vmid}) {
-           $source = "$vm->{$name}->{source_ip}:" if $vm->{$name}->{source_ip};
            $source .= $vm->{$name}->{vmid};
        } else {
-           $source = $vm->{$name}->{source_pool};
+           $source .= $vm->{$name}->{source_pool};
            $source .= $vm->{$name}->{source_path} if $vm->{$name}->{source_path};
        }
        die "Config already exists\n" if $cfg->{$source}->{$name};
 
-       cron_add($vm);
-
        $cfg->{$source}->{$name} = $vm->{$name};
 
+       write_to_cron($cfg);
+
        write_to_config($cfg);
     };
 
@@ -366,7 +372,10 @@ sub init {
 
        &$add_job($vm, $name);
     }
-
+    
+    lock($lock_fh);
+    close($lock_fh);
+    
     eval {sync($param) if !$param->{skip};};
     if(my $err = $@) {
        destroy($param);
@@ -377,53 +386,14 @@ sub init {
 sub destroy {
     my ($param) = @_;
 
-    my $cfg = read_from_config("$CONFIG_PATH$CONFIG");
-    my $name =  $param->{name} ? $param->{name} : "default";
-
-    my $source = parse_target($param->{source});
-
-    my $delete_cron = sub {
-       my ($path, $name, $cfg) = @_;
-
-       die "Source does not exist!\n" unless $cfg->{$path} ;
-
-       die "Sync Name does not exist!\n" unless $cfg->{$path}->{$name};
-
-       my $source .= $cfg->{$path}->{$name}->{source_ip} ? "$cfg->{$path}->{$name}->{source_ip}:"  : '';
-
-       $source .= $cfg->{$path}->{$name}->{source_pool};
-       $source .= $cfg->{$path}->{$name}->{source_path} ? $cfg->{$path}->{$name}->{source_path}  :'';
-
-       my $dest = $cfg->{$path}->{$name}->{dest_ip} ? $cfg->{$path}->{$name}->{dest_ip}  :"";
-       $dest .= $cfg->{$path}->{$name}->{dest_pool};
-       $dest .= $cfg->{$path}->{$name}->{dest_path} ? $cfg->{$path}->{$name}->{dest_path}  :'';
-
-       delete $cfg->{$path}->{$name};
-
-       delete $cfg->{$path} if keys%{$cfg->{$path}} == 0;
-
-       write_to_config($cfg);
-
-       cron_del($source, $dest, $name);
-    };
-
-
-    if ($source->{vmid}) {
-       my $path = $source->{vmid};
-
-        &$delete_cron($path, $name, $cfg)
-
-    } else {
-
-       my $path = $source->{pool};
-       $path .= $source->{path} if $source->{path};
+    modify_configs($param->{name}, $param->{source},1);
 
-       &$delete_cron($path, $name, $cfg);
-    }
 }
 
 sub sync {
     my ($param) = @_;
+    open(my $lock_fh, ">", $LOCKFILE) || die "cannot open Lock File: $LOCKFILE\n";
+    lock($lock_fh);
 
     my $cfg = read_from_config("$CONFIG_PATH$CONFIG");
 
@@ -440,27 +410,46 @@ sub sync {
        ($source->{old_snap},$source->{last_snap}) = snapshot_get($source, $dest, $max_snap, $name);
 
        my $job_status = check_config($source, $name, $cfg) if $cfg;
-       die "VM syncing at the moment!\n" if ($job_status && $job_status eq "active");
+       die "VM Status: $job_status syncing will not done!\n" if ($job_status && !($job_status eq "ok" || $job_status eq "stoped"));
 
-       if ($job_status && $job_status eq "exist") {
+       if ($job_status) {
            my $conf_name = $source->{abs_path};
            $conf_name = $source->{vmid} if $source->{vmid};
-           $cfg->{$conf_name}->{$name}->{locked} = "yes";
+           $cfg->{$conf_name}->{$name}->{status} = "syncing";
            write_to_config($cfg);
        }
 
-       my $date = snapshot_add($source, $dest, $name);
+       my $date = undef;
 
-       send_image($source, $dest, $method, $param->{verbose}, $param->{limit});
+       eval{
+           $date = snapshot_add($source, $dest, $name);
 
-       snapshot_destroy($source, $dest, $method, $source->{old_snap}) if ($source->{destroy} && $source->{old_snap});
+           send_image($source, $dest, $method, $param->{verbose}, $param->{limit});
 
+           snapshot_destroy($source, $dest, $method, $source->{old_snap}) if ($source->{destroy} && $source->{old_snap});
+       };
+       if(my $err = $@){
+           if ($job_status){
+               my $conf_name = $source->{abs_path};
+               $conf_name = $source->{vmid} if $source->{vmid};
+               $cfg->{$conf_name}->{$name}->{status} = "error";
+               write_to_config($cfg);
+
+               my $source_target = $source->{ip} ? $source->{ip}.":" : '';
+               $source_target .= $source->{vmid} ? $source->{vmid} : $source->{abs_path};
+               write_to_cron($cfg);
+               lock($lock_fh);
+               close($lock_fh);
+           }
+           die "$err\n";
+       }
 
-       if ($job_status && $job_status eq "exist") {
+       if ($job_status) {
            my $conf_name = $source->{abs_path};
            $conf_name = $source->{vmid} if $source->{vmid};
-           $cfg->{$conf_name}->{$name}->{locked} = "no";
+           $cfg->{$conf_name}->{$name}->{status} = "ok";
            $cfg->{$conf_name}->{$name}->{lsync} = $date;
+
            write_to_config($cfg);
        }
     };
@@ -480,9 +469,14 @@ sub sync {
 
            &$sync_path($source, $name, $cfg, $max_snap, $dest, $method);
        }
+       if ($method eq "ssh") {
+           send_config($source, $dest,'ssh');
+       }
     } else {
        &$sync_path($source, $name, $cfg, $max_snap, $dest, $method);
     }
+    lock($lock_fh);
+    close($lock_fh);
 }
 
 sub snapshot_get{
@@ -539,57 +533,40 @@ sub snapshot_add {
     return $date;
 }
 
-sub cron_add {
-    my ($vm) = @_;
+sub write_to_cron {
+    my ($cfg) = @_;
+
+    my $text = 'SHELL=/bin/sh'."\n";
+    $text .= 'PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin'."\n";
 
-    open(my $fh, '>>', "$CRONJOBS")
+    open(my $fh, '>', "$CRONJOBS")
        or die "Could not open file: $!\n";
 
-    foreach my $name (keys%{$vm}){
-       my $text = "*/$vm->{$name}->{interval} * * * * root ";
+    foreach my $source (sort keys%{$cfg}){
+       foreach my $sync_name (sort keys%{$cfg->{$source}}){
+           next if $cfg->{$source}->{$sync_name}->{status} ne 'ok';
+       $text .= "*/$cfg->{$source}->{$sync_name}->{interval} * * * * root ";
        $text .= "$PATH$PROGNAME sync";
        $text .= " -source  ";
-       if ($vm->{$name}->{vmid}) {
-           $text .= "$vm->{$name}->{source_ip}:" if $vm->{$name}->{source_ip};
-           $text .= "$vm->{$name}->{vmid} ";
+       if ($cfg->{$source}->{$sync_name}->{vmid}) {
+           $text .= "$cfg->{$source}->{$sync_name}->{source_ip}:" if $cfg->{$source}->{$sync_name}->{source_ip};
+           $text .= "$cfg->{$source}->{$sync_name}->{vmid} ";
        } else {
-           $text .= "$vm->{$name}->{source_ip}:" if $vm->{$name}->{source_ip};
-           $text .= "$vm->{$name}->{source_pool}";
-           $text .= "$vm->{$name}->{source_path}" if $vm->{$name}->{source_path};
+           $text .= "$cfg->{$source}->{$sync_name}->{source_ip}:" if $cfg->{$source}->{$sync_name}->{source_ip};
+           $text .= "$cfg->{$source}->{$sync_name}->{source_pool}";
+           $text .= "$cfg->{$source}->{$sync_name}->{source_path}" if $cfg->{$source}->{$sync_name}->{source_path};
        }
        $text .= " -dest  ";
-       $text .= "$vm->{$name}->{dest_ip}:" if $vm->{$name}->{dest_ip};
-       $text .= "$vm->{$name}->{dest_pool}";
-       $text .= "$vm->{$name}->{dest_path}" if $vm->{$name}->{dest_path};
-       $text .= " -name $name ";
-       $text .= " -limit $vm->{$name}->{limit}" if $vm->{$name}->{limit};
-       $text .= " -maxsnap $vm->{$name}->{maxsnap}" if $vm->{$name}->{maxsnap};
+       $text .= "$cfg->{$source}->{$sync_name}->{dest_ip}:" if $cfg->{$source}->{$sync_name}->{dest_ip};
+       $text .= "$cfg->{$source}->{$sync_name}->{dest_pool}";
+       $text .= "$cfg->{$source}->{$sync_name}->{dest_path}" if $cfg->{$source}->{$sync_name}->{dest_path};
+       $text .= " -name $sync_name ";
+       $text .= " -limit $cfg->{$source}->{$sync_name}->{limit}" if $cfg->{$source}->{$sync_name}->{limit};
+       $text .= " -maxsnap $cfg->{$source}->{$sync_name}->{maxsnap}" if $cfg->{$source}->{$sync_name}->{maxsnap};
        $text .= "\n";
-       print($fh $text);
-    }
-    close($fh);
-}
-
-sub cron_del {
-    my ($source, $dest, $name) = @_;
-
-    open(my $fh, '<', "$CRONJOBS")
-       or die "Could not open file: $!\n";
-
-    $/ = undef;
-
-    my $text = <$fh>;
-    my $buffer = "";
-    close($fh);
-    while ($text && $text =~ s/^(.*?)(\n|$)//) {
-       my $line = $1.$2;
-       if ($line !~ m/^.*root $PATH$PROGNAME sync -source  $source.*-dest  $dest.*-name $name.*$/){
-           $buffer .= $line;
        }
     }
-    open($fh, '>', "$CRONJOBS")
-       or die "Could not open file: $!\n";
-    print($fh $buffer);
+    print($fh $text);
     close($fh);
 }
 
@@ -635,6 +612,7 @@ sub parse_disks {
        my $line = $1;
        my $disk = undef;
        my $stor = undef;
+       my $is_disk = $line =~ m/^(virtio|ide|scsi|sata){1}\d+: /;
        if($line =~ m/^(virtio\d+: )(.+:)([A-Za-z0-9\-]+),(.*)$/) {
            $disk = $3;
            $stor = $2;
@@ -649,7 +627,9 @@ sub parse_disks {
            $stor = $2;
        }
 
-       if($disk && $disk ne "none" && $disk !~ m/cdrom/ ) {
+       die "disk is not on ZFS Storage\n" if $is_disk && !$disk && $line !~ m/cdrom/;
+
+       if($disk && $line !~ m/none/ && $line !~ m/cdrom/ ) {
            my $cmd = "";
            $cmd .= "ssh root\@$ip " if $ip;
            $cmd .= "pvesm path $stor$disk";
@@ -666,7 +646,6 @@ sub parse_disks {
            }
        }
     }
-    die "disk is not on ZFS Storage\n" if $num == 0;
     return $disks;
 }
 
@@ -760,12 +739,6 @@ sub send_image {
        snapshot_destroy($source, undef, $method, $source->{new_snap});
        die $erro;
     };
-
-    if ($source->{vmid}) {
-       if ($method eq "ssh") {
-           send_config($source, $dest,'ssh');
-       }
-    }
 }
 
 
@@ -810,27 +783,102 @@ sub status {
        foreach my $sync_name (sort keys%{$cfg->{$source}}){
          my $status;
 
-               my $source_name = $source;
+         my $source_name = $source;
 
-               $source_name = $cfg->{$source}->{$sync_name}->{source_ip}.":".$source if $cfg->{$source}->{$sync_name}->{source_ip};
+         $source_name = $cfg->{$source}->{$sync_name}->{source_ip}.":".$source if $cfg->{$source}->{$sync_name}->{source_ip};
 
-               if ($cfg->{$source}->{$sync_name}->{locked} eq 'no'){
-                   $status = sprintf("%-10s","OK");
-               } elsif ($cfg->{$source}->{$sync_name}->{locked} eq 'yes' &&
-                        $cfg->{$source}->{$sync_name}->{failure}) {
-                   $status = sprintf("%-10s","sync error");
-               } else {
-                   $status = sprintf("%-10s","syncing");
-               }
+         $status = sprintf("%-10s",$cfg->{$source}->{$sync_name}->{status});
 
-               $status_list .= sprintf("%-25s%-15s", cut_to_width($source_name,25), cut_to_width($sync_name,15));
-               $status_list .= "$status\n";
+         $status_list .= sprintf("%-25s%-15s", cut_to_width($source_name,25), cut_to_width($sync_name,15));
+         $status_list .= "$status\n";
        }
     }
 
     return $status_list;
 }
 
+sub enable{
+    my ($param) = @_;
+
+    modify_configs($param->{name}, $param->{source},4);
+}
+
+sub disable{
+    my ($param) = @_;
+
+    modify_configs($param->{name}, $param->{source},2);
+}
+
+sub modify_configs{
+    my ($name, $sou, $op) = @_;
+
+    $name =  $name ? $name : "default";
+
+    open(my $lock_fh, ">", $LOCKFILE) || die "cannot open Lock File: $LOCKFILE\n";
+    lock($lock_fh);
+
+    my $cfg = read_from_config("$CONFIG_PATH$CONFIG");
+
+    my $source = parse_target($sou);
+
+    my $change_configs = sub {
+       my ($path, $name, $cfg, $op) = @_;
+
+       die "Source does not exist!\n" unless $cfg->{$path} ;
+
+       die "Sync Name does not exist!\n" unless $cfg->{$path}->{$name};
+
+       my $source = $cfg->{$path}->{$name}->{source_ip} ? "$cfg->{$path}->{$name}->{source_ip}:"  : '';
+
+       $source .= $cfg->{$path}->{$name}->{source_pool} if $cfg->{$path}->{$name}->{source_pool};
+       $source .= $cfg->{$path}->{$name}->{source_path} ? $cfg->{$path}->{$name}->{source_path}  :'';
+
+       my $dest = $cfg->{$path}->{$name}->{dest_ip} ? $cfg->{$path}->{$name}->{dest_ip}  :"";
+       $dest .= $cfg->{$path}->{$name}->{dest_pool} if $cfg->{$path}->{$name}->{dest_pool};
+       $dest .= $cfg->{$path}->{$name}->{dest_path} ? $cfg->{$path}->{$name}->{dest_path}  :'';
+
+       if($op == 1){
+           delete $cfg->{$path}->{$name};
+
+           delete $cfg->{$path} if keys%{$cfg->{$path}} == 0;
+
+           write_to_config($cfg);
+       }
+       if($op == 1 || $op == 2) {
+
+           if ($op == 2) {
+
+               $cfg->{$path}->{$name}->{status} = "stoped";
+
+               write_to_config($cfg);
+           }
+           write_to_cron($cfg);
+       } elsif($op == 4) {
+           my $job = {};
+
+           $cfg->{$path}->{$name}->{status} = "ok";
+
+           write_to_config($cfg);
+
+           write_to_cron($cfg);
+       }
+    };
+
+
+    if ($source->{vmid}) {
+       my $path = $source->{vmid};
+
+        &$change_configs($path, $name, $cfg, $op)
+    } else {
+       my $path = $source->{pool};
+       $path .= $source->{path} if $source->{path};
+
+       &$change_configs($path, $name, $cfg, $op);
+    }
+    unlock($lock_fh);
+    close($lock_fh);
+}
+
 
 my $command = $ARGV[0];
 
@@ -839,9 +887,11 @@ my $commands = {'destroy' => 1,
               'sync' => 1,
               'list' => 1,
               'status' => 1,
-              'help' => 1};
+              'help' => 1,
+              'enable' => 1,
+              'disable' => 1};
 
-if (!$command ||  !$commands->{$command}) {
+if (!$command || !$commands->{$command}) {
     usage();
     die "\n";
 }
@@ -855,7 +905,7 @@ my $maxsnap = '';
 my $name = '';
 my $skip = '';
 
-my $help_sync = "zfs-zsync sync -dest <string> -source <string> [OPTIONS]\n
+my $help_sync = "$PROGNAME sync -dest <string> -source <string> [OPTIONS]\n
 \twill sync one time\n
 \t-dest\tstring\n
 \t\tthe destination target is like [IP:]<Pool>[/Path]\n
@@ -869,7 +919,7 @@ my $help_sync = "zfs-zsync sync -dest <string> -source <string> [OPTIONS]\n
 \t-source\tstring\n
 \t\tthe source can be an <VMID> or [IP:]<ZFSPool>[/Path]\n";
 
-my $help_create = "zfs-zsync create -dest <string> -source <string> [OPTIONS]/n
+my $help_create = "$PROGNAME create -dest <string> -source <string> [OPTIONS]/n
 \tCreate a sync Job\n
 \t-dest\tstringn\n
 \t\tthe destination target is like [IP]:<Pool>[/Path]\n
@@ -877,7 +927,7 @@ my $help_create = "zfs-zsync create -dest <string> -source <string> [OPTIONS]/n
 \t\tthe interval in min in witch the zfs will sync,
 \t\tdefault is 15 min\n
 \t-limit\tinteger\n
-\t\tmax sync speed, default unlimited\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
@@ -887,26 +937,40 @@ my $help_create = "zfs-zsync create -dest <string> -source <string> [OPTIONS]/n
 \t-source\tstring\n
 \t\tthe source can be an <VMID> or [IP:]<ZFSPool>[/Path]\n";
 
-my $help_destroy = "zfs-zsync destroy -source <string> [OPTIONS]\n
+my $help_destroy = "$PROGNAME destroy -source <string> [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  <VMID> or [IP:]<ZFSPool>[/Path]\n";
 
-my $help_help = "zfs-zsync help <cmd> [OPTIONS]\n
+my $help_help = "$PROGNAME help <cmd> [OPTIONS]\n
 \tGet help about specified command.\n
 \t<cmd>\tstring\n
 \t\tCommand name\n
 \t-verbose\tboolean\n
 \t\tVerbose output format.\n";
 
-my $help_list = "zfs-zsync list\n
+my $help_list = "$PROGNAME list\n
 \tGet a List of all scheduled Sync Jobs\n";
 
-my $help_status = "zfs-zsync status\n
+my $help_status = "$PROGNAME status\n
 \tGet the status of all scheduled Sync Jobs\n";
 
+my $help_enable = "$PROGNAME enable -source <string> [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  <VMID> or [IP:]<ZFSPool>[/Path]\n";
+
+my $help_disable = "$PROGNAME disable -source <string> [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  <VMID> or [IP:]<ZFSPool>[/Path]\n";
+
 sub help{
     my ($command) = @_;
 
@@ -935,6 +999,14 @@ sub help{
        {
            die "$help_status\n";
        }
+       case 'enable'
+       {
+            die "$help_enable\n";
+       }
+       case 'disable'
+       {
+           die "$help_enable\n";
+       }
     }
 
 }
@@ -1003,6 +1075,18 @@ switch($command){
            usage(1);
        }
     }
+    case "enable"
+    {
+       die "$help_enable\n" if !$source;
+       check_target($source);
+       enable($param);
+    }
+    case "disable"
+    {
+       die "$help_disable\n" if !$source;
+       check_target($source);
+       disable($param);
+    }
 }
 
 sub usage{
@@ -1010,12 +1094,14 @@ sub usage{
 
     print("ERROR:\tno command specified\n") if !$help;
     print("USAGE:\t$PROGNAME <COMMAND> [ARGS] [OPTIONS]\n");
-    print("\tpve-zsync help [<cmd>] [OPTIONS]\n\n");
-    print("\tpve-zsync create -dest <string> -source <string> [OPTIONS]\n");
-    print("\tpve-zsync destroy -source <string> [OPTIONS]\n");
-    print("\tpve-zsync list\n");
-    print("\tpve-zsync status\n");
-    print("\tpve-zsync sync -dest <string> -source <string> [OPTIONS]\n");
+    print("\t$PROGNAME help [<cmd>] [OPTIONS]\n\n");
+    print("\t$PROGNAME create -dest <string> -source <string> [OPTIONS]\n");
+    print("\t$PROGNAME destroy -source <string> [OPTIONS]\n");
+    print("\t$PROGNAME disable -source <string> [OPTIONS]\n");
+    print("\t$PROGNAME enable -source <string> [OPTIONS]\n");
+    print("\t$PROGNAME list\n");
+    print("\t$PROGNAME status\n");
+    print("\t$PROGNAME sync -dest <string> -source <string> [OPTIONS]\n");
 }
 
 sub check_target{
@@ -1066,7 +1152,7 @@ zfs-zsync create -dest <string> -source <string> [OPTIONS]
 
           -limit     integer
 
-               max sync speed, default unlimited
+               max sync speed in kBytes/s, default unlimited
 
           -maxsnap   string
 
@@ -1096,6 +1182,29 @@ zfs-zsync destroy -source <string> [OPTIONS]
 
                 the source can be an  <VMID> or [IP:]<ZFSPool>[/Path]
 
+zfs-zsync disable -source <string> [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  <VMID> or [IP:]<ZFSPool>[/Path]
+
+zfs-zsync enable -source <string> [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  <VMID> or [IP:]<ZFSPool>[/Path]
 zfs-zsync list
 
        Get a List of all scheduled Sync Jobs