Merge pull request #1288 from kernelkit/misc

Fix 'show dhcp-server' output in CLI and operational
This commit is contained in:
Joachim Wiberg
2025-12-01 11:08:23 +01:00
committed by GitHub
6 changed files with 80 additions and 6 deletions
@@ -113,6 +113,16 @@
{"id": "vendor-class", "value": "Banana Pi BPI-R3"} {"id": "vendor-class", "value": "Banana Pi BPI-R3"}
] ]
} }
},
"ietf-ip:ipv6": {
"infix-dhcpv6-client:dhcp": {
"option": [
{"id": "ntp-server"},
{"id": "client-fqdn"},
{"id": "domain-search"},
{"id": "dns-server"}
]
}
} }
}, },
{ {
@@ -284,6 +294,7 @@
"policy": [ "policy": [
{ {
"name": "lan-to-wan", "name": "lan-to-wan",
"action": "accept",
"ingress": [ "ingress": [
"lan" "lan"
], ],
@@ -72,6 +72,16 @@
{"id": "vendor-class", "value": "NanoPi R2S"} {"id": "vendor-class", "value": "NanoPi R2S"}
] ]
} }
},
"ietf-ip:ipv6": {
"infix-dhcpv6-client:dhcp": {
"option": [
{"id": "ntp-server"},
{"id": "client-fqdn"},
{"id": "domain-search"},
{"id": "dns-server"}
]
}
} }
} }
] ]
@@ -235,6 +245,7 @@
"policy": [ "policy": [
{ {
"name": "lan-to-wan", "name": "lan-to-wan",
"action": "accept",
"ingress": [ "ingress": [
"lan" "lan"
], ],
+9
View File
@@ -68,6 +68,9 @@ All notable changes to the project are documented in this file.
- Add support for property-based filtering with operators (contains, isequal, - Add support for property-based filtering with operators (contains, isequal,
startswith, regex, ereregex) on message properties (msg, msgid, programname, startswith, regex, ereregex) on message properties (msg, msgid, programname,
hostname, source, data), with optional case-insensitive and negate modifiers hostname, source, data), with optional case-insensitive and negate modifiers
- Update factory configuration for BPi-R3 and NanoPi R2S boards to enable
DHCPv6 client on WAN interface and allow traffic forwarding from LAN to WAN
zone in the firewall (this is what most users expect)
### Fixes ### Fixes
@@ -80,6 +83,12 @@ All notable changes to the project are documented in this file.
- Fix #1255: serious regression in boot time, introduced in v25.10, delays the - Fix #1255: serious regression in boot time, introduced in v25.10, delays the
boot step "Mounting filesystems ...", from 30 seconds up to five minutes! boot step "Mounting filesystems ...", from 30 seconds up to five minutes!
- Fix broken intra-document links in container and tunnel documentation - Fix broken intra-document links in container and tunnel documentation
- Fix `show dhcp-server` command crashing with invalid timestamp format.
DHCP lease expiry timestamps had double timezone suffix causing libyang
validation errors
- Fix `show dhcp-server` output alignment. The EXPIRES column was misaligned
when CLIENT ID field was empty, and CLIENT ID column was too narrow for
typical 20-character client IDs
[latest-boot]: https://github.com/kernelkit/infix/releases/latest-boot [latest-boot]: https://github.com/kernelkit/infix/releases/latest-boot
[bpi-r3-emmc-documentation]: https://github.com/kernelkit/infix/blob/main/board/aarch64/bananapi-bpi-r3/README.md [bpi-r3-emmc-documentation]: https://github.com/kernelkit/infix/blob/main/board/aarch64/bananapi-bpi-r3/README.md
+17 -4
View File
@@ -154,7 +154,7 @@ class PadDhcpServer:
ip = 17 ip = 17
mac = 19 mac = 19
host = 21 host = 21
cid = 19 cid = 22
exp = 10 exp = 10
@@ -652,7 +652,20 @@ class Sensor:
else: else:
return f"{self.value} W" return f"{self.value} W"
# Handle PWM fan sensors (reported as "other" type with milli scale)
# PWM duty cycle is reported as percentage (0-100)
elif self.value_type == 'other' and self.value_scale == 'milli':
# Check if this is likely a PWM sensor based on description or name
name_lower = self.name.lower()
desc_lower = (self.description or "").lower()
if 'pwm' in desc_lower or 'fan' in name_lower or 'fan' in desc_lower:
percent = self.value / 1000.0
return f"{percent:.1f}%"
# Fall through for other "other" type sensors
# For unknown sensor types, show raw value # For unknown sensor types, show raw value
if self.value_type in ['other', 'unknown']:
return f"{self.value}"
else: else:
return f"{self.value} {self.value_type}" return f"{self.value} {self.value_type}"
@@ -720,11 +733,11 @@ class DhcpServer:
now = datetime.now(timezone.utc) now = datetime.now(timezone.utc)
for lease in get_json_data([], self.data, 'leases', 'lease'): for lease in get_json_data([], self.data, 'leases', 'lease'):
if lease["expires"] == "never": if lease["expires"] == "never":
exp = " never" exp = "never"
else: else:
dt = datetime.strptime(lease['expires'], '%Y-%m-%dT%H:%M:%S%z') dt = datetime.strptime(lease['expires'], '%Y-%m-%dT%H:%M:%S%z')
seconds = int((dt - now).total_seconds()) seconds = int((dt - now).total_seconds())
exp = f" {self.format_duration(seconds)}" exp = self.format_duration(seconds)
self.leases.append({ self.leases.append({
"ip": lease["address"], "ip": lease["address"],
"mac": lease["phys-address"], "mac": lease["phys-address"],
@@ -775,7 +788,7 @@ class DhcpServer:
row += f"{mac:<{PadDhcpServer.mac}}" row += f"{mac:<{PadDhcpServer.mac}}"
row += f"{host:<{PadDhcpServer.host}}" row += f"{host:<{PadDhcpServer.host}}"
row += f"{cid:<{PadDhcpServer.cid}}" row += f"{cid:<{PadDhcpServer.cid}}"
row += f"{exp:>{PadDhcpServer.exp - 1}}" row += f"{exp:>{PadDhcpServer.exp}}"
print(row) print(row)
def print_stats(self): def print_stats(self):
+31 -1
View File
@@ -297,7 +297,7 @@ def hwmon_sensor_components():
except (FileNotFoundError, ValueError, IOError): except (FileNotFoundError, ValueError, IOError):
continue continue
# Fan sensors # Fan sensors (RPM from tachometer)
for fan_file in glob.glob(os.path.join(hwmon_path, "fan*_input")): for fan_file in glob.glob(os.path.join(hwmon_path, "fan*_input")):
try: try:
sensor_num = os.path.basename(fan_file).split('_')[0].replace('fan', '') sensor_num = os.path.basename(fan_file).split('_')[0].replace('fan', '')
@@ -314,6 +314,36 @@ def hwmon_sensor_components():
except (FileNotFoundError, ValueError, IOError): except (FileNotFoundError, ValueError, IOError):
continue continue
# PWM fan sensors (duty cycle percentage)
# Only add if no fan*_input exists for this device (avoid duplicates)
has_rpm_sensor = bool(glob.glob(os.path.join(hwmon_path, "fan*_input")))
if not has_rpm_sensor:
for pwm_file in glob.glob(os.path.join(hwmon_path, "pwm[0-9]*")):
# Skip pwm*_enable, pwm*_mode, etc. - only process pwm1, pwm2, etc.
pwm_basename = os.path.basename(pwm_file)
if not pwm_basename.replace('pwm', '').isdigit():
continue
try:
sensor_num = pwm_basename.replace('pwm', '')
pwm_raw = int(HOST.read(pwm_file).strip())
# Convert PWM duty cycle (0-255) to percentage (0-100)
# Note: Some devices are inverted (255=off, 0=max), but we report as-is
# The value represents duty cycle, not necessarily fan speed
# Use "other" value-type since PWM duty cycle isn't a standard IETF type
value = int((pwm_raw / 255.0) * 100 * 1000) # Convert to milli-percent (0-100000)
label_file = os.path.join(hwmon_path, f"pwm{sensor_num}_label")
raw_label = None
if HOST.exists(label_file):
raw_label = HOST.read(label_file).strip()
label = normalize_sensor_name(raw_label)
sensor_name = f"{base_name}-{label}"
else:
sensor_name = base_name if sensor_num == '1' else f"{base_name}{sensor_num}"
# Use "PWM Fan" as description so it displays nicely in show hardware
add_sensor(base_name, create_sensor(sensor_name, value, "other", "milli", raw_label or "PWM Fan"))
except (FileNotFoundError, ValueError, IOError):
continue
# Voltage sensors # Voltage sensors
for voltage_file in glob.glob(os.path.join(hwmon_path, "in*_input")): for voltage_file in glob.glob(os.path.join(hwmon_path, "in*_input")):
try: try:
+1 -1
View File
@@ -23,7 +23,7 @@ def leases(leases_file):
else: else:
dt = datetime.fromtimestamp(int(tokens[0]), dt = datetime.fromtimestamp(int(tokens[0]),
tz=timezone.utc) tz=timezone.utc)
expires = dt.isoformat() + "+00:00" expires = dt.isoformat()
row = { row = {
"expires": expires, "expires": expires,