chore: adopt the NAPALM driver standard #9
No files matched your search
@@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Licensed under the Apache License, Version 2.0
|
# Licensed under the Apache License, Version 2.0
|
||||||
|
|
||||||
"""NAPALM driver for OpenMediaVault NAS systems.
|
"""NAPALM driver for OpenMediaVault NAS systems.
|
||||||
@@ -22,7 +21,7 @@ import json as _json
|
|||||||
import re
|
import re
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from napalm_device_types import FingerprintRule
|
from napalm_device_types import FingerprintRule, StorageDriver
|
||||||
from napalm_device_types.models import (
|
from napalm_device_types.models import (
|
||||||
DiskPoolDict,
|
DiskPoolDict,
|
||||||
LogicalVolumeDict,
|
LogicalVolumeDict,
|
||||||
@@ -33,7 +32,6 @@ from napalm_device_types.models import (
|
|||||||
StorageServiceDict,
|
StorageServiceDict,
|
||||||
VolumeSnapshotDict,
|
VolumeSnapshotDict,
|
||||||
)
|
)
|
||||||
from napalm_device_types import StorageDriver
|
|
||||||
from napalm_linux.linux import LinuxDriver
|
from napalm_linux.linux import LinuxDriver
|
||||||
|
|
||||||
|
|
||||||
@@ -76,9 +74,7 @@ class OpenMediaVaultDriver(StorageDriver, LinuxDriver):
|
|||||||
"""Return device facts, overriding OMV version from dpkg."""
|
"""Return device facts, overriding OMV version from dpkg."""
|
||||||
facts = super().get_facts()
|
facts = super().get_facts()
|
||||||
try:
|
try:
|
||||||
ver = self._send(
|
ver = self._send("dpkg-query -W -f='${Version}' openmediavault 2>/dev/null").strip()
|
||||||
"dpkg-query -W -f='${Version}' openmediavault 2>/dev/null"
|
|
||||||
).strip()
|
|
||||||
if ver:
|
if ver:
|
||||||
facts["os_version"] = f"OMV {ver}"
|
facts["os_version"] = f"OMV {ver}"
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -159,9 +155,7 @@ class OpenMediaVaultDriver(StorageDriver, LinuxDriver):
|
|||||||
rpm = 0
|
rpm = 0
|
||||||
|
|
||||||
try:
|
try:
|
||||||
smart = self._sudo(
|
smart = self._sudo(f"smartctl -iA /dev/{name} 2>/dev/null", read_timeout=15)
|
||||||
f"smartctl -iA /dev/{name} 2>/dev/null", read_timeout=15
|
|
||||||
)
|
|
||||||
if "PASSED" in smart:
|
if "PASSED" in smart:
|
||||||
health = "healthy"
|
health = "healthy"
|
||||||
elif "FAILED" in smart:
|
elif "FAILED" in smart:
|
||||||
@@ -180,7 +174,8 @@ class OpenMediaVaultDriver(StorageDriver, LinuxDriver):
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
result.append(PhysicalDiskDict(
|
result.append(
|
||||||
|
PhysicalDiskDict(
|
||||||
slot=f"/dev/{name}",
|
slot=f"/dev/{name}",
|
||||||
model=model,
|
model=model,
|
||||||
serial=serial,
|
serial=serial,
|
||||||
@@ -191,7 +186,8 @@ class OpenMediaVaultDriver(StorageDriver, LinuxDriver):
|
|||||||
temperature=temperature,
|
temperature=temperature,
|
||||||
health=health,
|
health=health,
|
||||||
pool=self._disk_to_pool(name),
|
pool=self._disk_to_pool(name),
|
||||||
))
|
)
|
||||||
|
)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@@ -200,7 +196,7 @@ class OpenMediaVaultDriver(StorageDriver, LinuxDriver):
|
|||||||
try:
|
try:
|
||||||
mdstat = self._send("cat /proc/mdstat 2>/dev/null")
|
mdstat = self._send("cat /proc/mdstat 2>/dev/null")
|
||||||
for line in mdstat.splitlines():
|
for line in mdstat.splitlines():
|
||||||
if disk_name in line and re.match(r'^md\d+', line):
|
if disk_name in line and re.match(r"^md\d+", line):
|
||||||
return line.split(":")[0].strip()
|
return line.split(":")[0].strip()
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
@@ -251,19 +247,17 @@ class OpenMediaVaultDriver(StorageDriver, LinuxDriver):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
for line in mdstat.splitlines():
|
for line in mdstat.splitlines():
|
||||||
m = re.match(r'^(md\d+)\s*:\s+(\S+)\s+(\S+)\s+(.*)', line)
|
m = re.match(r"^(md\d+)\s*:\s+(\S+)\s+(\S+)\s+(.*)", line)
|
||||||
if not m:
|
if not m:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
md_name, state_word, level_raw, members_raw = m.groups()
|
md_name, state_word, level_raw, members_raw = m.groups()
|
||||||
member_disks = re.findall(r'(\w+)\[\d+\]', members_raw)
|
member_disks = re.findall(r"(\w+)\[\d+\]", members_raw)
|
||||||
status = "online" if state_word == "active" else "offline"
|
status = "online" if state_word == "active" else "offline"
|
||||||
total = 0
|
total = 0
|
||||||
|
|
||||||
try:
|
try:
|
||||||
detail = self._sudo(
|
detail = self._sudo(f"mdadm --detail /dev/{md_name} 2>/dev/null", read_timeout=10)
|
||||||
f"mdadm --detail /dev/{md_name} 2>/dev/null", read_timeout=10
|
|
||||||
)
|
|
||||||
size_m = re.search(r"Array Size\s*:\s*(\d+)\s*\(", detail)
|
size_m = re.search(r"Array Size\s*:\s*(\d+)\s*\(", detail)
|
||||||
if size_m:
|
if size_m:
|
||||||
total = int(size_m.group(1)) * 1024 # kB → bytes
|
total = int(size_m.group(1)) * 1024 # kB → bytes
|
||||||
@@ -359,10 +353,13 @@ class OpenMediaVaultDriver(StorageDriver, LinuxDriver):
|
|||||||
key = mountpoint.lstrip("/").replace("/", "-") or "root"
|
key = mountpoint.lstrip("/").replace("/", "-") or "root"
|
||||||
pool = device.removeprefix("/dev/")
|
pool = device.removeprefix("/dev/")
|
||||||
|
|
||||||
readonly = bool(re.search(
|
readonly = bool(
|
||||||
rf'^{re.escape(device)}\s+\S+\s+\S+\s+(?:[^,\s]*,)*ro(?:,|\s|$)',
|
re.search(
|
||||||
mounts, re.MULTILINE,
|
rf"^{re.escape(device)}\s+\S+\s+\S+\s+(?:[^,\s]*,)*ro(?:,|\s|$)",
|
||||||
))
|
mounts,
|
||||||
|
re.MULTILINE,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
result[key] = LogicalVolumeDict(
|
result[key] = LogicalVolumeDict(
|
||||||
name=key,
|
name=key,
|
||||||
@@ -478,7 +475,11 @@ class OpenMediaVaultDriver(StorageDriver, LinuxDriver):
|
|||||||
except Exception:
|
except Exception:
|
||||||
running = enabled = False
|
running = enabled = False
|
||||||
result[key] = StorageServiceDict(
|
result[key] = StorageServiceDict(
|
||||||
name=key, enabled=enabled, running=running, port=port, version="",
|
name=key,
|
||||||
|
enabled=enabled,
|
||||||
|
running=running,
|
||||||
|
port=port,
|
||||||
|
version="",
|
||||||
)
|
)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@@ -486,9 +487,7 @@ class OpenMediaVaultDriver(StorageDriver, LinuxDriver):
|
|||||||
|
|
||||||
def get_volume_snapshots(self, volume: str = "") -> list[VolumeSnapshotDict]:
|
def get_volume_snapshots(self, volume: str = "") -> list[VolumeSnapshotDict]:
|
||||||
"""Return volume snapshots (not supported on OpenMediaVault)."""
|
"""Return volume snapshots (not supported on OpenMediaVault)."""
|
||||||
raise NotImplementedError(
|
raise NotImplementedError("OpenMediaVault does not expose volume snapshots via this driver")
|
||||||
"OpenMediaVault does not expose volume snapshots via this driver"
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_replication_jobs(self) -> list[ReplicationJobDict]:
|
def get_replication_jobs(self) -> list[ReplicationJobDict]:
|
||||||
"""Return replication jobs (not supported on OpenMediaVault)."""
|
"""Return replication jobs (not supported on OpenMediaVault)."""
|
||||||
|
|||||||
@@ -41,6 +41,17 @@ Repository = "https://github.com/chrismanivong/napalm-openmediavault"
|
|||||||
where = ["."]
|
where = ["."]
|
||||||
include = ["napalm_openmediavault*"]
|
include = ["napalm_openmediavault*"]
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
line-length = 100
|
||||||
|
target-version = "py310"
|
||||||
|
|
||||||
|
[tool.ruff.lint]
|
||||||
|
select = ["E", "F", "I", "UP", "B"]
|
||||||
|
|
||||||
|
[tool.ruff.lint.per-file-ignores]
|
||||||
|
# Recorded device output keeps its lines.
|
||||||
|
"tests/**" = ["E501"]
|
||||||
|
|
||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
python_version = "3.9"
|
python_version = "3.9"
|
||||||
strict = true
|
strict = true
|
||||||
|
|||||||
@@ -9,10 +9,12 @@ def test_type_label_is_storage():
|
|||||||
|
|
||||||
def test_type_label_overrides_linux():
|
def test_type_label_overrides_linux():
|
||||||
from napalm_linux.linux import LinuxDriver
|
from napalm_linux.linux import LinuxDriver
|
||||||
|
|
||||||
assert LinuxDriver.TYPE_LABEL == "Linux"
|
assert LinuxDriver.TYPE_LABEL == "Linux"
|
||||||
assert OpenMediaVaultDriver.TYPE_LABEL != LinuxDriver.TYPE_LABEL
|
assert OpenMediaVaultDriver.TYPE_LABEL != LinuxDriver.TYPE_LABEL
|
||||||
|
|
||||||
|
|
||||||
def test_storage_device_flag_removed():
|
def test_storage_device_flag_removed():
|
||||||
assert not hasattr(OpenMediaVaultDriver, "storage_device"), \
|
assert not hasattr(OpenMediaVaultDriver, "storage_device"), (
|
||||||
"storage_device is superseded by TYPE_LABEL = 'Storage'"
|
"storage_device is superseded by TYPE_LABEL = 'Storage'"
|
||||||
|
)
|
||||||
Reference in new issue
Block a user