mirror of https://github.com/jimsalterjrs/sanoid
Compare commits
14 Commits
9b7f6540a1
...
fb91f90013
| Author | SHA1 | Date |
|---|---|---|
|
|
fb91f90013 | |
|
|
2c98f0501c | |
|
|
0f3a9c94d9 | |
|
|
a6728e48de | |
|
|
393a4672e5 | |
|
|
efd52f416d | |
|
|
749490830f | |
|
|
b50e55c17a | |
|
|
acbf5daa43 | |
|
|
d493ef7592 | |
|
|
876685a636 | |
|
|
b1c2bcf859 | |
|
|
41ee154d2c | |
|
|
a61808d3bf |
87
INSTALL.md
87
INSTALL.md
|
|
@ -8,7 +8,8 @@
|
||||||
- [Debian/Ubuntu](#debianubuntu)
|
- [Debian/Ubuntu](#debianubuntu)
|
||||||
- [RHEL/CentOS/AlmaLinux](#RHEL/CentOS/AlmaLinux)
|
- [RHEL/CentOS/AlmaLinux](#RHEL/CentOS/AlmaLinux)
|
||||||
- [FreeBSD](#freebsd)
|
- [FreeBSD](#freebsd)
|
||||||
- [Alpine Linux / busybox](#alpine-Linux-busybox-based-distributions)
|
- [Alpine Linux / busybox](#alpine-Linux-or-busybox-based-distributions)
|
||||||
|
- [OmniOS](#OmniOS)
|
||||||
- [Other OSes](#other-oses)
|
- [Other OSes](#other-oses)
|
||||||
- [Configuration](#configuration)
|
- [Configuration](#configuration)
|
||||||
- [Sanoid](#sanoid)
|
- [Sanoid](#sanoid)
|
||||||
|
|
@ -175,13 +176,95 @@ pkg install p5-Config-Inifiles p5-Capture-Tiny pv mbuffer lzop sanoid
|
||||||
|
|
||||||
* See note about tcsh unpleasantness and other things in FREEBSD.readme
|
* See note about tcsh unpleasantness and other things in FREEBSD.readme
|
||||||
|
|
||||||
## Alpine Linux / busybox based distributions
|
## Alpine Linux or busybox based distributions
|
||||||
|
|
||||||
The busybox implementation of ps is lacking needed arguments so a proper ps program needs to be installed.
|
The busybox implementation of ps is lacking needed arguments so a proper ps program needs to be installed.
|
||||||
For Alpine Linux this can be done with:
|
For Alpine Linux this can be done with:
|
||||||
|
|
||||||
`apk --no-cache add procps`
|
`apk --no-cache add procps`
|
||||||
|
|
||||||
|
## OmniOS
|
||||||
|
|
||||||
|
Used with OmniOS r34, r36 and r37 (with napp-it installed). Hence, we presume you have a standard perl installation etc.
|
||||||
|
|
||||||
|
1. Install prerequisites: Perl module Config::IniFiles, ssh, pv, gzip, lzop, and mbuffer
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# install/update standard programs
|
||||||
|
pfexec pkg install openssh gzip mbuffer pipe-viewer
|
||||||
|
|
||||||
|
# include OpenCSW repository
|
||||||
|
pfexec pkg set-publisher -G '*' -g https://sfe.opencsw.org/localhostomnios localhostomnios
|
||||||
|
|
||||||
|
# install LZOP (from OpenCSW)
|
||||||
|
pfexec pkg install lzop
|
||||||
|
|
||||||
|
# install Perl modules
|
||||||
|
pfexec perl -MCPAN -e shell
|
||||||
|
install CPAN ## update CPAN
|
||||||
|
reload cpan ## reload
|
||||||
|
|
||||||
|
install inc::latest ## not sure if required
|
||||||
|
install IO::Scalar ## not sure if required
|
||||||
|
install Config::IniFiles
|
||||||
|
install Capture::Tiny
|
||||||
|
install Data::Dumper ## not sure if required, may be installed already
|
||||||
|
install File::Path ## not sure if required, may be installed already
|
||||||
|
install Getopt::Long ## not sure if required
|
||||||
|
install Pod::Usage ## not sure if required
|
||||||
|
install Time::Local ## not sure if required
|
||||||
|
exit
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Download and clone the Sanoid repo:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# install git
|
||||||
|
pfexec pkg install git
|
||||||
|
|
||||||
|
# Tip: download the repo as root to avoid changing permissions later
|
||||||
|
pfexec git clone https://github.com/jimsalterjrs/sanoid.git
|
||||||
|
cd sanoid
|
||||||
|
|
||||||
|
# checkout latest stable release or stay on master for bleeding edge stuff (but expect bugs!)
|
||||||
|
pfexec git checkout $(git tag | grep "^v" | tail -n 1)
|
||||||
|
|
||||||
|
# patch syncoid, so that it correctly recognises the "zfs resume" capability under OmniOS (see https://github.com/jimsalterjrs/sanoid/issues/554)
|
||||||
|
<< $avail{'sourceresume'} = system("$sourcessh $resumechkcmd $srcpool 2>/dev/null | grep '\\(active\\|enabled\\)' >/dev/null 2>&1");
|
||||||
|
>> $avail{'sourceresume'} = system("$sourcessh $resumechkcmd $srcpool 2>/dev/null | grep -E '^(active|enabled)' >/dev/null 2>&1");
|
||||||
|
<< $avail{'targetresume'} = system("$targetssh $resumechkcmd $dstpool 2>/dev/null | grep '\\(active\\|enabled\\)' >/dev/null 2>&1");
|
||||||
|
>> $avail{'targetresume'} = system("$targetssh $resumechkcmd $dstpool 2>/dev/null | grep -E '^(active|enabled)' >/dev/null 2>&1");
|
||||||
|
|
||||||
|
# most likely not required, but make the executables eXecutable
|
||||||
|
pfexec chmod +x sanoid syncoid findoid sleepymutex
|
||||||
|
|
||||||
|
# Install the executables into /opt/sanoid
|
||||||
|
pfexec mkdir /opt/sanoid
|
||||||
|
pfexec cp sanoid syncoid findoid sleepymutex /opt/sanoid
|
||||||
|
|
||||||
|
# add symbolic links to executables to a directory in $path
|
||||||
|
pfexec ln -s /opt/sanoid/sanoid /usr/bin/sanoid & pfexec ln -s /opt/sanoid/syncoid /usr/bin/syncoid & pfexec ln -s /opt/sanoid/findoid /usr/bin/findoid & pfexec ln -s /opt/sanoid/sleepymutex /usr/bin/sleepymutex
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Create the config directory /etc/sanoid, put default sanoid files there, and create and edit sanoid.conf:
|
||||||
|
```bash
|
||||||
|
# Create the config directory
|
||||||
|
pfexec mkdir /etc/sanoid
|
||||||
|
|
||||||
|
# Copy default config and sample config
|
||||||
|
pfexec cp sanoid.defaults.conf sanoid.conf /etc/sanoid/sanoid.example.conf
|
||||||
|
|
||||||
|
# Create a blank config file
|
||||||
|
pfexec touch /etc/sanoid/sanoid.conf
|
||||||
|
## and edit it (using e.g. nano as editor):
|
||||||
|
pfexec nano /etc/sanoid/sanoid.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
Further steps (not OmniOS specific):
|
||||||
|
- set up SSH connections between two remote hosts
|
||||||
|
- create a cron job that runs sanoid --cron --quiet periodically
|
||||||
|
|
||||||
|
=======
|
||||||
## MacOS
|
## MacOS
|
||||||
|
|
||||||
Install prerequisite software:
|
Install prerequisite software:
|
||||||
|
|
|
||||||
36
sanoid
36
sanoid
|
|
@ -17,6 +17,7 @@ use Getopt::Long qw(:config auto_version auto_help);
|
||||||
use Pod::Usage; # pod2usage
|
use Pod::Usage; # pod2usage
|
||||||
use Time::Local; # to parse dates in reverse
|
use Time::Local; # to parse dates in reverse
|
||||||
use Capture::Tiny ':all';
|
use Capture::Tiny ':all';
|
||||||
|
use POSIX 'strftime';
|
||||||
|
|
||||||
my %args = (
|
my %args = (
|
||||||
"configdir" => "/etc/sanoid",
|
"configdir" => "/etc/sanoid",
|
||||||
|
|
@ -616,7 +617,11 @@ sub take_snapshots {
|
||||||
my @snapshots;
|
my @snapshots;
|
||||||
|
|
||||||
foreach my $type (@types) {
|
foreach my $type (@types) {
|
||||||
my $snapname = "autosnap_$datestamp{'sortable'}_$type";
|
my $sortable = strftime($config{$dataset}{'datestamp_format'}, localtime($datestamp{'unix_time'}));
|
||||||
|
my $snapname = $config{$dataset}{'snapname_format'};
|
||||||
|
$snapname =~ s/IDENTIFIER/$config{$dataset}{'identifier'}/g;
|
||||||
|
$snapname =~ s/DATE/$sortable/g;
|
||||||
|
$snapname =~ s/TYPE/$type/g;
|
||||||
push(@snapshots, $snapname);
|
push(@snapshots, $snapname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -906,12 +911,13 @@ sub getsnaps {
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach my $snap (@rawsnaps) {
|
foreach my $snap (@rawsnaps) {
|
||||||
my ($fs,$snapname,$snapdate) = ($snap =~ m/(.*)\@(.*ly)\t*creation\t*(\d*)/);
|
my ($fs,$snapname,$snapdate) = ($snap =~ m/(.*)\@(.*?)\t*creation\t*(\d*)/);
|
||||||
|
|
||||||
# avoid pissing off use warnings
|
# avoid pissing off use warnings
|
||||||
if (defined $snapname) {
|
if (defined $snapname) {
|
||||||
my ($snaptype) = ($snapname =~ m/.*_(\w*ly)/);
|
if ($snapname =~ /$config{$fs}{'identifier'}/) {
|
||||||
if ($snapname =~ /^autosnap/) {
|
my @types = qw(yearly monthly weekly daily hourly frequently);
|
||||||
|
my ($snaptype) = grep { $snapname =~ /$_/ } @types;
|
||||||
$snaps{$fs}{$snapname}{'ctime'}=$snapdate;
|
$snaps{$fs}{$snapname}{'ctime'}=$snapdate;
|
||||||
$snaps{$fs}{$snapname}{'type'}=$snaptype;
|
$snaps{$fs}{$snapname}{'type'}=$snaptype;
|
||||||
}
|
}
|
||||||
|
|
@ -1084,11 +1090,9 @@ sub init {
|
||||||
@datasets = getchilddatasets($config{$section}{'path'});
|
@datasets = getchilddatasets($config{$section}{'path'});
|
||||||
DATASETS: foreach my $dataset(@datasets) {
|
DATASETS: foreach my $dataset(@datasets) {
|
||||||
if (! @cachedatasets) {
|
if (! @cachedatasets) {
|
||||||
push (@updatedatasets, $dataset);
|
push (@updatedatasets, "$dataset\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
chomp $dataset;
|
|
||||||
|
|
||||||
if ($zfsRecursive) {
|
if ($zfsRecursive) {
|
||||||
# don't try to take the snapshot ourself, recursive zfs snapshot will take care of that
|
# don't try to take the snapshot ourself, recursive zfs snapshot will take care of that
|
||||||
$config{$dataset}{'autosnap'} = 0;
|
$config{$dataset}{'autosnap'} = 0;
|
||||||
|
|
@ -1150,16 +1154,15 @@ sub init {
|
||||||
|
|
||||||
sub get_date {
|
sub get_date {
|
||||||
my %datestamp;
|
my %datestamp;
|
||||||
($datestamp{'sec'},$datestamp{'min'},$datestamp{'hour'},$datestamp{'mday'},$datestamp{'mon'},$datestamp{'year'},$datestamp{'wday'},$datestamp{'yday'},$datestamp{'isdst'}) = localtime(time);
|
$datestamp{'unix_time'} = time();
|
||||||
|
($datestamp{'sec'},$datestamp{'min'},$datestamp{'hour'},$datestamp{'mday'},$datestamp{'mon'},$datestamp{'year'},$datestamp{'wday'},$datestamp{'yday'},$datestamp{'isdst'}) = localtime($datestamp{'unix_time'});
|
||||||
|
|
||||||
$datestamp{'year'} += 1900;
|
$datestamp{'year'} += 1900;
|
||||||
$datestamp{'unix_time'} = (((((((($datestamp{'year'} - 1971) * 365) + $datestamp{'yday'}) * 24) + $datestamp{'hour'}) * 60) + $datestamp{'min'}) * 60) + $datestamp{'sec'};
|
|
||||||
$datestamp{'sec'} = sprintf ("%02u", $datestamp{'sec'});
|
$datestamp{'sec'} = sprintf ("%02u", $datestamp{'sec'});
|
||||||
$datestamp{'min'} = sprintf ("%02u", $datestamp{'min'});
|
$datestamp{'min'} = sprintf ("%02u", $datestamp{'min'});
|
||||||
$datestamp{'hour'} = sprintf ("%02u", $datestamp{'hour'});
|
$datestamp{'hour'} = sprintf ("%02u", $datestamp{'hour'});
|
||||||
$datestamp{'mday'} = sprintf ("%02u", $datestamp{'mday'});
|
$datestamp{'mday'} = sprintf ("%02u", $datestamp{'mday'});
|
||||||
$datestamp{'mon'} = sprintf ("%02u", ($datestamp{'mon'} + 1));
|
$datestamp{'mon'} = sprintf ("%02u", ($datestamp{'mon'} + 1));
|
||||||
$datestamp{'noseconds'} = "$datestamp{'year'}-$datestamp{'mon'}-$datestamp{'mday'}_$datestamp{'hour'}:$datestamp{'min'}";
|
|
||||||
$datestamp{'sortable'} = "$datestamp{'noseconds'}:$datestamp{'sec'}";
|
|
||||||
return %datestamp;
|
return %datestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1691,7 +1694,7 @@ sub getchilddatasets {
|
||||||
my $getchildrencmd = "$mysudocmd $zfs list -o name -t filesystem,volume -Hr $fs |";
|
my $getchildrencmd = "$mysudocmd $zfs list -o name -t filesystem,volume -Hr $fs |";
|
||||||
if ($args{'debug'}) { print "DEBUG: getting list of child datasets on $fs using $getchildrencmd...\n"; }
|
if ($args{'debug'}) { print "DEBUG: getting list of child datasets on $fs using $getchildrencmd...\n"; }
|
||||||
open FH, $getchildrencmd;
|
open FH, $getchildrencmd;
|
||||||
my @children = <FH>;
|
chomp( my @children = <FH> );
|
||||||
close FH;
|
close FH;
|
||||||
|
|
||||||
# parent dataset is the first element
|
# parent dataset is the first element
|
||||||
|
|
@ -1781,25 +1784,26 @@ sub addcachedsnapshots {
|
||||||
|
|
||||||
copy($cache, "$cache.tmp") or die "Could not copy to $cache.tmp!\n";
|
copy($cache, "$cache.tmp") or die "Could not copy to $cache.tmp!\n";
|
||||||
|
|
||||||
open FH, ">> $cache.tmp" or die "Could not write to $cache.tmp!\n";
|
open my $fh, ">> $cache.tmp" or die "Could not write to $cache.tmp!\n";
|
||||||
while((my $snap, my $details) = each(%taken)) {
|
while((my $snap, my $details) = each(%taken)) {
|
||||||
my @parts = split("@", $snap, 2);
|
my @parts = split("@", $snap, 2);
|
||||||
|
|
||||||
my $suffix = $parts[1] . "\tcreation\t" . $details->{time} . "\t-";
|
my $suffix = $parts[1] . "\tcreation\t" . $details->{time} . "\t-";
|
||||||
my $dataset = $parts[0];
|
my $dataset = $parts[0];
|
||||||
|
|
||||||
print FH "${dataset}\@${suffix}\n";
|
print $fh "${dataset}\@${suffix}\n";
|
||||||
|
|
||||||
if ($details->{recursive}) {
|
if ($details->{recursive}) {
|
||||||
my @datasets = getchilddatasets($dataset);
|
my @datasets = getchilddatasets($dataset);
|
||||||
|
|
||||||
foreach my $dataset(@datasets) {
|
foreach my $dataset(@datasets) {
|
||||||
print FH "${dataset}\@${suffix}\n";
|
print "${dataset}\@${suffix}\n";
|
||||||
|
print $fh "${dataset}\@${suffix}\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
close FH;
|
close $fh;
|
||||||
|
|
||||||
# preserve mtime of cache for expire check
|
# preserve mtime of cache for expire check
|
||||||
my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($cache);
|
my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($cache);
|
||||||
|
|
|
||||||
|
|
@ -113,3 +113,9 @@ yearly_crit = 0
|
||||||
# for overriding these values one needs to specify them in a root pool section! ([tank]\n ...)
|
# for overriding these values one needs to specify them in a root pool section! ([tank]\n ...)
|
||||||
capacity_warn = 80
|
capacity_warn = 80
|
||||||
capacity_crit = 95
|
capacity_crit = 95
|
||||||
|
|
||||||
|
# snapshot name formats can be overridden
|
||||||
|
identifier = autosnap
|
||||||
|
# strftime-style format string
|
||||||
|
datestamp_format = %Y-%m-%d_%H:%M:%S
|
||||||
|
snapname_format = IDENTIFIER_DATE_TYPE
|
||||||
|
|
|
||||||
2
syncoid
2
syncoid
|
|
@ -178,7 +178,7 @@ if (length $args{'insecure-direct-connection'}) {
|
||||||
$directlisten = $args{'insecure-direct-connection'};
|
$directlisten = $args{'insecure-direct-connection'};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scalar @parts == 3) {
|
if (scalar @parts >= 3) {
|
||||||
$directtimeout = $parts[2];
|
$directtimeout = $parts[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue