diff --git a/python/vyos/config_mgmt.py b/python/vyos/config_mgmt.py index 6bc9ff3338..1ce3971363 100644 --- a/python/vyos/config_mgmt.py +++ b/python/vyos/config_mgmt.py @@ -48,6 +48,7 @@ from vyos.utils.process import rc_cmd from vyos.defaults import DEFAULT_COMMIT_CONFIRM_MINUTES from vyos.component_version import append_system_version +from vyos.utils.file import file_compare SAVE_CONFIG = '/usr/libexec/vyos/vyos-save-config.py' config_json = '/run/vyatta/config/config.json' @@ -97,7 +98,7 @@ def unsaved_commits(allow_missing_config=False) -> bool: return True tmp_save = '/tmp/config.running' save_config(tmp_save) - ret = not cmp(tmp_save, config_file, shallow=False) + ret = not file_compare(tmp_save, config_file) os.unlink(tmp_save) return ret diff --git a/python/vyos/utils/file.py b/python/vyos/utils/file.py index 98d88d9e4e..86d909250e 100644 --- a/python/vyos/utils/file.py +++ b/python/vyos/utils/file.py @@ -312,3 +312,29 @@ def move_recursive(src: str, dst: str, overwrite=False): copy_recursive(src, dst, overwrite=overwrite) shutil.rmtree(src) + + +def file_compare(file1: str, file2: str) -> bool: + """ + Compare two files modulo blank lines, leading/trailing whitespace, final + newline. + + Returns: + bool: True if files are equivalent in the sense above. + + """ + def non_empty(line): + return bool(line.strip()) + + with open(file1) as f1, open(file2) as f2: + it1 = filter(non_empty, f1) + it2 = filter(non_empty, f2) + + try: + for l1, l2 in zip(it1, it2, strict=True): + if l1.strip() != l2.strip(): + return False + except ValueError: + return False + + return True diff --git a/src/op_mode/image_installer.py b/src/op_mode/image_installer.py index d17cfb2af5..9fa094f7c2 100755 --- a/src/op_mode/image_installer.py +++ b/src/op_mode/image_installer.py @@ -63,11 +63,13 @@ from vyos.utils.auth import get_local_users from vyos.utils.auth import get_user_home_dir from vyos.version import get_version_data +from vyos.config_mgmt import unsaved_commits # define text messages MSG_ERR_NOT_LIVE: str = 'The system is already installed. Please use "add system image" instead.' MSG_ERR_LIVE: str = 'The system is in live-boot mode. Please use "install image" instead.' MSG_ERR_NOT_ENOUGH_SPACE: str = 'Image upgrade requires at least 2GB of free drive space.' +MSG_ERR_UNSAVED_COMMITS: str = 'There are unsaved changes to the configuration. Either save or revert before upgrade.' MSG_ERR_NO_DISK: str = 'No suitable disk was found. There must be at least one disk of 2GB or greater size.' MSG_ERR_IMPROPER_IMAGE: str = 'Missing sha256sum.txt.\nEither this image is corrupted, or of era 1.2.x (md5sum) and would downgrade image tools;\ndisallowed in either case.' MSG_ERR_INCOMPATIBLE_IMAGE: str = 'Image compatibility check failed, aborting installation.' @@ -1102,6 +1104,9 @@ def add_image(image_path: str, vrf: str = None, username: str = '', if disk_usage('/').free < (2 * 1024**3): exit(MSG_ERR_NOT_ENOUGH_SPACE) + if unsaved_commits(): + exit(MSG_ERR_UNSAVED_COMMITS) + environ['REMOTE_USERNAME'] = username environ['REMOTE_PASSWORD'] = password