Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 87 additions & 1 deletion providers/base/bin/wifi_nmcli_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,17 @@
import subprocess as sp
import sys
import shlex
from pathlib import Path
import shutil
import glob
import tempfile

from packaging import version as version_parser

from checkbox_support.helpers.retry import retry
from gateway_ping_test import ping

NETPLAN_DIR = "/lib/netplan"

print = functools.partial(print, flush=True)

Expand Down Expand Up @@ -386,8 +391,75 @@ def parser_args():
return args


def backup_netplan_files(backup_dir: str, netplan_dir: str):
"""
Backup netplan YAML files from /etc/netplan/ to a
temporary directory, if there are.
"""

# Find all netplan YAML files
yaml_files = glob.glob(os.path.join(netplan_dir, "*.yaml"))

if not yaml_files:
print("No netplan YAML files found")
return

# Create temporary directory
Path(backup_dir).mkdir(parents=True, exist_ok=True)

# Copy each file to temp directory
for yaml_file in yaml_files:
filename = os.path.basename(yaml_file)
temp_path = os.path.join(backup_dir, filename)
shutil.copy2(yaml_file, temp_path)
# Then copy ownership
st = os.stat(yaml_file)
os.chown(temp_path, st.st_uid, st.st_gid)
print("Backed up: {} -> {}".format(yaml_file, temp_path))

print("Netplan files backed up to: {}", backup_dir)


def restore_netplan_files(backup_dir: str, netplan_dir: str):
"""
Restore netplan YAML files from backup directory to /etc/netplan/.

Returns:
bool: True if restoration successful, False otherwise
"""
if not backup_dir or not os.path.exists(backup_dir):
print("Backup directory does not exist: {}".format(netplan_dir))
return

# Clean up existing netplan files first
existing_files = glob.glob(os.path.join(netplan_dir, "*.yaml"))
for existing_file in existing_files:
os.remove(existing_file)
print("Removed: {}".format(existing_file))

# Find all YAML files in backup directory
backup_files = glob.glob(os.path.join(backup_dir, "*.yaml"))

if not backup_files:
print("No netplan files found in backup directory")
return

# Restore each file
for backup_file in backup_files:
filename = os.path.basename(backup_file)
target_path = os.path.join(netplan_dir, filename)
shutil.copy2(backup_file, target_path)
# Then copy ownership
st = os.stat(backup_file)
os.chown(target_path, st.st_uid, st.st_gid)
print("Restored: {} -> {}".format(backup_file, target_path))

print("Netplan files restored successfully")
return


@retry(max_attempts=5, delay=60)
def main():
def run():
args = parser_args()
start_time = datetime.datetime.now()
device_rescan()
Expand Down Expand Up @@ -425,5 +497,19 @@ def main():
delete_test_ap_ssid_connection()


def main():

# backup the netplans, because nmcli corrupts them
# and debsums will complain afterwards
# This is ugly. Ideally, nmcli should be patched instead
temp_dir = tempfile.TemporaryDirectory()
backup_netplan_files(str(temp_dir.name), NETPLAN_DIR)

try:
run()
finally:
restore_netplan_files(str(temp_dir.name), NETPLAN_DIR)


if __name__ == "__main__":
sys.exit(main())
1 change: 1 addition & 0 deletions providers/base/tests/test_wifi_client_test_netplan.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import io
import sys
from unittest.mock import call

from wifi_client_test_netplan import (
netplan_renderer,
check_and_get_renderer,
Expand Down
144 changes: 144 additions & 0 deletions providers/base/tests/test_wifi_nmcli_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
import unittest
from subprocess import CalledProcessError
from unittest.mock import patch, call, MagicMock
from pathlib import Path
import shutil
import tempfile

from checkbox_support.helpers.retry import mock_retry

Expand All @@ -42,6 +45,8 @@
perform_ping_test,
parser_args,
main,
restore_netplan_files,
backup_netplan_files,
)


Expand Down Expand Up @@ -606,6 +611,8 @@ def test_main_open_no_aps_found(
"TestSSID": {"Chan": "11", "Freq": "2462", "Signal": "80"},
},
)
@patch("wifi_nmcli_test.backup_netplan_files")
@patch("wifi_nmcli_test.restore_netplan_files")
@patch("wifi_nmcli_test.open_connection", return_value=0)
@patch(
"wifi_nmcli_test.sys.argv",
Expand All @@ -616,5 +623,142 @@ def test_main_open_aps_found(
list_aps_mock,
get_nm_activate_connection_mock,
mock_open_connection,
mock_rest_back,
mock_cr_back,
):
main()


class TestNetplanBackupFunctions(unittest.TestCase):
def setUp(self):
self.TEST_BACKUP_DIR = tempfile.TemporaryDirectory()
self.TEST_NETPLAN_DIR = tempfile.TemporaryDirectory()

@patch("glob.glob")
@patch("builtins.print")
def test_backup_netplan_files_no_files_found(self, mock_print, mock_glob):
"""Test backup when no YAML files are found."""
mock_glob.return_value = []

backup_netplan_files(
str(self.TEST_BACKUP_DIR.name), str(self.TEST_NETPLAN_DIR.name)
)

@patch("os.chown")
@patch("os.stat")
@patch("glob.glob")
@patch("shutil.copy2")
@patch("pathlib.Path.mkdir")
@patch("builtins.print")
def test_backup_netplan_files_success(
self,
mock_print,
mock_mkdir,
mock_copy2,
mock_glob,
mock_stat,
mock_chown,
):
"""Test successful backup of netplan files."""
mock_glob.return_value = [
str(self.TEST_NETPLAN_DIR.name) + "/config1.yaml",
str(self.TEST_NETPLAN_DIR.name) + "/config2.yaml",
]

backup_netplan_files(
str(self.TEST_BACKUP_DIR.name), str(self.TEST_NETPLAN_DIR.name)
)

self.assertEqual(mock_copy2.call_count, 2)
mock_mkdir.assert_called_once_with(parents=True, exist_ok=True)

@patch("os.chown")
@patch("os.stat")
@patch("os.path.exists")
@patch("glob.glob")
@patch("os.remove")
@patch("os.makedirs")
@patch("shutil.copy2")
@patch("builtins.print")
def test_restore_netplan_files_success(
self,
mock_print,
mock_copy2,
mock_makedirs,
mock_remove,
mock_glob,
mock_exists,
mock_stat,
mock_chown,
):
"""Test successful restore of netplan files."""
mock_exists.return_value = True

mock_glob.side_effect = [
[],
[],
]

restore_netplan_files(None, str(self.TEST_NETPLAN_DIR.name))
restore_netplan_files(
str(self.TEST_BACKUP_DIR.name), str(self.TEST_NETPLAN_DIR.name)
)

mock_glob.side_effect = [
# Existing files to remove
[str(self.TEST_NETPLAN_DIR.name) + "/old1.yaml"],
[
"{}/config1.yaml".format(str(self.TEST_BACKUP_DIR.name)),
"{}/config2.yaml".format(str(self.TEST_BACKUP_DIR.name)),
], # Backup files
]

restore_netplan_files(
str(self.TEST_BACKUP_DIR.name), str(self.TEST_NETPLAN_DIR.name)
)

mock_remove.assert_called_once_with(
str(self.TEST_NETPLAN_DIR.name) + "/old1.yaml"
)
self.assertEqual(mock_copy2.call_count, 2)

@patch("os.path.exists")
@patch("glob.glob")
@patch("os.remove")
@patch("builtins.print")
def test_restore_netplan_files_remove_error(
self, mock_print, mock_remove, mock_glob, mock_exists
):
"""Test restore when removing existing files fails."""
mock_exists.return_value = True
mock_glob.side_effect = [
# Existing files to remove
[str(self.TEST_NETPLAN_DIR.name) + "/old1.yaml"],
# Backup files
["{}/config1.yaml".format(str(self.TEST_BACKUP_DIR.name))],
]
mock_remove.side_effect = OSError("Permission denied")
with self.assertRaises(OSError):
restore_netplan_files(
str(self.TEST_BACKUP_DIR.name), str(self.TEST_NETPLAN_DIR.name)
)

@patch("os.path.exists")
@patch("glob.glob")
@patch("os.makedirs")
@patch("builtins.print")
def test_restore_netplan_files_makedirs_error(
self, mock_print, mock_makedirs, mock_glob, mock_exists
):
"""Test restore when makedirs operation fails."""
mock_exists.return_value = True
mock_glob.side_effect = [
[],
["{}/config1.yaml".format(str(self.TEST_BACKUP_DIR.name))],
]
mock_makedirs.side_effect = OSError("Permission denied")

with self.assertRaises(FileNotFoundError):
restore_netplan_files(
str(self.TEST_BACKUP_DIR.name), str(self.TEST_NETPLAN_DIR.name)
)
Loading