| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394 |
- #!/usr/node/bin/node
- "use strict";
- var CronJob = require('cron').CronJob;
- var async = require('async');
- var fs = require('fs')
- var minimatch = require('minimatch')
- var util = require('util');
- var zfs = require('zfs')
- // Generate a snapshot name given a base.
- function snapshotName(base) {
- var when = (new Date()).toISOString().replace(/[-:]|(\.\d\d\d)/g, '');
- return base + '-' + when;
- }
- // Create a snapshot on the specified dataset with the specified name, exluding all datasets in excl.
- function createSnapshot(ds, sn, cb) {
- util.log('Create snapshot ' + ds + '@' + sn);
- zfs.snapshot({ dataset: ds, name: sn, recursive: true }, cb);
- }
- // Keep only num snapshots with the specified base on the specified dataset
- function cleanSnapshots(patterns, base, num, cb) {
- zfs.list({ type: 'snapshot' }, function (err, snaps) {
- var allSnaps = {};
- patterns.forEach(function (pat) {
- snaps.forEach(function (s) {
- var fields = s.name.split('@');
- var parts = fields[1].split('-');
- if (minimatch(fields[0], pat) && parts[0] === base) {
- var l = allSnaps[fields[0]] || [];
- l.push(s.name);
- allSnaps[fields[0]] = l;
- }
- });
- });
- async.eachLimit(Object.keys(allSnaps), 10, function (dsName, cb) {
- var snapNames = allSnaps[dsName];
- if (snapNames.length > num) {
- snapNames.sort();
- // Get the ones that exceed the specified number of snapshots.
- var toDestroy = snapNames.slice(0, snapNames.length - num);
- // Destroy them, one after the other.
- async.eachSeries(toDestroy, function (sn, cb) {
- util.log('Destroy snapshot ' + sn + ' (cleaned)');
- zfs.destroy({ name: sn, recursive: true }, cb);
- }, cb);
- } else {
- cb(null);
- }
- }, cb);
- });
- }
- function loadConfig() {
- var data = fs.readFileSync(process.argv[2], 'utf-8');
- var confObj = JSON.parse(data);
- return confObj;
- }
- function snap(datasets, snapBase, num) {
- var snapId = snapshotName(snapBase);
- zfs.list({type: 'filesystem,volume'}, function (err, dss) {
- dss = dss.map(function (s) { return s.name; });
- var toSnap = dss.filter(function (ds) {
- return datasets.filter(function (pat) { return minimatch(ds, pat) }).length > 0;
- });
- async.eachLimit(toSnap, 10, function (ds, cb) {
- createSnapshot(ds, snapId, cb);
- }, function () {
- cleanSnapshots(datasets, snapBase, num, function () {
- util.log("Done " + snapId);
- });
- });
- });
- }
- var cronJobs = [];
- var conf = loadConfig();
- Object.keys(conf).forEach(function (snapBase) {
- var config = conf[snapBase];
- var job = new CronJob('00 ' + config.when, function () {
- snap(config.datasets, snapBase, parseInt(config.count, 10));
- }, null, true);
- cronJobs.push(job);
- });
- // vim: set filetype=javascript:
|