fs = require('fs')
async = require('async')
winston = require('winston')
winston.remove(winston.transports.Console)
winston.add(winston.transports.Console, {level: 'debug', timestamp:true, colorize:true})
misc_node = require('smc-util-node/misc_node')
misc = require('smc-util/misc')
{defaults, required} = misc
list_snapshots = (filesystem, cb) ->
misc_node.execute_code
command : 'sudo'
args : ['zfs', 'list', '-r', '-H', '-t', 'snapshot', filesystem]
cb : (err, output) ->
if err
cb(err)
else
v = (misc.split(x)[0].split('@')[1] for x in output.stdout.split('\n') when x.trim())
v.sort()
cb(undefined, v)
make_snapshot = (filesystem, snap, cb) ->
async.series([
(cb) ->
misc_node.execute_code
command : 'sudo'
args : ['zfs', 'snapshot', "#{filesystem}@#{snap}"]
cb : cb
(cb) ->
fs.readdir("/#{filesystem}/.zfs/snapshot/#{snap}/", cb)
], cb)
delete_snapshot = (filesystem, snap, cb) ->
misc_node.execute_code
command : 'sudo'
args : ['zfs', 'destroy', "#{filesystem}@#{snap}"]
cb : cb
INTERVALS =
five : 5
hourly : 60
daily : 60*24
weekly : 60*24*7
monthly : 60*24*7*4
exports.update_snapshots = (opts) ->
opts = defaults opts,
filesystem : required
five : 12*6
hourly : 24*7
daily : 30
weekly : 8
monthly : 6
cb : undefined
dbg = (m) -> winston.debug("snapshot('#{opts.filesystem}'): #{m}")
dbg()
snapshots = undefined
to_create = []
to_delete = []
async.series([
(cb) ->
dbg("get list of all snapshots")
list_snapshots opts.filesystem, (err, x) ->
snapshots = x; cb(err)
(cb) ->
dbg("got #{snapshots.length} snapshots")
now = new Date()
for name, interval of INTERVALS
if opts[name] <= 0
continue
v = (s for s in snapshots when misc.endswith(s, '-'+name))
if v.length > 0
newest = v[v.length-1]
t = misc.parse_bup_timestamp(newest)
age_m = (now - t)/(60*1000)
else
age_m = 999999999999
if age_m > interval
to_create.push("#{misc.to_iso_path(now)}-#{name}")
if v.length > opts[name]
for s in v.slice(0, v.length - opts[name])
to_delete.push(s)
cb()
(cb) ->
dbg("snapshots to make: #{misc.to_json(to_create)}")
if to_create.length > 0
f = (snap, cb) ->
make_snapshot(opts.filesystem, snap, cb)
async.map(to_create, f, cb)
else
cb()
(cb) ->
dbg("snapshots to delete: #{to_delete}")
if to_delete.length > 0
f = (snap, cb) ->
delete_snapshot(opts.filesystem, snap, cb)
async.map(to_delete, f, cb)
else
cb()
], (err) -> opts.cb?(err))