X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=pve-zsync;h=2503117235bbc5bb3540ba69147aa33c77faf63d;hb=dfd3d834246a5c58d5bda69bec112ba970f51a78;hp=0d8088f2eec60063646802efa4c9d2988b1d2c6d;hpb=eee21241abda017102b13a0f2608ed28f88d5778;p=pve-zsync.git diff --git a/pve-zsync b/pve-zsync index 0d8088f..2503117 100644 --- a/pve-zsync +++ b/pve-zsync @@ -7,21 +7,20 @@ 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'; my $PROGNAME = "pve-zsync"; -my $CONFIG_PATH = "/var/lib/${PROGNAME}/"; -my $STATE = "${CONFIG_PATH}sync_state"; +my $CONFIG_PATH = "/var/lib/${PROGNAME}"; +my $STATE = "${CONFIG_PATH}/sync_state"; my $CRONJOBS = "/etc/cron.d/$PROGNAME"; -my $PATH = "/usr/sbin/"; -my $PVE_DIR = "/etc/pve/local/"; -my $QEMU_CONF = "${PVE_DIR}qemu-server/"; -my $LXC_CONF = "${PVE_DIR}lxc/"; -my $LOCKFILE = "$CONFIG_PATH${PROGNAME}.lock"; -my $PROG_PATH = "$PATH${PROGNAME}"; +my $PATH = "/usr/sbin"; +my $PVE_DIR = "/etc/pve/local"; +my $QEMU_CONF = "${PVE_DIR}/qemu-server"; +my $LXC_CONF = "${PVE_DIR}/lxc"; +my $LOCKFILE = "$CONFIG_PATH/${PROGNAME}.lock"; +my $PROG_PATH = "$PATH/${PROGNAME}"; my $INTERVAL = 15; my $DEBUG = 0; @@ -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 $path if length($path) <= $maxlen; - return $target if (length($target) <= $max); - my @spl = split('/', $target); + return '..'.substr($path, -$maxlen+2) if $path !~ m@/@; - my $count = length($spl[@spl-1]); - return "..\/".substr($spl[@spl-1],($count-$max)+3 , $count) if $count > $max; + $path =~ s@/([^/]+/?)$@@; + my $tail = $1; - $count += length($spl[0]) if @spl > 1; - return substr($spl[0], 0, $max-4-length($spl[@spl-1]))."\/..\/".$spl[@spl-1] if $count > $max; + if (length($tail)+3 == $maxlen) { + return "../$tail"; + } elsif (length($tail)+2 >= $maxlen) { + return '..'.substr($tail, -$maxlen+2) + } + + $path =~ s@(/[^/]+)(?:/|$)@@; + my $head = $1; + my $both = length($head) + length($tail); + my $remaining = $maxlen-$both-4; # -4 for "/../" - my $rest = 1; - $rest = $max-$count if ($max-$count > 0); + if ($remaining < 0) { + return substr($head, 0, $maxlen - length($tail) - 3) . "../$tail"; # -3 for "../" + } - return "$spl[0]".substr($target, length($spl[0]), $rest)."..\/".$spl[@spl-1]; + 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 { @@ -404,6 +423,7 @@ sub format_job { $text .= " root"; $text .= " $PROGNAME sync --source $job->{source} --dest $job->{dest}"; $text .= " --name $job->{name} --maxsnap $job->{maxsnap}"; + $text .= " --limit $job->{limit}" if $job->{limit}; $text .= " --method $job->{method}"; $text .= " --verbose" if $job->{verbose}; $text .= "\n"; @@ -415,16 +435,16 @@ sub list { my $cfg = read_cron(); - my $list = sprintf("%-25s%-10s%-7s%-20s%-5s%-5s\n" , "SOURCE", "NAME", "STATE", "LAST SYNC", "TYPE", "CON"); + my $list = sprintf("%-25s%-25s%-10s%-20s%-6s%-5s\n" , "SOURCE", "NAME", "STATE", "LAST SYNC", "TYPE", "CON"); my $states = read_state(); foreach my $source (sort keys%{$cfg}) { foreach my $name (sort keys%{$cfg->{$source}}) { $list .= sprintf("%-25s", cut_target_width($source, 25)); - $list .= sprintf("%-10s", cut_target_width($name, 10)); - $list .= sprintf("%-7s", $states->{$source}->{$name}->{state}); + $list .= sprintf("%-25s", cut_target_width($name, 25)); + $list .= sprintf("%-10s", $states->{$source}->{$name}->{state}); $list .= sprintf("%-20s", $states->{$source}->{$name}->{lsync}); - $list .= sprintf("%-5s", $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}); } } @@ -439,11 +459,13 @@ sub vm_exists { my $res = undef; - eval { $res = run_cmd([@cmd, 'ls', "$QEMU_CONF$target->{vmid}.conf"]) }; + return undef if !defined($target->{vmid}); + + eval { $res = run_cmd([@cmd, 'ls', "$QEMU_CONF/$target->{vmid}.conf"]) }; return "qemu" if $res; - eval { $res = run_cmd([@cmd, 'ls', "$LXC_CONF$target->{vmid}.conf"]) }; + eval { $res = run_cmd([@cmd, 'ls', "$LXC_CONF/$target->{vmid}.conf"]) }; return "lxc" if $res; @@ -471,19 +493,23 @@ 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); + 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 $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) if $source->{vmid}; + update_cron($job); update_state($job); @@ -622,7 +648,8 @@ 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; $index++; @@ -743,16 +770,31 @@ sub parse_disks { while ($text && $text =~ s/^(.*?)(\n|$)//) { my $line = $1; - 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)/); + + #LXC if backup is not set do no in sync + 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 { - die "disk is not on ZFS Storage\n"; + 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; } my $cmd = []; @@ -760,6 +802,8 @@ sub parse_disks { push @$cmd, 'pvesm', 'path', "$stor$disk"; my $path = run_cmd($cmd); + die "Get no path from pvesm path $stor$disk\n" if !$path; + if ($vm_type eq 'qemu' && $path =~ m/^\/dev\/zvol\/(\w+.*)(\/$disk)$/) { my @array = split('/', $1); @@ -773,13 +817,13 @@ sub parse_disks { $disks->{$num}->{all} .= "\/$disk"; $num++; - } elsif ($vm_type eq 'lxc' && $path =~ m/^\/(\w+.+)\/(\w+.*)(\/$disk)$/) { + } elsif ($vm_type eq 'lxc' && $path =~ m/^\/(\w+.+)(\/(\w+.*))*(\/$disk)$/) { $disks->{$num}->{pool} = $1; $disks->{$num}->{all} = $disks->{$num}->{pool}; if ($2) { - $disks->{$num}->{path} = $2; + $disks->{$num}->{path} = $3; $disks->{$num}->{all} .= "\/$disks->{$num}->{path}"; } @@ -793,6 +837,7 @@ sub parse_disks { } } + die "Vm include no disk on zfs.\n" if !$disks->{0}; return $disks; } @@ -852,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}; @@ -869,453 +914,512 @@ 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; - print Dumper $dest_target_new, $dest; - 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->{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%-15s%-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("%-15s", 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\tstringn\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); } - __END__ +} 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 +=head1 NAME - pve-zsync - PVE ZFS Replication Manager +pve-zsync - PVE ZFS Replication Manager - =head1 SYNOPSIS +=head1 SYNOPSIS - pve-zsync [ARGS] [OPTIONS] +pve-zsync [ARGS] [OPTIONS] - pve-zsync help [OPTIONS] +pve-zsync help [OPTIONS] Get help about specified command. - string + string - Command name + Command name -verbose boolean - Verbose output format. + Verbose output format. - pve-zsync create -dest -source [OPTIONS] +pve-zsync create -dest -source [OPTIONS] - Create a sync Job + Create a sync Job - -dest string + -dest string - the destination target is like [IP]:[/Path] + the destination target is like [IP]:[/Path] - -limit integer + -limit integer max sync speed in kBytes/s, default unlimited - -maxsnap string + -maxsnap string - how much snapshots will be kept before get erased, default 1 + how much snapshots will be kept before get erased, default 1 - -name string + -name string - name of the sync job, if not set it is default + name of the sync job, if not set it is default - -skip boolean + -skip boolean - if this flag is set it will skip the first sync + if this flag is set it will skip the first sync - -source string + -source string - the source can be an or [IP:][/Path] + the source can be an or [IP:][/Path] pve-zsync destroy -source [OPTIONS] - remove a sync Job from the scheduler + remove a sync Job from the scheduler - -name string + -name string name of the sync job, if not set it is default - -source string + -source string - the source can be an or [IP:][/Path] + the source can be an or [IP:][/Path] - pve-zsync disable -source [OPTIONS] +pve-zsync disable -source [OPTIONS] - pause a sync job + pause a sync job - -name string + -name string - name of the sync job, if not set it is default + name of the sync job, if not set it is default - -source string + -source string - the source can be an or [IP:][/Path] + the source can be an or [IP:][/Path] pve-zsync enable -source [OPTIONS] - enable a syncjob and reset error + enable a syncjob and reset error - -name string + -name string name of the sync job, if not set it is default - -source string + -source string - the source can be an or [IP:][/Path] - pve-zsync list + the source can be an or [IP:][/Path] +pve-zsync list - Get a List of all scheduled Sync Jobs + Get a List of all scheduled Sync Jobs - pve-zsync status +pve-zsync status - Get the status of all scheduled Sync Jobs + Get the status of all scheduled Sync Jobs - pve-zsync sync -dest -source [OPTIONS] +pve-zsync sync -dest -source [OPTIONS] - will sync one time + will sync one time - -dest string + -dest string - the destination target is like [IP:][/Path] + the destination target is like [IP:][/Path] -limit integer max sync speed in kBytes/s, default unlimited - -maxsnap integer + -maxsnap integer - how much snapshots will be kept before get erased, default 1 + how much snapshots will be kept before get erased, default 1 - -name string + -name string - name of the sync job, if not set it is default. - It is only necessary if scheduler allready contains this source. + name of the sync job, if not set it is default. + It is only necessary if scheduler allready contains this source. - -source string + -source string + + the source can be an or [IP:][/Path] + + -verbose boolean - the source can be an or [IP:][/Path] + print out the sync progress. =head1 DESCRIPTION This Tool helps you to sync your VM or directory which stored on ZFS between 2 servers. This tool also has the capability to add jobs to cron so the sync will be automatically done. The default syncing interval is set to 15 min, if you want to change this value you can do this in /etc/cron.d/pve-zsync. - To config cron see man crontab. +To config cron see man crontab. - =head2 PVE ZFS Storage sync Tool +=head2 PVE ZFS Storage sync Tool - This Tool can get remote pool on other PVE or send Pool to others ZFS machines +This Tool can get remote pool on other PVE or send Pool to others ZFS machines - =head1 EXAMPLES +=head1 EXAMPLES - add sync job from local VM to remote ZFS Server - pve-zsync create -source=100 -dest=192.168.1.2:zfspool +add sync job from local VM to remote ZFS Server +pve-zsync create -source=100 -dest=192.168.1.2:zfspool - =head1 IMPORTANT FILES +=head1 IMPORTANT FILES - Cron jobs and config are stored at /etc/cron.d/pve-zsync +Cron jobs and config are stored at /etc/cron.d/pve-zsync - The VM config get copied on the destination machine to /var/lib/pve-zsync/ +The VM config get copied on the destination machine to /var/lib/pve-zsync/ - =head1 COPYRIGHT AND DISCLAIMER +=head1 COPYRIGHT AND DISCLAIMER - Copyright (C) 2007-2015 Proxmox Server Solutions GmbH +Copyright (C) 2007-2015 Proxmox Server Solutions GmbH - This program is free software: you can redistribute it and/or modify it +This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 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 - Affero General Public License for more details. +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 +Affero General Public License for more details. - You should have received a copy of the GNU Affero General Public - License along with this program. If not, see - . +You should have received a copy of the GNU Affero General Public +License along with this program. If not, see +.