diff --git a/CHANGELIST b/CHANGELIST index e149556..20c4aa1 100644 --- a/CHANGELIST +++ b/CHANGELIST @@ -1,3 +1,5 @@ +1.1.0 woooo - working recursive definitions in Sanoid! Also intelligent config errors in Sanoid; will die with errors if unknown config value is set. + 1.0.20 greatly cleaned up config parsing in sanoid, got rid of 'hardcoded defaults' in favor of /etc/sanoid/sanoid.defaults.conf 1.0.19 working recursive sync (sync specified dataset and all child datasets, ie pool/ds, pool/ds/1, pool, ds/1/a, pool/ds/2 ...) with --recursive or -r in syncoid! diff --git a/VERSION b/VERSION index c2320f5..9084fa2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.20 +1.1.0 diff --git a/sanoid b/sanoid index 764db65..efd4a8c 100755 --- a/sanoid +++ b/sanoid @@ -4,7 +4,7 @@ # from http://www.gnu.org/licenses/gpl-3.0.html on 2014-11-17. A copy should also be available in this # project's Git repository at https://github.com/jimsalterjrs/sanoid/blob/master/LICENSE. -my $version = '1.0.19'; +my $version = '1.1.0'; use strict; use Config::IniFiles; # read samba-style conf file @@ -327,9 +327,11 @@ sub take_snapshots { if ( (scalar(@newsnaps)) > 0) { foreach my $snap ( @newsnaps ) { if ($args{'verbose'}) { print "taking snapshot $snap\n"; } - if (!$args{'readonly'}) { system($zfs, "snapshot", "$snap"); } - # make sure we don't end up with multiple snapshots with the same ctime - sleep 1; + if (!$args{'readonly'}) { + system($zfs, "snapshot", "$snap"); + # make sure we don't end up with multiple snapshots with the same ctime + sleep 1; + } } $forcecacheupdate = 1; %snaps = getsnaps(%config,$cacheTTL,$forcecacheupdate); @@ -525,30 +527,40 @@ sub init { tie my %ini, 'Config::IniFiles', ( -file => $conf_file ); # we'll use these later to normalize potentially true and false values on any toggle keys - my @toggles = ('autosnap','autoprune','monitor_dont_warn','monitor_dont_crit','monitor'); + my @toggles = ('autosnap','autoprune','monitor_dont_warn','monitor_dont_crit','monitor','recursive'); my @istrue=(1,"true","True","TRUE","yes","Yes","YES","on","On","ON"); my @isfalse=(0,"false","False","FALSE","no","No","NO","off","Off","OFF"); foreach my $section (keys %ini) { - if ($section =~ /^template_/) { next; } # don't process templates directly - - # set default values from %defaults, which can then be overriden by template - # and/or local settings within the module. - foreach my $key (keys %{$defaults{'template_default'}}) { - if (! ($key =~ /template/)) { - if ($args{'debug'}) { print "INFO: setting $key on $section from $default_conf_file.\n"; } - $config{$section}{$key} = $defaults{'template_default'}{$key}; + # first up - die with honor if unknown parameters are set in any modules or templates by the user. + foreach my $key (keys %{$ini{$section}}) { + if (! defined ($defaults{'template_default'}{$key})) { + die "FATAL ERROR: I don't understand the setting $key you've set in \[$section\] in $conf_file.\n"; } } - - # override with values from user-defined default template, if any - - foreach my $key (keys %{$ini{'template_default'}}) { - if (! ($key =~ /template/)) { - if ($args{'debug'}) { print "INFO: overriding $key on $section with value from user-defined default template.\n"; } - $config{$section}{$key} = $ini{'template_default'}{$key}; + if ($section =~ /^template_/) { next; } # don't process templates directly + + # only set defaults on sections that haven't already been initialized - this allows us to override values + # for sections directly when they've already been defined recursively, without starting them over from scratch. + if (! defined ($config{$section}{'initialized'})) { + if ($args{'debug'}) { print "DEBUG: initializing \$config\{$section\} with default values from $default_conf_file.\n"; } + # set default values from %defaults, which can then be overriden by template + # and/or local settings within the module. + foreach my $key (keys %{$defaults{'template_default'}}) { + if (! ($key =~ /template|recursive/)) { + $config{$section}{$key} = $defaults{'template_default'}{$key}; + } + } + + # override with values from user-defined default template, if any + + foreach my $key (keys %{$ini{'template_default'}}) { + if (! ($key =~ /template|recursive/)) { + if ($args{'debug'}) { print "DEBUG: overriding $key on $section with value from user-defined default template.\n"; } + $config{$section}{$key} = $ini{'template_default'}{$key}; + } } } @@ -559,8 +571,8 @@ sub init { foreach my $rawtemplate (@templates) { my $template = 'template_'.$rawtemplate; foreach my $key (keys %{$ini{$template}}) { - if (! ($key =~ /template/)) { - if ($args{'debug'}) { print "INFO: overriding $key on $section with value from user-defined template $template.\n"; } + if (! ($key =~ /template|recursive/)) { + if ($args{'debug'}) { print "DEBUG: overriding $key on $section with value from user-defined template $template.\n"; } $config{$section}{$key} = $ini{$template}{$key}; } } @@ -569,8 +581,8 @@ sub init { # override with any locally set values in the module itself foreach my $key (keys %{$ini{$section}} ) { - if (! ($key =~ /template/)) { - if ($args{'debug'}) { print "INFO: overriding $key on $section with value directly set in module.\n"; } + if (! ($key =~ /template|recursive/)) { + if ($args{'debug'}) { print "DEBUG: overriding $key on $section with value directly set in module.\n"; } $config{$section}{$key} = $ini{$section}{$key}; } } @@ -591,6 +603,26 @@ sub init { } else { $config{$section}{'path'} = $section; } + + # how 'bout some recursion? =) + my @datasets; + if ($ini{$section}{'recursive'}) { + @datasets = getchilddatasets($config{$section}{'path'}); + foreach my $dataset(@datasets) { + chomp $dataset; + foreach my $key (keys %{$config{$section}} ) { + if (! ($key =~ /template|recursive/)) { + if ($args{'debug'}) { print "DEBUG: recursively setting $key from $section to $dataset.\n"; } + $config{$dataset}{$key} = $config{$section}{$key}; + } + } + $config{$dataset}{'path'} = $dataset; + $config{$dataset}{'initialized'} = 1; + } + } + + + } return %config; @@ -1043,3 +1075,17 @@ sub getargs { return %args; } +sub getchilddatasets { + # for later, if we make sanoid itself support sudo use + my $fs = shift; + my $mysudocmd; + + my $getchildrencmd = "$mysudocmd $zfs list -o name -Hr $fs |"; + if ($args{'debug'}) { print "DEBUG: getting list of child datasets on $fs using $getchildrencmd...\n"; } + open FH, $getchildrencmd; + my @children = ; + close FH; + + return @children; +} + diff --git a/sanoid.conf b/sanoid.conf index 7a6c410..c2c8c57 100644 --- a/sanoid.conf +++ b/sanoid.conf @@ -37,7 +37,7 @@ hourly = 30 daily = 90 monthly = 12 - yearlies = 0 + yearly = 0 ### don't take new snapshots - snapshots on backup ### datasets are replicated in from source, not diff --git a/syncoid b/syncoid index 5972a3e..a2474c5 100755 --- a/syncoid +++ b/syncoid @@ -4,7 +4,7 @@ # from http://www.gnu.org/licenses/gpl-3.0.html on 2014-11-17. A copy should also be available in this # project's Git repository at https://github.com/jimsalterjrs/sanoid/blob/master/LICENSE. -my $version = '1.0.19'; +my $version = '1.1.0'; use strict; use Data::Dumper; @@ -82,7 +82,7 @@ sub getchilddatasets { if ($isroot) { $mysudocmd = ''; } else { $mysudocmd = $sudocmd; } if ($rhost ne '') { $rhost = "$sshcmd $rhost"; } - my $getchildrencmd = "$rhost $mysudocmd $zfscmd list -o name -Hpr $fs |"; + my $getchildrencmd = "$rhost $mysudocmd $zfscmd list -o name -Hr $fs |"; if ($debug) { print "DEBUG: getting list of child datasets on $fs using $getchildrencmd...\n"; } open FH, $getchildrencmd; my @children = ;