Skip to content

Commit

Permalink
Merge pull request #221 from ISISComputingGroup/move_old_backups_first
Browse files Browse the repository at this point in the history
move old backups first
  • Loading branch information
Tom-Willemsen authored Jan 22, 2025
2 parents f3c5c01 + b114049 commit 7cc13af
Showing 1 changed file with 44 additions and 29 deletions.
73 changes: 44 additions & 29 deletions installation_and_upgrade/ibex_install_utils/tasks/backup_tasks.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
import shutil
from typing import Callable, Tuple

from ibex_install_utils.file_utils import FileUtils
from ibex_install_utils.progress_bar import ProgressBar
Expand Down Expand Up @@ -38,7 +39,8 @@
),
}
"""
Dictionary {PATH: list of glob-style ignore patterns} with ignore pattern passed to shutil.copytree during backup
Dictionary {PATH: list of glob-style ignore patterns} with ignore pattern
passed to shutil.copytree during backup
used to exclude directories from a running instrument that are either not useful
or e.g. may have too long a path or some other issues
Expand All @@ -50,7 +52,8 @@

class BackupTasks(BaseTasks):
"""
The tasks dealing with backing up current install, removing current install and moving old backups to the shares.
The tasks dealing with backing up current install, removing current install
and moving old backups to the shares.
"""

Expand All @@ -64,7 +67,7 @@ class BackupTasks(BaseTasks):
"""To indicate tasks' progress"""

@task("Backup old directories")
def backup_old_directories(self):
def backup_old_directories(self) -> None:
"""
Backup old directories.
Expand All @@ -73,57 +76,64 @@ def backup_old_directories(self):
if not os.path.exists(BACKUP_DIR):
os.mkdir(BACKUP_DIR)

# move old backups to create space
self._move_old_backups_to_share()

# Move the folders
for path in DIRECTORIES_TO_BACKUP:
if path in os.getcwd():
self.prompt.prompt_and_raise_if_not_yes(
f"You appear to be trying to delete the folder, {path}, containing the current working directory {os.getcwd()}. "
f"You appear to be trying to delete the folder, {path}, "
f"containing the current working directory {os.getcwd()}. "
f"Please do this manually to be on the safe side"
)
else:
self._backup_dir(path, ignore=IGNORE_PATTERNS.get(path), copy=True)

self._move_old_backups_to_share()
else:
self.prompt.prompt_and_raise_if_not_yes(
f"Unable to find data directory '{BACKUP_DATA_DIR}'. Please backup the current installation of IBEX "
f"manually"
f"Unable to find data directory '{BACKUP_DATA_DIR}'. "
"Please backup the current installation of IBEX manually"
)

@task("Verify backup")
def backup_checker(self):
def backup_checker(self) -> None:
"""
Verify backup. This function checks if the backup has been sucessful by checking for a
VERSION.txt file within the backup folders for EPICS, PYTHON, GUI.
Verify backup. This function checks if the backup has been sucessful by checking
for a VERSION.txt file within the backup folders for EPICS, PYTHON, GUI.
"""
for path in (EPICS_PATH, PYTHON_3_PATH, GUI_PATH):
path_to_backup = self._path_to_backup(path)
if not os.path.exists(os.path.join(path_to_backup, "VERSION.txt")):
self.prompt.prompt_and_raise_if_not_yes(
f"Error found with backup. Backup failed at '{path_to_backup}'. Please backup manually."
f"Error found with backup. Backup failed at '{path_to_backup}'. "
"Please backup manually."
)

for path in (SETTINGS_DIR, AUTOSAVE, EPICS_UTILS_PATH):
if not os.path.exists(self._path_to_backup(path)):
self.prompt.prompt_and_raise_if_not_yes(
f"Error found with backup. '{path}' did not back up properly. Please backup manually."
f"Error found with backup. '{path}' did not back up properly. "
"Please backup manually."
)

@task("Removing old version of IBEX")
def remove_old_ibex(self):
def remove_old_ibex(self) -> None:
"""
Removes older versions of IBEX server, client, genie_python and epics utils.
"""
for path in ALL_INSTALL_DIRECTORIES:
self._file_utils.remove_tree(path[0], self.prompt, leave_top_if_link=True)

def _path_to_backup(self, path):
def _path_to_backup(self, path: str) -> str:
"""Returns backup path for the given path"""
return os.path.join(self._get_backup_dir(), os.path.basename(path))

def _check_backup_space(self, src, ignore=None):
def _check_backup_space(
self, src: str, ignore: "Callable[[str, list[str]], set[str]] | None" = None
) -> Tuple[int, int]:
# Checks if there is enough space to move dir at src into the backup directory
# (all in bytes)
_, _, free = shutil.disk_usage(BACKUP_DIR)
Expand All @@ -137,7 +147,12 @@ def _check_backup_space(self, src, ignore=None):

return backup_size, number_of_files

def _backup_dir(self, src, copy=True, ignore=None):
def _backup_dir(
self,
src: str,
copy: bool = True,
ignore: "Callable[[str, list[str]], set[str]] | None" = None,
) -> None:
"""Move a directory to the backup area.
If the optional copy flag is true, the directory in `src` will be kept;
Expand All @@ -152,6 +167,7 @@ def _backup_dir(self, src, copy=True, ignore=None):
ignore: A callable like `shutil.ignore_patterns`
"""
dst = None
try:
# Optimistic start
print(f"\nPreparing to back up {src} ...")
Expand All @@ -163,7 +179,7 @@ def _backup_dir(self, src, copy=True, ignore=None):

print("Attempting to " + ("copy" if copy else "move") + f" {src} to {dst}")

def copy_function(src, dst):
def copy_function(src: str, dst: str) -> None:
"""Just a copy or move operation that also updates the progress bar."""
if copy:
shutil.copy2(src, dst)
Expand All @@ -187,7 +203,8 @@ def copy_function(src, dst):
print(f"Skipping {src} backup as not present has been marked as OK.")
else:
self.prompt.prompt_and_raise_if_not_yes(
f"You appear to backing up {src}, but it doesn't exist on this machine. Please manually check your installation first."
f"You appear to backing up {src}, but it doesn't exist on this machine. "
"Please manually check your installation first."
)

except FileExistsError:
Expand All @@ -209,34 +226,31 @@ def copy_function(src, dst):
_, _, msg = error
print(msg)

except:
except Exception as e:
# All other errors
self.prompt.prompt_and_raise_if_not_yes(
f"Something went wrong while backing up {src}. Please backup this app manually."
f"Something went wrong while backing up {src} ({e}). "
"Please backup this app manually."
)

else:
# Finished successfully
print(f"Successfully backed up to {dst}.")

# ? Moving other backups to stage deleted could be a task on its own
def _move_old_backups_to_share(self):
def _move_old_backups_to_share(self) -> None:
"""
Move all but the newest backup to the shares.
Move all backups to the shares. This should be
run before the current installation is backed up
"""
current_backups = [
os.path.join(BACKUP_DIR, d)
for d in os.listdir(BACKUP_DIR)
if os.path.isdir(os.path.join(BACKUP_DIR, d)) and d.startswith("ibex_backup")
]
if len(current_backups) > 0:
all_but_newest_backup = sorted(current_backups, key=os.path.getmtime)[:-1]
backups_to_move = all_but_newest_backup
else:
backups_to_move = []

for d in backups_to_move:
for d in current_backups:
backup = STAGE_DELETED + "\\" + self._get_machine_name() + "\\" + os.path.basename(d)
print(f"Moving backup {d} to {backup}")
self._file_utils.move_dir(d, backup, self.prompt)
Expand All @@ -247,7 +261,8 @@ def _move_old_backups_to_share(self):
Must be called with pythonpath set to `<exact path on your pc>/installation_and_upgrade`
as that is the root of this module and all our imports work that way.
This effectively means to call `set PYTHONPATH=. && python ibex_install_utils/tasks/backup_tasks.py`
This effectively means to call
`set PYTHONPATH=. && python ibex_install_utils/tasks/backup_tasks.py`
from the installation_and_upgrade directory in terminal.
"""
print("Running backup task standalone.")
Expand Down

0 comments on commit 7cc13af

Please sign in to comment.