mirror of
https://github.com/kevinveenbirkenbach/create-linux-swapfile.git
synced 2025-10-09 17:08:08 +02:00
Replace Bash swapfile script with Python implementation using argparse
- Added btrfs-safe handling (/var/swap/swapfile) - Automatically recreates swapfile if size differs - Removed legacy main.sh Conversation: https://chatgpt.com/share/68d2c38b-84dc-800f-9ca4-dffa158c8e80
This commit is contained in:
113
main.py
Normal file
113
main.py
Normal file
@@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
FSTAB_FILE = Path("/etc/fstab")
|
||||
BTRFS_SWAP_DIR = Path("/var/swap")
|
||||
BTRFS_SWAP_PATH = BTRFS_SWAP_DIR / "swapfile"
|
||||
DEFAULT_SWAP_PATH = Path("/swapfile")
|
||||
|
||||
|
||||
def run(cmd, check=True, capture=False):
|
||||
"""Helper to run shell commands."""
|
||||
kwargs = {"check": check}
|
||||
if capture:
|
||||
kwargs["stdout"] = subprocess.PIPE
|
||||
kwargs["stderr"] = subprocess.PIPE
|
||||
kwargs["text"] = True
|
||||
return subprocess.run(cmd, **kwargs)
|
||||
|
||||
|
||||
def detect_root_fs() -> str:
|
||||
result = run(["findmnt", "-no", "FSTYPE", "/"], capture=True)
|
||||
return result.stdout.strip()
|
||||
|
||||
|
||||
def parse_size_to_mib(size: str) -> int:
|
||||
"""Convert '64G' or '2048M' into MiB integer."""
|
||||
match = re.match(r"^(\d+)([gGmM]?[bB]?)?$", size.strip())
|
||||
if not match:
|
||||
raise ValueError(f"Invalid size format: {size}")
|
||||
num, unit = match.groups()
|
||||
num = int(num)
|
||||
unit = (unit or "M").lower()
|
||||
|
||||
if unit in ("g", "gb"):
|
||||
return num * 1024
|
||||
elif unit in ("m", "mb"):
|
||||
return num
|
||||
else:
|
||||
raise ValueError(f"Unsupported unit: {unit}")
|
||||
|
||||
|
||||
def get_swap_size(path: Path) -> int:
|
||||
"""Return swapfile size in MiB if it exists, else 0."""
|
||||
if not path.exists():
|
||||
return 0
|
||||
return path.stat().st_size // (1024 * 1024)
|
||||
|
||||
|
||||
def remove_swap(path: Path):
|
||||
"""Disable and remove swapfile and its fstab entry."""
|
||||
run(["swapoff", str(path)], check=False)
|
||||
if path.exists():
|
||||
path.unlink()
|
||||
# remove old fstab line
|
||||
if FSTAB_FILE.exists():
|
||||
text = FSTAB_FILE.read_text().splitlines()
|
||||
new_lines = [
|
||||
line for line in text
|
||||
if not re.search(rf"^{re.escape(str(path))}\s+none\s+swap", line)
|
||||
]
|
||||
FSTAB_FILE.write_text("\n".join(new_lines) + "\n")
|
||||
|
||||
|
||||
def create_swap(path: Path, size_mib: int, fs: str):
|
||||
if fs == "btrfs":
|
||||
print(f"Creating {size_mib} MiB swapfile on btrfs at {path}")
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
run(["chattr", "+C", str(path.parent)], check=False)
|
||||
run(["btrfs", "property", "set", "-ts", str(path.parent), "compression", "none"], check=False)
|
||||
run([
|
||||
"dd", "if=/dev/zero", f"of={path}", "bs=1M",
|
||||
f"count={size_mib}", "status=progress"
|
||||
])
|
||||
else:
|
||||
print(f"Creating {size_mib} MiB swapfile on {fs} at {path}")
|
||||
run(["fallocate", "-l", f"{size_mib}M", str(path)])
|
||||
|
||||
run(["chmod", "600", str(path)])
|
||||
run(["mkswap", str(path)])
|
||||
run(["swapon", str(path)])
|
||||
with FSTAB_FILE.open("a") as f:
|
||||
f.write(f"{path} none swap sw 0 0\n")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="SwapForge Python edition")
|
||||
parser.add_argument("size", help="Swapfile size (e.g. 2048M or 64G)")
|
||||
args = parser.parse_args()
|
||||
|
||||
fs = detect_root_fs()
|
||||
path = BTRFS_SWAP_PATH if fs == "btrfs" else DEFAULT_SWAP_PATH
|
||||
new_size = parse_size_to_mib(args.size)
|
||||
|
||||
old_size = get_swap_size(path)
|
||||
if old_size == new_size and old_size > 0:
|
||||
print(f"Swapfile {path} already exists with correct size ({old_size} MiB). Skipping.")
|
||||
return
|
||||
|
||||
if old_size > 0 and old_size != new_size:
|
||||
print(f"Existing swapfile {path} has size {old_size} MiB, expected {new_size} MiB. Recreating...")
|
||||
remove_swap(path)
|
||||
|
||||
create_swap(path, new_size, fs)
|
||||
print("Done.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
14
main.sh
14
main.sh
@@ -1,14 +0,0 @@
|
||||
#!/bin/bash
|
||||
FSTAB_SWAP_ENTRY="/swapfile none swap defaults 0 0"
|
||||
SWAP_FILE="/swapfile"
|
||||
FSTAB_FILE="/etc/fstab"
|
||||
if grep -q "$FSTAB_SWAP_ENTRY" "$FSTAB_FILE"; then
|
||||
echo "Skipping creation of swap partion because entry allready exists in \"$FSTAB_FILE\"!"
|
||||
else
|
||||
echo "Creating swap partition..." &&
|
||||
sudo fallocate -l "$1" "$SWAP_FILE" &&
|
||||
sudo chmod 600 "$SWAP_FILE" &&
|
||||
sudo mkswap "$SWAP_FILE" &&
|
||||
sudo swapon "$SWAP_FILE" &&
|
||||
sudo sh -c "echo \"$FSTAB_SWAP_ENTRY\">>\"$FSTAB_FILE\"" || exit 1
|
||||
fi
|
Reference in New Issue
Block a user