Created
February 28, 2022 19:21
-
-
Save geek-at/b27a25735bf2045856c8df3ae116911d to your computer and use it in GitHub Desktop.
PHP script that syncs remote ZFS snapshots to local dataset. Works with encrypted volumes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
Script that syncs remote zfs snapshots to a local zfs target. | |
Can sync remote encrypted datasets without ever needing to know the key on the local machine. | |
It will automatically search for the last snapshot in common with the remote host and only sync the differences. | |
If it doesn't find any local ones or has none in common, it will sync the oldest snapshot of the remote machine. | |
Meant to be run as cronjob | |
Prerequisites: | |
- ZFS on remote and local machine | |
- Passwordless SSH login from local to remote machine | |
- | |
Usage: | |
php zfs_sync.php <remote ip or hostname> <remote dataset> <local dataset> | |
eg. If I have a server called "green" and the remote dataset is data/pictures | |
we can sync to a local pool called backup/pictures using | |
php zfs_sync.php green data/pictures backup/pictures | |
*/ | |
$remoteIP = $argv[1]; | |
$remoteDataset = $argv[2]; | |
$localDataset = $argv[3]; | |
if($argc!=4) exit('usage: php sync.php <remote ip> <remote dataset> <local dataset>'.PHP_EOL); | |
$tmplock = '/tmp/'.md5($remoteIP.$remoteDataset.$localDataset); | |
echo "[i] ======== SYNC START FROM $remoteIP: $remoteDataset to $( hostname ): $localDataset =========\n"; | |
if(file_exists($tmplock)) | |
exit('Lockfile exists. Sync already running?'); | |
touch($tmplock); | |
$cmd_zfs = "zfs list -t snapshot $remoteDataset | cut -d ' ' -f 1 | cut -d '@' -f 2 | tail -n +2"; | |
$cmd_zfs_local = "zfs list -t snapshot $localDataset | cut -d ' ' -f 1 | cut -d '@' -f 2 | tail -n +2"; | |
$cmd_ssh = "ssh $remoteIP $cmd_zfs"; | |
$remotesnapshots = []; | |
exec($cmd_ssh,$remotesnapshots); | |
$localsnapshots = []; | |
exec($cmd_zfs_local,$localsnapshots); | |
$tosync = array_diff($remotesnapshots,$localsnapshots); | |
if(count($tosync)<1) exit("[i] Nothing to sync. Exiting\n".unlink($tmplock)); | |
$snapshot = end($tosync); | |
$lastincommon = getLastInCommon($remotesnapshots,$localsnapshots,$snapshot); | |
if(!$lastincommon) | |
{ | |
echo "[i] Don't have any snapshots of that dataset yet. I'll sync initial\n"; | |
$firstsnap = $remotesnapshots[0]; | |
$synccmd = "ssh $remoteIP zfs send -w $remoteDataset@$firstsnap | zfs receive -F $localDataset"; | |
echo "$synccmd\n"; | |
system($synccmd); | |
unlink($tmplock); | |
exit("[i] ======== SYNC END ========\n"); | |
} | |
echo "[i] Syncing from $lastincommon to $snapshot\n"; | |
$synccmd = "ssh $remoteIP zfs send -w -I $remoteDataset@$lastincommon $remoteDataset@$snapshot | zfs receive -F $localDataset"; | |
echo "$synccmd\n"; | |
//run the actual zfs command | |
system($synccmd); | |
//remove the lock file | |
unlink($tmplock); | |
echo "[i] ======== SYNC END ========\n"; | |
function getLastInCommon($remote,$local,$target) | |
{ | |
$incommon = []; | |
foreach($remote as $key=>$rsnap) | |
{ | |
foreach($local as $lkey=>$lsnap) | |
{ | |
if($rsnap == $lsnap) | |
$incommon[] = $rsnap; | |
if($lsnap==$target) break; | |
} | |
} | |
return end($incommon); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment