# # # backup utilities # # # # # utc_epoc_to_local_date_time_name() # # subroutine(utc_epoc_to_local_date_time_name( int utc_epoc), ( # This converts utc_epoc to local_epoc and returns # a formatted date_time string to be used in backup # directories. int local_epoc = utc_epoc - (now() - now_localzone()); string date_time = format(local_epoc, "strftime:%Y%m%d_%H%M%S"); # Return date_time; )); # # # delete_backup_files() # # subroutine(delete_backup_files( string subdirectory), ( # This deletes the backup files for the given profile name string backup_path = LOGANALYSISINFO_DIRECTORY . "backup/" . subdirectory; if (file_exists(backup_path)) then ( delete_directory(backup_path); ); )); # # # clean_backup_files() # # subroutine(cleanup_backup_files( string backup_directory), ( debug_message("##### cleanup_backup_files() \n"); # backup_directory is a subdirectory of "LogAnalysisInfo/backup/", i.e. "profiles/" # This removes backup subdirectories which contain files which are # older than two months. It also removes directories if the backup_directory # has more than 25 subdirectories. string backup_path = LOGANALYSISINFO_DIRECTORY . "backup/" . backup_directory; if (file_exists(backup_path)) then ( node backup_directory_info = new_node(); get_directory_contents(backup_path, backup_directory_info); int number_dirs = num_subnodes(backup_directory_info); # debug_message("#### backup_directory_info:\n" . node_as_string(backup_directory_info) . "\n"); # Run cleanup only if there exists more than two backup directories if (number_dirs > 2) then ( # Sort ascending by directory name. Older directories will be on top. sort(backup_directory_info, "field:label,alphabetical,ascending"); node item; int count = number_dirs; int utc_epoc = now(); # Delete backup files older than two months int max_file_age = 3600 * 24 * 60 ; # 2 months in seconds foreach item backup_directory_info ( debug_message("\n\n##### count: " . count . "\n"); if (count > 25) then ( # Delete the directory delete_directory(@item{"pathname"}); ) else if (count > 2) then ( # The last two backup directories are ignored. # Get modification time of the directory node backup_file_info = new_node(); get_file_info(@item{"pathname"}, backup_file_info); debug_message("##### backup_file_info:\n" . node_as_string(backup_file_info) . "\n"); int modification_time = @backup_file_info{"modification_time"}; int file_age = utc_epoc - modification_time; # if (file_age <= 60) then ( # debug_message("##### file_age is: " . file_age . " seconds \n"); # ) # else if (file_age <= 60 * 60) then ( # debug_message("##### file_age is: " . file_age / 60 . " minutes \n"); # ) # else ( # debug_message("##### file_age is: " . file_age / (60 * 60) . " hours \n"); # ); # Delete directory if its older than two month if (file_age > max_file_age) then ( delete_directory(@item{"pathname"}); ) else ( # There are no more older files, exit the loop. last; ); ) else ( last; ); count--; ); ); ); )); # # # _backup_profile() # # subroutine(_backup_profile( string profile_name, bool ignore_existing_backups), ( debug_message("##### backup_profile() \n"); # This creates a backup of the given profile name. # If ignore_existing_backups is true: Creates always a new backup file, it doesn't check for existing backups. # If ignore_existing_backups is true: Only creates the backup if no specific profile backup file exists yet. string profile_path = LOGANALYSISINFO_DIRECTORY . "profiles/" . profile_name . ".cfg"; debug_message("##### profile_path: " . profile_path . "\n"); # Get file_info given profile. node profile_file_info = new_node(); get_file_info(profile_path, profile_file_info); debug_message("##### profile_file_info: " . node_as_string(profile_file_info) . "\n"); # If profile file exists if (@profile_file_info{"exists"}) then ( string subdirectory_name = utc_epoc_to_local_date_time_name(@profile_file_info{"modification_time"}); string backup_subpath = LOGANALYSISINFO_DIRECTORY . "backup/profiles/" . profile_name . "/" . subdirectory_name; if (ignore_existing_backups and file_exists(backup_subpath)) then ( # subdirectory_name already exists, append number to make it unique. int count = 2; while (file_exists(backup_subpath . "_" . count)) ( count++; ); backup_subpath .= "_" . count; ); string backup_path = backup_subpath . "/" . profile_name . ".cfg"; if (ignore_existing_backups or !file_exists(backup_path)) then ( # Create the destination directory write_file(backup_subpath . "/placeholder.txt", "placeholder"); # Create the backup file copy_files(profile_path, backup_path); ); ); )); # # # optionally_create_profile_backup() # # subroutine(optionally_create_profile_backup( string profile_name), ( # This creates a new profile backup if it doesn't yet exist. # It is used to save files with an older modification date. bool ignore_existing_backups = false; _backup_profile(profile_name, ignore_existing_backups); )); # # # create_profile_backup() # # subroutine(create_profile_backup( string profile_name), ( # This creates a new profile backup regardless if a backup for # the same backup directory (defined by modification_time) already exists. # This is used to backup new files which have been just saved. bool ignore_existing_backups = true; _backup_profile(profile_name, ignore_existing_backups); )); # # # save_and_backup_profile() # # subroutine(save_and_backup_profile( string profile_name, node profile), ( # This creates a profile backup and saves the given profile node. # Note, backup_profile() is called twice. # The first backup_profile() ensures that a backup file is created # before the new profile node is saved. The backup file is only created # if it doesn't yet exist, i.e. when a profile file has been edited manually. # The second backup_profile() call creates the backup of the file which has just # been saved. # First backup call, this creates a backup if it doesn't yet exist for the given file date. optionally_create_profile_backup(profile_name); # Save new profile data save_node(profile); # Second backup_profile() call, this creates a backup of the just saved profile. create_profile_backup(profile_name); # Run the backup cleanup cleanup_backup_files("profiles/" . profile_name); )); # # # handle_rename_profile_in_profile_backup() # # subroutine(handle_rename_profile_in_profile_backup( string source_profile_name, string new_profile_name), ( # source_profile_name has been renamed to new_profile_name. # Rename the backup/profiles/ to new_profile_name # Create an additional profiles backup entry with the new profile name. string old_backup_path = LOGANALYSISINFO_DIRECTORY . "backup/profiles/" . source_profile_name; if (file_exists(old_backup_path)) then ( # Copy directory to new new_backup_path string new_backup_path = LOGANALYSISINFO_DIRECTORY . "backup/profiles/" . new_profile_name; copy_files(old_backup_path, new_backup_path); # Delete old_backup_path delete_directory(old_backup_path); ); # Create a backup with the new profile name create_profile_backup(new_profile_name); )); # # # # Handle backup of profiles which become deleted # # # # # # clean_backup_files() # # subroutine(cleanup_deleted_profiles_backup, ( debug_message("##### cleanup_deleted_profiles_backup() \n"); # This removes profile file backups from backup/deleted_profiles which are older than # two month. string backup_path = LOGANALYSISINFO_DIRECTORY . "backup/deleted_profiles"; if (file_exists(backup_path)) then ( node backup_directory_info = new_node(); get_directory_contents(backup_path, backup_directory_info); int number_dirs = num_subnodes(backup_directory_info); # debug_message("#### backup_directory_info:\n" . node_as_string(backup_directory_info) . "\n"); # Run cleanup only if there exists more than one backup directories if (number_dirs > 1) then ( node item; int utc_epoc = now(); # Delete backup files older than two months int max_file_age = 3600 * 24 * 60 ; # 2 months in seconds foreach item backup_directory_info ( # Get modification time of the directory node backup_file_info = new_node(); get_file_info(@item{"pathname"}, backup_file_info); debug_message("##### backup_file_info:\n" . node_as_string(backup_file_info) . "\n"); int modification_time = @backup_file_info{"modification_time"}; int file_age = utc_epoc - modification_time; # if (file_age <= 60) then ( # debug_message("##### file_age is: " . file_age . " seconds \n"); # ) # else if (file_age <= 60 * 60) then ( # debug_message("##### file_age is: " . file_age / 60 . " minutes \n"); # ) # else ( # debug_message("##### file_age is: " . file_age / (60 * 60) . " hours \n"); # ); # Delete directory if its older than two month if (file_age > max_file_age) then ( delete_directory(@item{"pathname"}); ); ); ); ); )); # # # backup_profile_in_deleted_profiles_backup() # # subroutine(backup_profile_in_deleted_profiles_backup( string profile_name), ( # This creates a backup of the profile which becomes deleted # in LogAnalysisInfo/backup/deleted_profiles. # The subroutine must be called before the profile becomes deleted. string profile_path = LOGANALYSISINFO_DIRECTORY . "profiles/" . profile_name . ".cfg"; debug_message("##### profile_path: " . profile_path . "\n"); # Get file_info given profile. node profile_file_info = new_node(); get_file_info(profile_path, profile_file_info); debug_message("##### profile_file_info: " . node_as_string(profile_file_info) . "\n"); # If profile file exists if (@profile_file_info{"exists"}) then ( string subdirectory_name = utc_epoc_to_local_date_time_name(@profile_file_info{"modification_time"}); subdirectory_name .= "_" . profile_name; string backup_subpath = LOGANALYSISINFO_DIRECTORY . "backup/deleted_profiles/" . subdirectory_name; string backup_path = backup_subpath . "/" . profile_name . ".cfg"; # Create the destination directory write_file(backup_subpath . "/placeholder.txt", "placeholder"); # Create the backup file copy_files(profile_path, backup_path); ); # # Run a cleanup which deletes older deleted profile backups. # cleanup_deleted_profiles_backup(); ));