Compare commits

...

14 Commits

Author SHA1 Message Date
Kenny Phelps-McKeown fb91f90013
Merge b50e55c17a into 2c98f0501c 2025-08-24 16:18:49 +00:00
Jim Salter 2c98f0501c
Merge pull request #634 from asche77/patch-1
Update INSTALL.md to include OmniOS installation instructions
2025-08-24 11:29:35 -04:00
Jim Salter 0f3a9c94d9
Merge branch 'master' into patch-1 2025-08-24 11:28:40 -04:00
Jim Salter a6728e48de
Merge pull request #996 from jjakob/fix_directtimeout
syncoid: fix directtimeout in directmbuffer mode
2025-08-24 10:56:44 -04:00
Jim Salter 393a4672e5
Merge pull request #1023 from phreaker0/fix-file-handle-conflict
fixed file handle conflict
2025-08-24 10:50:58 -04:00
Christoph Klaffl efd52f416d
fixed file handle conflict 2025-08-12 14:19:47 +02:00
Jernej Jakob 749490830f
syncoid: fix directtimeout in directmbuffer mode
If --insecure-direct-connection contained 4 parts (including the
',mbuffer' at the end), the 3rd part (timeout) was silently ignored
and left at the default 60s.

Do not ignore the timeout part even in directmbuffer mode.
2025-03-31 14:54:46 +02:00
kennypm b50e55c17a full flexibility for naming template 2025-02-22 13:16:34 -05:00
kennypm acbf5daa43 custom name prefix without breaking getsnaps()
custom datestamp format
no name reordering yet as getsnaps() expects leading prefix and trailing snap type
2025-02-22 12:05:59 -05:00
asche d493ef7592
Merge branch 'jimsalterjrs:master' into patch-1 2021-10-24 18:16:46 +02:00
asche 876685a636
Update INSTALL.md 2021-04-08 21:41:45 +02:00
asche b1c2bcf859
Update INSTALL.md 2021-04-08 21:40:51 +02:00
asche 41ee154d2c
Update INSTALL.md
Add installation instructions for OmniOS; cleaned up description somewhat.
2021-04-08 21:38:46 +02:00
asche a61808d3bf
Update INSTALL.md 2021-04-04 12:28:21 +02:00
4 changed files with 112 additions and 19 deletions

View File

@ -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
View File

@ -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);

View File

@ -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

View File

@ -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];
} }