| 51 |
rodolico |
1 |
#! /usr/bin/env perl
|
|
|
2 |
|
| 58 |
rodolico |
3 |
# Test script for ZFS_Utils module
|
|
|
4 |
# if run with no parameters, wil do smoke tests of functions (dry-run safe)
|
|
|
5 |
# if run with 2 or more parameters, will read snapshot files and process them
|
|
|
6 |
# Usage:
|
|
|
7 |
# perl test_zfs_utils.pl source_snapshots.txt target_snapshots.txt [dataset_name]
|
|
|
8 |
# - source_snapshots.txt: file containing list of source snapshots (one per line)
|
|
|
9 |
# - target_snapshots.txt: file containing list of target snapshots (one per line)
|
|
|
10 |
# - dataset_name: (optional) dataset name to process; if not provided, will
|
|
|
11 |
# attempt to auto-detect from snapshot names
|
|
|
12 |
|
| 51 |
rodolico |
13 |
use strict;
|
|
|
14 |
use warnings;
|
|
|
15 |
use FindBin;
|
|
|
16 |
use lib "$FindBin::Bin";
|
|
|
17 |
use lib "$FindBin::Bin/.."; # ensure module path
|
|
|
18 |
|
|
|
19 |
use ZFS_Utils qw(runCmd loadConfig logMsg makeReplicateCommands $logFileName $displayLogsOnConsole);
|
|
|
20 |
|
|
|
21 |
# Test script for ZFS_Utils (dry-run safe)
|
|
|
22 |
|
|
|
23 |
$displayLogsOnConsole = 1;
|
|
|
24 |
$logFileName = '/tmp/zfs_utils_test.log';
|
|
|
25 |
unlink $logFileName if -f $logFileName;
|
|
|
26 |
|
| 58 |
rodolico |
27 |
# Check for command line arguments (source and target snapshot files, and optional dataset)
|
|
|
28 |
if (@ARGV >= 2) {
|
|
|
29 |
my ($source_file, $target_file, $dataset_filter) = @ARGV;
|
|
|
30 |
|
|
|
31 |
print "Reading snapshot files from command line:\n";
|
|
|
32 |
print " Source: $source_file\n";
|
|
|
33 |
print " Target: $target_file\n";
|
|
|
34 |
print " Dataset filter: " . ($dataset_filter || "(auto-detect)") . "\n";
|
|
|
35 |
|
|
|
36 |
# Read source snapshots (all snapshots on source dataset)
|
|
|
37 |
open my $src_fh, '<', $source_file or die "Cannot open source file '$source_file': $!";
|
|
|
38 |
my @source_snaps = <$src_fh>;
|
|
|
39 |
chomp @source_snaps;
|
|
|
40 |
close $src_fh;
|
|
|
41 |
|
|
|
42 |
# Read target snapshots (status - snapshots already on target)
|
|
|
43 |
open my $tgt_fh, '<', $target_file or die "Cannot open target file '$target_file': $!";
|
|
|
44 |
my @target_snaps = <$tgt_fh>;
|
|
|
45 |
chomp @target_snaps;
|
|
|
46 |
close $tgt_fh;
|
|
|
47 |
|
|
|
48 |
print "\nSource snapshots (" . scalar(@source_snaps) . " entries):\n";
|
|
|
49 |
if (@source_snaps) {
|
|
|
50 |
my $end = @source_snaps > 5 ? 4 : $#source_snaps;
|
|
|
51 |
print " $_\n" for @source_snaps[0..$end];
|
|
|
52 |
print " ...\n" if @source_snaps > 5;
|
|
|
53 |
}
|
|
|
54 |
|
|
|
55 |
print "\nTarget snapshots (" . scalar(@target_snaps) . " entries):\n";
|
|
|
56 |
if (@target_snaps) {
|
|
|
57 |
my $end = @target_snaps > 5 ? 4 : $#target_snaps;
|
|
|
58 |
print " $_\n" for @target_snaps[0..$end];
|
|
|
59 |
print " ...\n" if @target_snaps > 5;
|
|
|
60 |
}
|
|
|
61 |
|
|
|
62 |
# Determine dataset to process and extract parent directories
|
|
|
63 |
my $dataset;
|
|
|
64 |
my $source_parent = '';
|
|
|
65 |
my $target_parent = '';
|
|
|
66 |
|
|
|
67 |
if ($dataset_filter) {
|
|
|
68 |
# Use explicitly provided dataset
|
|
|
69 |
$dataset = $dataset_filter;
|
|
|
70 |
print "\nUsing provided dataset: $dataset\n";
|
|
|
71 |
} else {
|
|
|
72 |
# Extract dataset name from first source snapshot
|
|
|
73 |
if (@source_snaps && $source_snaps[0] =~ /^([^@]+)@/) {
|
|
|
74 |
my $full_path = $1;
|
|
|
75 |
# Extract just the dataset name (last component)
|
|
|
76 |
$dataset = $full_path;
|
|
|
77 |
$dataset =~ s/.*\///;
|
|
|
78 |
# Extract parent directory if present
|
|
|
79 |
if ($full_path =~ /^(.+)\/[^\/]+$/) {
|
|
|
80 |
$source_parent = $1;
|
|
|
81 |
}
|
|
|
82 |
}
|
|
|
83 |
|
|
|
84 |
unless ($dataset) {
|
|
|
85 |
die "Error: Could not extract dataset name from source snapshots\n";
|
|
|
86 |
}
|
|
|
87 |
|
|
|
88 |
print "\nAuto-detected dataset: $dataset\n";
|
|
|
89 |
}
|
|
|
90 |
|
|
|
91 |
# Extract target parent from first target snapshot if available
|
|
|
92 |
if (@target_snaps && $target_snaps[0] =~ /^([^@]+)@/) {
|
|
|
93 |
my $full_path = $1;
|
|
|
94 |
# Extract parent directory if present
|
|
|
95 |
if ($full_path =~ /^(.+)\/[^\/]+$/) {
|
|
|
96 |
$target_parent = $1;
|
|
|
97 |
}
|
|
|
98 |
}
|
|
|
99 |
|
|
|
100 |
print "Source parent: " . ($source_parent || "(none)") . "\n";
|
|
|
101 |
print "Target parent: " . ($target_parent || "(none)") . "\n";
|
|
|
102 |
|
|
|
103 |
# Process with makeReplicateCommands
|
|
|
104 |
my @newstatus;
|
|
|
105 |
my $cmds = makeReplicateCommands(\@source_snaps, \@target_snaps, $dataset, $source_parent, $target_parent, \@newstatus);
|
|
|
106 |
|
|
|
107 |
print "\nGenerated replicate commands:\n";
|
|
|
108 |
if (ref($cmds) eq 'HASH') {
|
|
|
109 |
for my $fs (sort keys %$cmds) {
|
|
|
110 |
print " $fs => $cmds->{$fs}\n";
|
|
|
111 |
}
|
|
|
112 |
} elsif (ref($cmds) eq 'ARRAY') {
|
|
|
113 |
print " (No commands generated - returned empty array)\n";
|
|
|
114 |
} else {
|
|
|
115 |
print " (Unexpected return type)\n";
|
|
|
116 |
}
|
|
|
117 |
|
|
|
118 |
print "\nNew status entries (" . scalar(@newstatus) . "):\n";
|
|
|
119 |
print " $_\n" for @newstatus;
|
|
|
120 |
|
|
|
121 |
print "\nSnapshot file processing complete.\n";
|
|
|
122 |
exit 0;
|
|
|
123 |
}
|
|
|
124 |
|
| 51 |
rodolico |
125 |
print "Running ZFS_Utils smoke tests (dry-run safe)\n";
|
|
|
126 |
|
|
|
127 |
# 1) runCmd in scalar context
|
|
|
128 |
my $echo_out = runCmd('printf', 'hello_from_runCmd');
|
|
|
129 |
print "runCmd scalar output: [$echo_out]\n";
|
|
|
130 |
|
|
|
131 |
# 2) runCmd in list context
|
|
|
132 |
my @ls = runCmd('printf', "line1\nline2\n");
|
|
|
133 |
print "runCmd list output: ". join('|', @ls) ."\n";
|
|
|
134 |
|
|
|
135 |
# 3) loadConfig using a temporary YAML file
|
|
|
136 |
my $tmp_yaml = '/tmp/test_zfs_utils.conf.yaml';
|
|
|
137 |
open my $yh, '>', $tmp_yaml or die "Cannot write $tmp_yaml: $!";
|
|
|
138 |
print $yh "transport:\n mount_point: /tmp/mount_point_test\n dryrun: 1\n";
|
|
|
139 |
print $yh "datasets:\n demo:\n source: pool\n target: backup\n";
|
|
|
140 |
close $yh;
|
|
|
141 |
|
|
|
142 |
my $loaded = loadConfig($tmp_yaml, { default_key => 'default_value' });
|
|
|
143 |
print "Loaded config keys: ". join(', ', sort keys %$loaded) ."\n";
|
|
|
144 |
print "transport.mount_point = " . ($loaded->{transport}{mount_point} // '(undef)') ."\n";
|
|
|
145 |
|
|
|
146 |
# 4) makeReplicateCommands - sample snapshot lines
|
|
|
147 |
my @snaplines = (
|
|
|
148 |
'tank/home@2025-12-01-1d extra',
|
|
|
149 |
'tank/home@2025-12-07-1d extra',
|
|
|
150 |
'tank/data@2025-11-01-7d',
|
|
|
151 |
);
|
|
|
152 |
my @status = ('tank/home@2025-11-30-1d');
|
|
|
153 |
my @newstatus;
|
| 58 |
rodolico |
154 |
my $cmds = makeReplicateCommands(\@snaplines, \@status, 'home', 'tank', 'tank', \@newstatus);
|
| 51 |
rodolico |
155 |
print "Generated replicate commands:\n";
|
|
|
156 |
for my $fs (sort keys %$cmds) {
|
|
|
157 |
print " $fs => $cmds->{$fs}\n";
|
|
|
158 |
}
|
|
|
159 |
print "New status entries: ". join(', ', @newstatus) ."\n";
|
|
|
160 |
|
|
|
161 |
# 5) logMsg test
|
|
|
162 |
logMsg("Test log entry from test_zfs_utils.pl");
|
|
|
163 |
print "Log file written to $logFileName (if enabled).\n";
|
|
|
164 |
|
|
|
165 |
print "Smoke tests complete.\n";
|
|
|
166 |
|
|
|
167 |
# Clean up temporary YAML
|
|
|
168 |
unlink $tmp_yaml;
|
|
|
169 |
|
|
|
170 |
1;
|