def backup_guard_recurse(pool, class_nr): """Recurse version tower of hanoi backup rotation scheme for ZFS: http://en.wikipedia.org/wiki/Backup_rotation_scheme#Tower_of_Hanoi Snapshots are taken atomically, so that all recursive snapshots corre- spond to the same moment in time. :param fs: zpool name to backup (snapshot make also for all child zfs) :type fs: str :param class_nr: number of class used for rotation :type class_nr: int :raises: zfswrapper.ZfsException """ # tag for new snapshot snp_tag = _gen_snapshot_tag() unknown_label = 'unknown' unknown_control_nr = 'X' # make new recursive snapshot - if problem ZfsException is throw zfs.zfs_snapshot(fs=pool, tag=snp_tag, recurse=True, properties={_backup_property:unknown_label, _backup_control_nr:unknown_control_nr}) # get all filesystem given pool all_fs = zfs.zfs_list(fs=pool, types='filesystem') or [] for fs in all_fs: print 'fs', fs ## Note: you can set class_num per filesystem, exclude some zfs filesystems, e.g. pool # get all snapshots given fs fs_snapshots = zfs.zfs_list(fs=fs, types='snapshot') or [] # get list of snapshots and properties sorted by creation time. First is allready taken snapshot. srt_snapshots = _get_current_hanoi_state(fs, fs_snapshots)[1:] # get control number - useful for debuging and checking hanoi rotation scheme _hanoi_builder(fs, snp_tag, class_nr, srt_snapshots, recurse=True)
def _hanoi_builder(fs, tag, class_nr, srt_snapshots, recurse=False): """Help function, take care for hanoi status :raises: zfswrapper.ZfsException """ class_list = _get_class_list(class_nr) #e.g. ['A', 'B', 'C', 'D', 'E'] modulov = 2**(class_nr-1) #useful to create control number next_control_nr = _get_next_control_number(srt_snapshots, modulov) for ptr in range(0, class_nr): # last must be replaced anyway; snapshot has proper class last_class = (ptr == class_nr-1) has_proper_class = False if len(srt_snapshots) > ptr: has_proper_class = (srt_snapshots[ptr][_backup_property] == class_list[ptr]) if not last_class and has_proper_class: continue else: # Hanoi rotation method has the drawback of overwriting # the very first backup (day 1 of the cycle) after only two days. # However, this can easily be overcome by starting # on the last day of a cycle. class_label = class_list[ptr] if not srt_snapshots: class_label = class_list[-1] # get all old snapshots same class old_snapshot = filter(lambda x: x[_backup_property]==class_label, srt_snapshots) print "to create: {'%s': '%s', '%s': '%s'}" % (_backup_property, class_label, _backup_control_nr, next_control_nr) if recurse: snp_already_taken = '%s@%s' % (fs, tag) # mark already taken snapshot with proper class and control_nr - if problem ZfsException is throw zfs.zfs_set(snp_already_taken, _backup_control_nr, next_control_nr) zfs.zfs_set(snp_already_taken, _backup_property, class_label) else: # make new snapshot - if problem ZfsException is throw zfs.zfs_snapshot(fs, tag, properties={_backup_property:class_label, _backup_control_nr:next_control_nr}) # delete old snapshots same class, only if new snapshot was taken - if probelm ZfsException is throw for old in old_snapshot: print 'to destroy:', old zfs.zfs_destroy(old['name']) # job done break
def replication_simulator(): """Demo - send and receive snapshot. .. warning:: recv_fs can't exist on recv_host, otherwise error is risen """ send_fs = "galaxy01/fleet11" send_tag = "satellite-red01" to_send = "%s@%s" % (send_fs, send_tag) # create local snapshot to teleport if not exist if not zfs.zfs_list(fs=to_send): zfs.zfs_snapshot(send_fs, send_tag) # Remote repl. - using ssh recv_fs = "galaxy_slave01/fleet11_alfa" # recv_fs = 'galaxy_slave01/fleet11_beta' zfs.zfs_teleport_snapshot(to_send, recv_fs, recv_host="zepto") # Local repl. recv_fs = "galaxy02/fleet11_alfa" # recv_fs = 'galaxy02/fleet11_beta' zfs.zfs_teleport_snapshot(to_send, recv_fs)