mirror of
https://github.com/RichieCahill/dotfiles.git
synced 2026-04-17 13:08:19 -04:00
fixed most ruff error
This commit is contained in:
@@ -11,6 +11,8 @@ from typing import TYPE_CHECKING, Any, Generic, Literal, TypeVar
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from collections.abc import Callable, Mapping, Sequence
|
from collections.abc import Callable, Mapping, Sequence
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
R = TypeVar("R")
|
R = TypeVar("R")
|
||||||
|
|
||||||
modes = Literal["normal", "early_error"]
|
modes = Literal["normal", "early_error"]
|
||||||
@@ -45,7 +47,7 @@ def _parallelize_base(
|
|||||||
exceptions = []
|
exceptions = []
|
||||||
for index, future in enumerate(futures, 1):
|
for index, future in enumerate(futures, 1):
|
||||||
if exception := future.exception():
|
if exception := future.exception():
|
||||||
logging.error(f"{future} raised {exception.__class__.__name__}")
|
logger.error(f"{future} raised {exception.__class__.__name__}")
|
||||||
exceptions.append(exception)
|
exceptions.append(exception)
|
||||||
if mode == "early_error":
|
if mode == "early_error":
|
||||||
executor.shutdown(wait=False)
|
executor.shutdown(wait=False)
|
||||||
@@ -55,7 +57,7 @@ def _parallelize_base(
|
|||||||
results.append(future.result())
|
results.append(future.result())
|
||||||
|
|
||||||
if progress_tracker and index % progress_tracker == 0:
|
if progress_tracker and index % progress_tracker == 0:
|
||||||
logging.info(f"Progress: {index}/{total_work}")
|
logger.info(f"Progress: {index}/{total_work}")
|
||||||
|
|
||||||
return ExecutorResults(results, exceptions)
|
return ExecutorResults(results, exceptions)
|
||||||
|
|
||||||
|
|||||||
1
python/random/__init__.py
Normal file
1
python/random/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"""init."""
|
||||||
@@ -1,20 +1,25 @@
|
|||||||
def calculate_capacitor_capacity(voltage, farads):
|
"""capasitor."""
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_capacitor_capacity(voltage: float, farads: float) -> float:
|
||||||
|
"""Calculate capacitor capacity."""
|
||||||
joules = (farads * voltage**2) // 2
|
joules = (farads * voltage**2) // 2
|
||||||
return joules // 3600
|
return joules // 3600
|
||||||
|
|
||||||
|
|
||||||
def calculate_pack_capacity(cells, cell_voltage, farads):
|
def calculate_pack_capacity(cells: int, cell_voltage: float, farads: float) -> float:
|
||||||
|
"""Calculate pack capacity."""
|
||||||
return calculate_capacitor_capacity(cells * cell_voltage, farads / cells)
|
return calculate_capacitor_capacity(cells * cell_voltage, farads / cells)
|
||||||
|
|
||||||
|
|
||||||
def calculate_pack_capacity2(cells, cell_voltage, farads, cell_cost):
|
def calculate_pack_capacity2(cells: int, cell_voltage: float, farads: float, cell_cost: float) -> tuple[float, float]:
|
||||||
capacitor_capacity = calculate_capacitor_capacity(
|
"""Calculate pack capacity."""
|
||||||
cells * cell_voltage, farads / cells
|
capacitor_capacity = calculate_capacitor_capacity(cells * cell_voltage, farads / cells)
|
||||||
)
|
|
||||||
return capacitor_capacity, cell_cost * cells
|
return capacitor_capacity, cell_cost * cells
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main() -> None:
|
||||||
|
"""Main."""
|
||||||
watt_hours = calculate_pack_capacity(cells=10, cell_voltage=2.7, farads=500)
|
watt_hours = calculate_pack_capacity(cells=10, cell_voltage=2.7, farads=500)
|
||||||
print(f"{watt_hours=}")
|
print(f"{watt_hours=}")
|
||||||
print(f"{watt_hours*16=}")
|
print(f"{watt_hours*16=}")
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
def dc_charger_on(
|
|
||||||
battery_max_kwh: float,
|
|
||||||
battery_current_kwh: float,
|
|
||||||
solar_max_kwh: float,
|
|
||||||
daily_power_kwh: float,
|
|
||||||
night: bool,
|
|
||||||
) -> bool:
|
|
||||||
battery_free_kwh = battery_max_kwh - battery_current_kwh
|
|
||||||
|
|
||||||
if daily_power_kwh <= battery_current_kwh or night:
|
|
||||||
return True
|
|
||||||
|
|
||||||
if battery_current_kwh >= battery_max_kwh:
|
|
||||||
return False
|
|
||||||
|
|
||||||
if solar_max_kwh >= battery_free_kwh:
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
@@ -1,4 +1,12 @@
|
|||||||
def caculat_batry_specs(cell_amp_hour, cell_voltage, cells_per_pack, packs):
|
"""thing."""
|
||||||
|
|
||||||
|
def caculat_batry_specs(
|
||||||
|
cell_amp_hour: int,
|
||||||
|
cell_voltage: float,
|
||||||
|
cells_per_pack: int,
|
||||||
|
packs: int,
|
||||||
|
) -> tuple[float, float]:
|
||||||
|
"""Caculat battry specs."""
|
||||||
pack_voltage = cell_voltage * cells_per_pack
|
pack_voltage = cell_voltage * cells_per_pack
|
||||||
|
|
||||||
pack_watt_hours = pack_voltage * cell_amp_hour
|
pack_watt_hours = pack_voltage * cell_amp_hour
|
||||||
@@ -14,55 +22,3 @@ battry_capacity, pack_voltage = caculat_batry_specs(300, 3.2, 8, 2)
|
|||||||
print(f"{battry_capacity=} {pack_voltage=}")
|
print(f"{battry_capacity=} {pack_voltage=}")
|
||||||
cost = 1700
|
cost = 1700
|
||||||
print(f"$/kWh {cost / battry_capacity}")
|
print(f"$/kWh {cost / battry_capacity}")
|
||||||
|
|
||||||
battry_capacity, pack_voltage = caculat_batry_specs(300, 3.2, 8, 4)
|
|
||||||
print(f"{battry_capacity=} {pack_voltage=}")
|
|
||||||
cost = 3300
|
|
||||||
print(f"$/kWh {cost / battry_capacity}")
|
|
||||||
|
|
||||||
|
|
||||||
3300/32
|
|
||||||
|
|
||||||
battry_capacity, pack_voltage = caculat_batry_specs(600, 12.8, 2, 1)
|
|
||||||
print(f"{battry_capacity=} {pack_voltage=}")
|
|
||||||
cost = (740 * 2)
|
|
||||||
print({f"{cost=}"})
|
|
||||||
print(f"$/kWh {cost / battry_capacity}")
|
|
||||||
|
|
||||||
battry_capacity, pack_voltage = caculat_batry_specs(300, 12.8, 2, 2)
|
|
||||||
print(f"{battry_capacity=} {pack_voltage=}")
|
|
||||||
cost = 330 * 4
|
|
||||||
print({f"{cost=}"})
|
|
||||||
print(f"$/kWh {cost / battry_capacity}")
|
|
||||||
print("a")
|
|
||||||
|
|
||||||
battry_capacity, pack_voltage = caculat_batry_specs(280, 3.2, 8, 1)
|
|
||||||
print(f"{battry_capacity=} {pack_voltage=}")
|
|
||||||
cost = 130 * 8
|
|
||||||
print({f"{cost=}"})
|
|
||||||
print(f"$/kWh {cost / battry_capacity}")
|
|
||||||
|
|
||||||
battry_capacity, pack_voltage = caculat_batry_specs(200, 48, 1, 1)
|
|
||||||
print(f"{battry_capacity=} {pack_voltage=}")
|
|
||||||
cost = 2060
|
|
||||||
print({f"{cost=}"})
|
|
||||||
print(f"$/kWh {cost / battry_capacity}")
|
|
||||||
|
|
||||||
battry_capacity, pack_voltage = caculat_batry_specs(600, 12, 2, 1)
|
|
||||||
print(f"{battry_capacity=} {pack_voltage=}")
|
|
||||||
cost = 740 * 2
|
|
||||||
print({f"{cost=}"})
|
|
||||||
print(f"$/kWh {cost / battry_capacity}")
|
|
||||||
|
|
||||||
battry_capacity, pack_voltage = caculat_batry_specs(400, 12, 2, 1)
|
|
||||||
print(f"{battry_capacity=} {pack_voltage=}")
|
|
||||||
cost = 590 * 2
|
|
||||||
print({f"{cost=}"})
|
|
||||||
print(f"$/kWh {cost / battry_capacity}")
|
|
||||||
|
|
||||||
battry_capacity, pack_voltage = caculat_batry_specs(100, 3.2, 8, 4)
|
|
||||||
print(f"{battry_capacity=} {pack_voltage=}")
|
|
||||||
cost = 880 * 2
|
|
||||||
print({f"{cost=}"})
|
|
||||||
print(f"$/kWh {cost / battry_capacity}")
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,26 @@
|
|||||||
from enum import Enum
|
"""voltage_drop."""
|
||||||
|
|
||||||
import math
|
import math
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
class TemperatureUnit(Enum):
|
class TemperatureUnit(Enum):
|
||||||
|
"""Temperature unit."""
|
||||||
|
|
||||||
CELSIUS = "c"
|
CELSIUS = "c"
|
||||||
FAHRENHEIT = "f"
|
FAHRENHEIT = "f"
|
||||||
KELVIN = "k"
|
KELVIN = "k"
|
||||||
|
|
||||||
|
|
||||||
class Temperature:
|
class Temperature:
|
||||||
|
"""Temperature."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
temperature: float,
|
temperature: float,
|
||||||
unit: TemperatureUnit = TemperatureUnit.CELSIUS,
|
unit: TemperatureUnit = TemperatureUnit.CELSIUS,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""__init__."""
|
||||||
Args:
|
|
||||||
temperature (float): Temperature in degrees Celsius
|
|
||||||
unit (TemperatureUnit, optional): Temperature unit. Defaults to TemperatureUnit.CELSIUS.
|
|
||||||
"""
|
|
||||||
unit_modifier = {
|
unit_modifier = {
|
||||||
TemperatureUnit.CELSIUS: 1,
|
TemperatureUnit.CELSIUS: 1,
|
||||||
TemperatureUnit.FAHRENHEIT: 0.5556,
|
TemperatureUnit.FAHRENHEIT: 0.5556,
|
||||||
@@ -27,17 +29,23 @@ class Temperature:
|
|||||||
self.temperature = temperature * unit_modifier[unit]
|
self.temperature = temperature * unit_modifier[unit]
|
||||||
|
|
||||||
def __float__(self) -> float:
|
def __float__(self) -> float:
|
||||||
|
"""Return the temperature in degrees Celsius."""
|
||||||
return self.temperature
|
return self.temperature
|
||||||
|
|
||||||
|
|
||||||
class LengthUnit(Enum):
|
class LengthUnit(Enum):
|
||||||
|
"""Length unit."""
|
||||||
|
|
||||||
METERS = "m"
|
METERS = "m"
|
||||||
FEET = "ft"
|
FEET = "ft"
|
||||||
INCHES = "in"
|
INCHES = "in"
|
||||||
|
|
||||||
|
|
||||||
class Length:
|
class Length:
|
||||||
def __init__(self, length: float, unit: LengthUnit):
|
"""Length."""
|
||||||
|
|
||||||
|
def __init__(self, length: float, unit: LengthUnit) -> None:
|
||||||
|
"""__init__."""
|
||||||
self.meters = self._convert_to_meters(length, unit)
|
self.meters = self._convert_to_meters(length, unit)
|
||||||
|
|
||||||
def _convert_to_meters(self, length: float, unit: LengthUnit) -> float:
|
def _convert_to_meters(self, length: float, unit: LengthUnit) -> float:
|
||||||
@@ -49,16 +57,21 @@ class Length:
|
|||||||
test = thing.get(unit)
|
test = thing.get(unit)
|
||||||
if test:
|
if test:
|
||||||
return length * test
|
return length * test
|
||||||
raise ValueError(f"Unsupported unit: {unit}")
|
error = f"Unsupported unit: {unit}"
|
||||||
|
raise ValueError(error)
|
||||||
|
|
||||||
def __float__(self):
|
def __float__(self) -> float:
|
||||||
|
"""Return the length in meters."""
|
||||||
return self.meters
|
return self.meters
|
||||||
|
|
||||||
def feet(self) -> float:
|
def feet(self) -> float:
|
||||||
|
"""Return the length in feet."""
|
||||||
return self.meters * 3.2808
|
return self.meters * 3.2808
|
||||||
|
|
||||||
|
|
||||||
class MaterialType(Enum):
|
class MaterialType(Enum):
|
||||||
|
"""Material type."""
|
||||||
|
|
||||||
COPPER = "copper"
|
COPPER = "copper"
|
||||||
ALUMINUM = "aluminum"
|
ALUMINUM = "aluminum"
|
||||||
CCA = "cca"
|
CCA = "cca"
|
||||||
@@ -68,8 +81,11 @@ class MaterialType(Enum):
|
|||||||
|
|
||||||
def get_material_resistivity(
|
def get_material_resistivity(
|
||||||
material: MaterialType,
|
material: MaterialType,
|
||||||
temperature: Temperature = Temperature(20.0),
|
temperature: Temperature | None = None,
|
||||||
) -> float:
|
) -> float:
|
||||||
|
"""Get the resistivity of a material."""
|
||||||
|
if not temperature:
|
||||||
|
Temperature(20.0)
|
||||||
material_info = {
|
material_info = {
|
||||||
MaterialType.COPPER: (1.724e-8, 0.00393),
|
MaterialType.COPPER: (1.724e-8, 0.00393),
|
||||||
MaterialType.ALUMINUM: (2.908e-8, 0.00403),
|
MaterialType.ALUMINUM: (2.908e-8, 0.00403),
|
||||||
@@ -83,14 +99,7 @@ def get_material_resistivity(
|
|||||||
|
|
||||||
|
|
||||||
def calculate_awg_diameter_mm(gauge: int) -> float:
|
def calculate_awg_diameter_mm(gauge: int) -> float:
|
||||||
"""
|
"""Calculate wire diameter in millimeters for a given AWG gauge."""
|
||||||
Calculate wire diameter in millimeters for a given AWG gauge.
|
|
||||||
Formula: diameter = 0.127 * 92^((36-gauge)/39)
|
|
||||||
Where:
|
|
||||||
- 0.127mm is the diameter of AWG 36
|
|
||||||
- 92 is the ratio of diameters between AWG 0000 and AWG 36
|
|
||||||
- 39 is the number of steps between AWG 0000 and AWG 36
|
|
||||||
"""
|
|
||||||
return round(0.127 * 92 ** ((36 - gauge) / 39), 3)
|
return round(0.127 * 92 ** ((36 - gauge) / 39), 3)
|
||||||
|
|
||||||
|
|
||||||
@@ -124,6 +133,17 @@ def voltage_drop(
|
|||||||
length: Length,
|
length: Length,
|
||||||
current_a: float,
|
current_a: float,
|
||||||
) -> float:
|
) -> float:
|
||||||
|
"""Calculate the voltage drop of a wire.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gauge (int): The AWG (American Wire Gauge) number of the wire
|
||||||
|
material (MaterialType): The type of conductor material (e.g., copper, aluminum)
|
||||||
|
length (Length): The length of the wire in meters
|
||||||
|
current_a (float): The current flowing through the wire in amperes
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: The voltage drop of the wire in volts
|
||||||
|
"""
|
||||||
resistivity = get_material_resistivity(material)
|
resistivity = get_material_resistivity(material)
|
||||||
resistance_per_meter = resistivity / calculate_wire_area_m2(gauge)
|
resistance_per_meter = resistivity / calculate_wire_area_m2(gauge)
|
||||||
total_resistance = resistance_per_meter * float(length) * 2 # round-trip
|
total_resistance = resistance_per_meter * float(length) * 2 # round-trip
|
||||||
@@ -145,7 +165,7 @@ def max_wire_length(
|
|||||||
material: MaterialType,
|
material: MaterialType,
|
||||||
current_amps: float,
|
current_amps: float,
|
||||||
voltage_drop: float = 0.3,
|
voltage_drop: float = 0.3,
|
||||||
temperature: Temperature = Temperature(100.0, unit=TemperatureUnit.FAHRENHEIT),
|
temperature: Temperature | None = None,
|
||||||
) -> Length:
|
) -> Length:
|
||||||
"""Calculate the maximum allowable wire length based on voltage drop criteria.
|
"""Calculate the maximum allowable wire length based on voltage drop criteria.
|
||||||
|
|
||||||
@@ -154,10 +174,14 @@ def max_wire_length(
|
|||||||
material (MaterialType): The type of conductor material (e.g., copper, aluminum)
|
material (MaterialType): The type of conductor material (e.g., copper, aluminum)
|
||||||
current_amps (float): The current flowing through the wire in amperes
|
current_amps (float): The current flowing through the wire in amperes
|
||||||
voltage_drop (float, optional): Maximum allowable voltage drop as a decimal (default 0.1 or 10%)
|
voltage_drop (float, optional): Maximum allowable voltage drop as a decimal (default 0.1 or 10%)
|
||||||
|
temperature (Temperature | None, optional): The temperature of the wire. Defaults to None.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
float: Maximum wire length in meters that maintains the specified voltage drop
|
float: Maximum wire length in meters that maintains the specified voltage drop
|
||||||
"""
|
"""
|
||||||
|
if not temperature:
|
||||||
|
Temperature(100.0, unit=TemperatureUnit.FAHRENHEIT)
|
||||||
|
|
||||||
resistivity = get_material_resistivity(material, temperature)
|
resistivity = get_material_resistivity(material, temperature)
|
||||||
resistance_per_meter = resistivity / calculate_wire_area_m2(gauge)
|
resistance_per_meter = resistivity / calculate_wire_area_m2(gauge)
|
||||||
# V = IR, solve for length where V is the allowed voltage drop
|
# V = IR, solve for length where V is the allowed voltage drop
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ from python.zfs import Zpool
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from collections.abc import Sequence
|
from collections.abc import Sequence
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def zpool_tests(pool_names: Sequence[str], zpool_capacity_threshold: int = 90) -> list[str] | None:
|
def zpool_tests(pool_names: Sequence[str], zpool_capacity_threshold: int = 90) -> list[str] | None:
|
||||||
"""Check the zpool health and capacity.
|
"""Check the zpool health and capacity.
|
||||||
@@ -25,7 +27,7 @@ def zpool_tests(pool_names: Sequence[str], zpool_capacity_threshold: int = 90) -
|
|||||||
Returns:
|
Returns:
|
||||||
list[str] | None: A list of errors if any.
|
list[str] | None: A list of errors if any.
|
||||||
"""
|
"""
|
||||||
logging.info("Testing zpool")
|
logger.info("Testing zpool")
|
||||||
|
|
||||||
errors: list[str] = []
|
errors: list[str] = []
|
||||||
for pool_name in pool_names:
|
for pool_name in pool_names:
|
||||||
@@ -63,7 +65,7 @@ def systemd_tests(
|
|||||||
Returns:
|
Returns:
|
||||||
list[str] | None: A list of errors if any.
|
list[str] | None: A list of errors if any.
|
||||||
"""
|
"""
|
||||||
logging.info("Testing systemd service")
|
logger.info("Testing systemd service")
|
||||||
|
|
||||||
max_retries = max(max_retries, 1)
|
max_retries = max(max_retries, 1)
|
||||||
retry_delay_secs = max(retry_delay_secs, 1)
|
retry_delay_secs = max(retry_delay_secs, 1)
|
||||||
@@ -81,7 +83,7 @@ def systemd_tests(
|
|||||||
for retry in range(max_retries):
|
for retry in range(max_retries):
|
||||||
if not service_names_set:
|
if not service_names_set:
|
||||||
break
|
break
|
||||||
logging.info(f"Testing systemd service in {retry + 1} of {max_retries}")
|
logger.info(f"Testing systemd service in {retry + 1} of {max_retries}")
|
||||||
service_names_to_test = copy(service_names_set)
|
service_names_to_test = copy(service_names_set)
|
||||||
for service_name in service_names_to_test:
|
for service_name in service_names_to_test:
|
||||||
service_status, _ = bash_wrapper(f"systemctl is-active {service_name}")
|
service_status, _ = bash_wrapper(f"systemctl is-active {service_name}")
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import typer
|
|||||||
from python.common import configure_logger, signal_alert
|
from python.common import configure_logger, signal_alert
|
||||||
from python.system_tests.components import systemd_tests, zpool_tests
|
from python.system_tests.components import systemd_tests, zpool_tests
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def load_config_data(config_file: Path) -> dict[str, list[str]]:
|
def load_config_data(config_file: Path) -> dict[str, list[str]]:
|
||||||
"""Load a TOML configuration file.
|
"""Load a TOML configuration file.
|
||||||
@@ -30,7 +32,7 @@ def main(config_file: Path) -> None:
|
|||||||
configure_logger(level=environ.get("LOG_LEVEL", "INFO"))
|
configure_logger(level=environ.get("LOG_LEVEL", "INFO"))
|
||||||
|
|
||||||
server_name = gethostname()
|
server_name = gethostname()
|
||||||
logging.info(f"Starting {server_name} validation")
|
logger.info(f"Starting {server_name} validation")
|
||||||
|
|
||||||
config_data = load_config_data(config_file)
|
config_data = load_config_data(config_file)
|
||||||
|
|
||||||
@@ -43,16 +45,16 @@ def main(config_file: Path) -> None:
|
|||||||
errors.extend(systemd_errors)
|
errors.extend(systemd_errors)
|
||||||
|
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
logging.exception(f"{server_name} validation failed")
|
logger.exception(f"{server_name} validation failed")
|
||||||
errors.append(f"{server_name} validation failed: {error}")
|
errors.append(f"{server_name} validation failed: {error}")
|
||||||
|
|
||||||
if errors:
|
if errors:
|
||||||
logging.error(f"{server_name} validation failed: \n{'\n'.join(errors)}")
|
logger.error(f"{server_name} validation failed: \n{'\n'.join(errors)}")
|
||||||
signal_alert(f"{server_name} validation failed {errors}")
|
signal_alert(f"{server_name} validation failed {errors}")
|
||||||
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
logging.info(f"{server_name} validation passed")
|
logger.info(f"{server_name} validation passed")
|
||||||
|
|
||||||
|
|
||||||
def cli() -> None:
|
def cli() -> None:
|
||||||
|
|||||||
@@ -12,35 +12,36 @@ from re import search
|
|||||||
|
|
||||||
import typer
|
import typer
|
||||||
|
|
||||||
from python.common import configure_logger, signal_alert
|
from python.common import configure_logger, signal_alert, utcnow
|
||||||
from python.common import utcnow
|
|
||||||
from python.zfs import Dataset, get_datasets
|
from python.zfs import Dataset, get_datasets
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def main(config_file: Path) -> None:
|
def main(config_file: Path) -> None:
|
||||||
"""Main."""
|
"""Main."""
|
||||||
configure_logger(level="DEBUG")
|
configure_logger(level="DEBUG")
|
||||||
logging.info("Starting snapshot_manager")
|
logger.info("Starting snapshot_manager")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
time_stamp = get_time_stamp()
|
time_stamp = get_time_stamp()
|
||||||
|
|
||||||
for dataset in get_datasets():
|
for dataset in get_datasets():
|
||||||
status = dataset.create_snapshot(time_stamp)
|
status = dataset.create_snapshot(time_stamp)
|
||||||
logging.debug(f"{status=}")
|
logger.debug(f"{status=}")
|
||||||
if status != "snapshot created":
|
if status != "snapshot created":
|
||||||
msg = f"{dataset.name} failed to create snapshot {time_stamp}"
|
msg = f"{dataset.name} failed to create snapshot {time_stamp}"
|
||||||
logging.error(msg)
|
logger.error(msg)
|
||||||
signal_alert(msg)
|
signal_alert(msg)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
get_snapshots_to_delete(dataset, get_count_lookup(config_file, dataset.name))
|
get_snapshots_to_delete(dataset, get_count_lookup(config_file, dataset.name))
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.exception("snapshot_manager failed")
|
logger.exception("snapshot_manager failed")
|
||||||
signal_alert("snapshot_manager failed")
|
signal_alert("snapshot_manager failed")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
else:
|
else:
|
||||||
logging.info("snapshot_manager completed")
|
logger.info("snapshot_manager completed")
|
||||||
|
|
||||||
|
|
||||||
def get_count_lookup(config_file: Path, dataset_name: str) -> dict[str, int]:
|
def get_count_lookup(config_file: Path, dataset_name: str) -> dict[str, int]:
|
||||||
@@ -99,7 +100,7 @@ def get_snapshots_to_delete(
|
|||||||
snapshots = dataset.get_snapshots()
|
snapshots = dataset.get_snapshots()
|
||||||
|
|
||||||
if not snapshots:
|
if not snapshots:
|
||||||
logging.info(f"{dataset.name} has no snapshots")
|
logger.info(f"{dataset.name} has no snapshots")
|
||||||
return
|
return
|
||||||
|
|
||||||
filters = (
|
filters = (
|
||||||
@@ -110,21 +111,21 @@ def get_snapshots_to_delete(
|
|||||||
)
|
)
|
||||||
|
|
||||||
for filter_name, snapshot_filter in filters:
|
for filter_name, snapshot_filter in filters:
|
||||||
logging.debug(f"{filter_name=}\n{snapshot_filter=}")
|
logger.debug(f"{filter_name=}\n{snapshot_filter=}")
|
||||||
|
|
||||||
filtered_snapshots = sorted(snapshot.name for snapshot in snapshots if search(snapshot_filter, snapshot.name))
|
filtered_snapshots = sorted(snapshot.name for snapshot in snapshots if search(snapshot_filter, snapshot.name))
|
||||||
|
|
||||||
logging.debug(f"{filtered_snapshots=}")
|
logger.debug(f"{filtered_snapshots=}")
|
||||||
|
|
||||||
snapshots_wanted = count_lookup[filter_name]
|
snapshots_wanted = count_lookup[filter_name]
|
||||||
snapshots_being_deleted = filtered_snapshots[:-snapshots_wanted] if snapshots_wanted > 0 else filtered_snapshots
|
snapshots_being_deleted = filtered_snapshots[:-snapshots_wanted] if snapshots_wanted > 0 else filtered_snapshots
|
||||||
|
|
||||||
logging.info(f"{snapshots_being_deleted} are being deleted")
|
logger.info(f"{snapshots_being_deleted} are being deleted")
|
||||||
for snapshot in snapshots_being_deleted:
|
for snapshot in snapshots_being_deleted:
|
||||||
if error := dataset.delete_snapshot(snapshot):
|
if error := dataset.delete_snapshot(snapshot):
|
||||||
error_message = f"{dataset.name}@{snapshot} failed to delete: {error}"
|
error_message = f"{dataset.name}@{snapshot} failed to delete: {error}"
|
||||||
signal_alert(error_message)
|
signal_alert(error_message)
|
||||||
logging.error(error_message)
|
logger.error(error_message)
|
||||||
|
|
||||||
|
|
||||||
def get_time_stamp() -> str:
|
def get_time_stamp() -> str:
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ from typing import Any
|
|||||||
|
|
||||||
from python.common import bash_wrapper
|
from python.common import bash_wrapper
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def _zfs_list(zfs_list: str) -> dict[str, Any]:
|
def _zfs_list(zfs_list: str) -> dict[str, Any]:
|
||||||
"""Check the version of zfs."""
|
"""Check the version of zfs."""
|
||||||
@@ -118,7 +120,7 @@ class Dataset:
|
|||||||
Args:
|
Args:
|
||||||
snapshot_name (str): a snapshot name
|
snapshot_name (str): a snapshot name
|
||||||
"""
|
"""
|
||||||
logging.debug(f"Creating {self.name}@{snapshot_name}")
|
logger.debug(f"Creating {self.name}@{snapshot_name}")
|
||||||
_, return_code = bash_wrapper(f"zfs snapshot {self.name}@{snapshot_name}")
|
_, return_code = bash_wrapper(f"zfs snapshot {self.name}@{snapshot_name}")
|
||||||
if return_code == 0:
|
if return_code == 0:
|
||||||
return "snapshot created"
|
return "snapshot created"
|
||||||
@@ -136,7 +138,7 @@ class Dataset:
|
|||||||
Args:
|
Args:
|
||||||
snapshot_name (str): a snapshot name
|
snapshot_name (str): a snapshot name
|
||||||
"""
|
"""
|
||||||
logging.debug(f"deleting {self.name}@{snapshot_name}")
|
logger.debug(f"deleting {self.name}@{snapshot_name}")
|
||||||
msg, return_code = bash_wrapper(f"zfs destroy {self.name}@{snapshot_name}")
|
msg, return_code = bash_wrapper(f"zfs destroy {self.name}@{snapshot_name}")
|
||||||
if return_code != 0:
|
if return_code != 0:
|
||||||
if msg.startswith(f"cannot destroy '{self.name}@{snapshot_name}': snapshot has dependent clones"):
|
if msg.startswith(f"cannot destroy '{self.name}@{snapshot_name}': snapshot has dependent clones"):
|
||||||
@@ -203,7 +205,7 @@ def get_datasets() -> list[Dataset]:
|
|||||||
Returns:
|
Returns:
|
||||||
list[Dataset]: A list of zfs datasets.
|
list[Dataset]: A list of zfs datasets.
|
||||||
"""
|
"""
|
||||||
logging.info("Getting zfs list")
|
logger.info("Getting zfs list")
|
||||||
|
|
||||||
dataset_names, _ = bash_wrapper("zfs list -Hp -t filesystem -o name")
|
dataset_names, _ = bash_wrapper("zfs list -Hp -t filesystem -o name")
|
||||||
|
|
||||||
|
|||||||
@@ -91,8 +91,7 @@ def test_parallelize_process() -> None:
|
|||||||
|
|
||||||
def test_parallelize_process_to_many_max_workers(mocker: MockerFixture) -> None:
|
def test_parallelize_process_to_many_max_workers(mocker: MockerFixture) -> None:
|
||||||
"""test_parallelize_process."""
|
"""test_parallelize_process."""
|
||||||
mocker.patch(target="python"
|
mocker.patch(target="python.parallelize.cpu_count", return_value=1)
|
||||||
".parallelize.cpu_count", return_value=1)
|
|
||||||
|
|
||||||
with pytest.raises(RuntimeError, match="max_workers must be less than or equal to 1"):
|
with pytest.raises(RuntimeError, match="max_workers must be less than or equal to 1"):
|
||||||
parallelize_process(func=add, kwargs_list=[{"a": 1, "b": 2}], max_workers=8)
|
parallelize_process(func=add, kwargs_list=[{"a": 1, "b": 2}], max_workers=8)
|
||||||
|
|||||||
Reference in New Issue
Block a user