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
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,16 @@
{"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"}
]
}
}
},
{
Expand Down Expand Up @@ -284,6 +294,7 @@
"policy": [
{
"name": "lan-to-wan",
"action": "accept",
"ingress": [
"lan"
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@
{"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"}
]
}
}
}
]
Expand Down Expand Up @@ -235,6 +245,7 @@
"policy": [
{
"name": "lan-to-wan",
"action": "accept",
"ingress": [
"lan"
],
Expand Down
9 changes: 9 additions & 0 deletions doc/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
startswith, regex, ereregex) on message properties (msg, msgid, programname,
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

Expand All @@ -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
boot step "Mounting filesystems ...", from 30 seconds up to five minutes!
- 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
[bpi-r3-emmc-documentation]: https://github.com/kernelkit/infix/blob/main/board/aarch64/bananapi-bpi-r3/README.md
Expand Down
21 changes: 17 additions & 4 deletions src/statd/python/cli_pretty/cli_pretty.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ class PadDhcpServer:
ip = 17
mac = 19
host = 21
cid = 19
cid = 22
exp = 10


Expand Down Expand Up @@ -652,7 +652,20 @@ def get_formatted_value(self):
else:
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
if self.value_type in ['other', 'unknown']:
return f"{self.value}"
else:
return f"{self.value} {self.value_type}"

Expand Down Expand Up @@ -720,11 +733,11 @@ def __init__(self, data):
now = datetime.now(timezone.utc)
for lease in get_json_data([], self.data, 'leases', 'lease'):
if lease["expires"] == "never":
exp = " never"
exp = "never"
else:
dt = datetime.strptime(lease['expires'], '%Y-%m-%dT%H:%M:%S%z')
seconds = int((dt - now).total_seconds())
exp = f" {self.format_duration(seconds)}"
exp = self.format_duration(seconds)
self.leases.append({
"ip": lease["address"],
"mac": lease["phys-address"],
Expand Down Expand Up @@ -775,7 +788,7 @@ def print(self):
row += f"{mac:<{PadDhcpServer.mac}}"
row += f"{host:<{PadDhcpServer.host}}"
row += f"{cid:<{PadDhcpServer.cid}}"
row += f"{exp:>{PadDhcpServer.exp - 1}}"
row += f"{exp:>{PadDhcpServer.exp}}"
print(row)

def print_stats(self):
Expand Down
32 changes: 31 additions & 1 deletion src/statd/python/yanger/ietf_hardware.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ def create_sensor(sensor_name, value, value_type, value_scale, label=None):
except (FileNotFoundError, ValueError, IOError):
continue

# Fan sensors
# Fan sensors (RPM from tachometer)
for fan_file in glob.glob(os.path.join(hwmon_path, "fan*_input")):
try:
sensor_num = os.path.basename(fan_file).split('_')[0].replace('fan', '')
Expand All @@ -314,6 +314,36 @@ def create_sensor(sensor_name, value, value_type, value_scale, label=None):
except (FileNotFoundError, ValueError, IOError):
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
for voltage_file in glob.glob(os.path.join(hwmon_path, "in*_input")):
try:
Expand Down
2 changes: 1 addition & 1 deletion src/statd/python/yanger/infix_dhcp_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def leases(leases_file):
else:
dt = datetime.fromtimestamp(int(tokens[0]),
tz=timezone.utc)
expires = dt.isoformat() + "+00:00"
expires = dt.isoformat()

row = {
"expires": expires,
Expand Down