Skip to content
Open
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
31 changes: 28 additions & 3 deletions collector/fibrechannel_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,22 +127,47 @@ func (c *fibrechannelCollector) Update(ch chan<- prometheus.Metric) error {
)...)

// Then the counters
// Note: `procfs` does not guarantee a safe dereference for these counters.
// A disabled host returns no statistics counters.
if host.PortState == nil || *host.PortState == "Unknown" {
// Note: These are not guaranteed to exist in the filesystem

@prb112 prb112 Aug 2, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This defense addition makes sense to me.

if host.Counters.DumpedFrames == nil {
host.Counters.DumpedFrames = new(uint64)
}
if host.Counters.ErrorFrames == nil {
host.Counters.ErrorFrames = new(uint64)
}
if host.Counters.InvalidCRCCount == nil {
host.Counters.InvalidCRCCount = new(uint64)
}
if host.Counters.RXFrames == nil {
host.Counters.RXFrames = new(uint64)
}
if host.Counters.RXWords == nil {
host.Counters.RXWords = new(uint64)
}
if host.Counters.TXFrames == nil {
host.Counters.TXFrames = new(uint64)
}
if host.Counters.TXWords == nil {
host.Counters.TXWords = new(uint64)
}
if host.Counters.SecondsSinceLastReset == nil {
host.Counters.SecondsSinceLastReset = new(uint64)
}
if host.Counters.InvalidTXWordCount == nil {
host.Counters.InvalidTXWordCount = new(uint64)
}
if host.Counters.LinkFailureCount == nil {
host.Counters.LinkFailureCount = new(uint64)
}
if host.Counters.LossOfSyncCount == nil {
host.Counters.LossOfSyncCount = new(uint64)
}
if host.Counters.LossOfSignalCount == nil {
host.Counters.LossOfSignalCount = new(uint64)
}
if host.Counters.NosCount == nil {
host.Counters.NosCount = new(uint64)
}
if host.Counters.FCPPacketAborts == nil {
host.Counters.FCPPacketAborts = new(uint64)
}
c.pushCounter(ch, "dumped_frames_total", *host.Counters.DumpedFrames, *host.Name)
Expand Down
51 changes: 51 additions & 0 deletions collector/fibrechannel_linux_test.go
Comment thread
hamzy marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright 2021 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//go:build !nofibrechannel

package collector

import (
"io"
"log/slog"
"testing"

"github.com/prometheus/client_golang/prometheus"
)

func TestFibreChannelCollector(t *testing.T) {
// The fixtures directory includes host2 which has
// port_state="Online" but an empty statistics/ dir.
// Before the fix, this would panic with a nil pointer
// dereference when trying to read counter values.

logger := slog.New(slog.NewTextHandler(io.Discard, nil))

// Override sysPath to point to our fixtures
testSysPath := "fixtures/sys"
oldSysPath := *sysPath
*sysPath = testSysPath
defer func() { *sysPath = oldSysPath }()

collector, err := NewFibreChannelCollector(logger)
if err != nil {
t.Fatal(err)
}

// This should not panic even with missing statistics files
ch := make(chan prometheus.Metric, 100)
err = collector.Update(ch)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
15 changes: 15 additions & 0 deletions collector/fixtures/e2e-64k-page-output.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1406,61 +1406,76 @@ node_entropy_pool_size_bits 4096
# HELP node_fibrechannel_dumped_frames_total Number of dumped frames
# TYPE node_fibrechannel_dumped_frames_total counter
node_fibrechannel_dumped_frames_total{fc_host="host1"} 0
node_fibrechannel_dumped_frames_total{fc_host="host2"} 0
# HELP node_fibrechannel_error_frames_total Number of errors in frames
# TYPE node_fibrechannel_error_frames_total counter
node_fibrechannel_error_frames_total{fc_host="host0"} 0
node_fibrechannel_error_frames_total{fc_host="host1"} 19
node_fibrechannel_error_frames_total{fc_host="host2"} 0
# HELP node_fibrechannel_fcp_packet_aborts_total Number of aborted packets
# TYPE node_fibrechannel_fcp_packet_aborts_total counter
node_fibrechannel_fcp_packet_aborts_total{fc_host="host0"} 19
node_fibrechannel_fcp_packet_aborts_total{fc_host="host2"} 0
# HELP node_fibrechannel_info Non-numeric data from /sys/class/fc_host/<host>, value is always 1.
# TYPE node_fibrechannel_info gauge
node_fibrechannel_info{dev_loss_tmo="",fabric_name="",fc_host="host1",port_id="",port_name="",port_state="Online",port_type="",speed="8 Gbit",supported_classes="",supported_speeds="",symbolic_name=""} 1
node_fibrechannel_info{dev_loss_tmo="30",fabric_name="0",fc_host="host0",port_id="000002",port_name="1000e0071bce95f2",port_state="Online",port_type="Point-To-Point (direct nport connection)",speed="16 Gbit",supported_classes="Class 3",supported_speeds="4 Gbit, 8 Gbit, 16 Gbit",symbolic_name="Emulex SN1100E2P FV12.4.270.3 DV12.4.0.0. HN:gotest. OS:Linux"} 1
node_fibrechannel_info{dev_loss_tmo="300",fabric_name="c050760aab8f2cff",fc_host="host2",port_id="0b19e2",port_name="c050760aab8f2cff",port_state="Online",port_type="NPIV VPORT",speed="unknown",supported_classes="Class 3",supported_speeds="",symbolic_name=""} 1
# HELP node_fibrechannel_invalid_crc_total Invalid Cyclic Redundancy Check count
# TYPE node_fibrechannel_invalid_crc_total counter
node_fibrechannel_invalid_crc_total{fc_host="host0"} 2
node_fibrechannel_invalid_crc_total{fc_host="host1"} 32
node_fibrechannel_invalid_crc_total{fc_host="host2"} 0
# HELP node_fibrechannel_invalid_tx_words_total Number of invalid words transmitted by host port
# TYPE node_fibrechannel_invalid_tx_words_total counter
node_fibrechannel_invalid_tx_words_total{fc_host="host0"} 8
node_fibrechannel_invalid_tx_words_total{fc_host="host1"} 128
node_fibrechannel_invalid_tx_words_total{fc_host="host2"} 0
# HELP node_fibrechannel_link_failure_total Number of times the host port link has failed
# TYPE node_fibrechannel_link_failure_total counter
node_fibrechannel_link_failure_total{fc_host="host0"} 9
node_fibrechannel_link_failure_total{fc_host="host1"} 144
node_fibrechannel_link_failure_total{fc_host="host2"} 0
# HELP node_fibrechannel_loss_of_signal_total Number of times signal has been lost
# TYPE node_fibrechannel_loss_of_signal_total counter
node_fibrechannel_loss_of_signal_total{fc_host="host0"} 17
node_fibrechannel_loss_of_signal_total{fc_host="host1"} 272
node_fibrechannel_loss_of_signal_total{fc_host="host2"} 0
# HELP node_fibrechannel_loss_of_sync_total Number of failures on either bit or transmission word boundaries
# TYPE node_fibrechannel_loss_of_sync_total counter
node_fibrechannel_loss_of_sync_total{fc_host="host0"} 16
node_fibrechannel_loss_of_sync_total{fc_host="host1"} 256
node_fibrechannel_loss_of_sync_total{fc_host="host2"} 0
# HELP node_fibrechannel_nos_total Number Not_Operational Primitive Sequence received by host port
# TYPE node_fibrechannel_nos_total counter
node_fibrechannel_nos_total{fc_host="host0"} 18
node_fibrechannel_nos_total{fc_host="host1"} 288
node_fibrechannel_nos_total{fc_host="host2"} 0
# HELP node_fibrechannel_rx_frames_total Number of frames received
# TYPE node_fibrechannel_rx_frames_total counter
node_fibrechannel_rx_frames_total{fc_host="host0"} 3
node_fibrechannel_rx_frames_total{fc_host="host1"} 48
node_fibrechannel_rx_frames_total{fc_host="host2"} 0
# HELP node_fibrechannel_rx_words_total Number of words received by host port
# TYPE node_fibrechannel_rx_words_total counter
node_fibrechannel_rx_words_total{fc_host="host0"} 4
node_fibrechannel_rx_words_total{fc_host="host1"} 64
node_fibrechannel_rx_words_total{fc_host="host2"} 0
# HELP node_fibrechannel_seconds_since_last_reset_total Number of seconds since last host port reset
# TYPE node_fibrechannel_seconds_since_last_reset_total counter
node_fibrechannel_seconds_since_last_reset_total{fc_host="host0"} 7
node_fibrechannel_seconds_since_last_reset_total{fc_host="host1"} 112
node_fibrechannel_seconds_since_last_reset_total{fc_host="host2"} 0
# HELP node_fibrechannel_tx_frames_total Number of frames transmitted by host port
# TYPE node_fibrechannel_tx_frames_total counter
node_fibrechannel_tx_frames_total{fc_host="host0"} 5
node_fibrechannel_tx_frames_total{fc_host="host1"} 80
node_fibrechannel_tx_frames_total{fc_host="host2"} 0
# HELP node_fibrechannel_tx_words_total Number of words transmitted by host port
# TYPE node_fibrechannel_tx_words_total counter
node_fibrechannel_tx_words_total{fc_host="host0"} 6
node_fibrechannel_tx_words_total{fc_host="host1"} 96
node_fibrechannel_tx_words_total{fc_host="host2"} 0
# HELP node_filefd_allocated File descriptor statistics: allocated.
# TYPE node_filefd_allocated gauge
node_filefd_allocated 1024
Expand Down
15 changes: 15 additions & 0 deletions collector/fixtures/e2e-output.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1438,61 +1438,76 @@ node_entropy_pool_size_bits 4096
# HELP node_fibrechannel_dumped_frames_total Number of dumped frames
# TYPE node_fibrechannel_dumped_frames_total counter
node_fibrechannel_dumped_frames_total{fc_host="host1"} 0
node_fibrechannel_dumped_frames_total{fc_host="host2"} 0
# HELP node_fibrechannel_error_frames_total Number of errors in frames
# TYPE node_fibrechannel_error_frames_total counter
node_fibrechannel_error_frames_total{fc_host="host0"} 0
node_fibrechannel_error_frames_total{fc_host="host1"} 19
node_fibrechannel_error_frames_total{fc_host="host2"} 0
# HELP node_fibrechannel_fcp_packet_aborts_total Number of aborted packets
# TYPE node_fibrechannel_fcp_packet_aborts_total counter
node_fibrechannel_fcp_packet_aborts_total{fc_host="host0"} 19
node_fibrechannel_fcp_packet_aborts_total{fc_host="host2"} 0
# HELP node_fibrechannel_info Non-numeric data from /sys/class/fc_host/<host>, value is always 1.
# TYPE node_fibrechannel_info gauge
node_fibrechannel_info{dev_loss_tmo="",fabric_name="",fc_host="host1",port_id="",port_name="",port_state="Online",port_type="",speed="8 Gbit",supported_classes="",supported_speeds="",symbolic_name=""} 1
node_fibrechannel_info{dev_loss_tmo="30",fabric_name="0",fc_host="host0",port_id="000002",port_name="1000e0071bce95f2",port_state="Online",port_type="Point-To-Point (direct nport connection)",speed="16 Gbit",supported_classes="Class 3",supported_speeds="4 Gbit, 8 Gbit, 16 Gbit",symbolic_name="Emulex SN1100E2P FV12.4.270.3 DV12.4.0.0. HN:gotest. OS:Linux"} 1
node_fibrechannel_info{dev_loss_tmo="300",fabric_name="c050760aab8f2cff",fc_host="host2",port_id="0b19e2",port_name="c050760aab8f2cff",port_state="Online",port_type="NPIV VPORT",speed="unknown",supported_classes="Class 3",supported_speeds="",symbolic_name=""} 1
# HELP node_fibrechannel_invalid_crc_total Invalid Cyclic Redundancy Check count
# TYPE node_fibrechannel_invalid_crc_total counter
node_fibrechannel_invalid_crc_total{fc_host="host0"} 2
node_fibrechannel_invalid_crc_total{fc_host="host1"} 32
node_fibrechannel_invalid_crc_total{fc_host="host2"} 0
# HELP node_fibrechannel_invalid_tx_words_total Number of invalid words transmitted by host port
# TYPE node_fibrechannel_invalid_tx_words_total counter
node_fibrechannel_invalid_tx_words_total{fc_host="host0"} 8
node_fibrechannel_invalid_tx_words_total{fc_host="host1"} 128
node_fibrechannel_invalid_tx_words_total{fc_host="host2"} 0
# HELP node_fibrechannel_link_failure_total Number of times the host port link has failed
# TYPE node_fibrechannel_link_failure_total counter
node_fibrechannel_link_failure_total{fc_host="host0"} 9
node_fibrechannel_link_failure_total{fc_host="host1"} 144
node_fibrechannel_link_failure_total{fc_host="host2"} 0
# HELP node_fibrechannel_loss_of_signal_total Number of times signal has been lost
# TYPE node_fibrechannel_loss_of_signal_total counter
node_fibrechannel_loss_of_signal_total{fc_host="host0"} 17
node_fibrechannel_loss_of_signal_total{fc_host="host1"} 272
node_fibrechannel_loss_of_signal_total{fc_host="host2"} 0
# HELP node_fibrechannel_loss_of_sync_total Number of failures on either bit or transmission word boundaries
# TYPE node_fibrechannel_loss_of_sync_total counter
node_fibrechannel_loss_of_sync_total{fc_host="host0"} 16
node_fibrechannel_loss_of_sync_total{fc_host="host1"} 256
node_fibrechannel_loss_of_sync_total{fc_host="host2"} 0
# HELP node_fibrechannel_nos_total Number Not_Operational Primitive Sequence received by host port
# TYPE node_fibrechannel_nos_total counter
node_fibrechannel_nos_total{fc_host="host0"} 18
node_fibrechannel_nos_total{fc_host="host1"} 288
node_fibrechannel_nos_total{fc_host="host2"} 0
# HELP node_fibrechannel_rx_frames_total Number of frames received
# TYPE node_fibrechannel_rx_frames_total counter
node_fibrechannel_rx_frames_total{fc_host="host0"} 3
node_fibrechannel_rx_frames_total{fc_host="host1"} 48
node_fibrechannel_rx_frames_total{fc_host="host2"} 0
# HELP node_fibrechannel_rx_words_total Number of words received by host port
# TYPE node_fibrechannel_rx_words_total counter
node_fibrechannel_rx_words_total{fc_host="host0"} 4
node_fibrechannel_rx_words_total{fc_host="host1"} 64
node_fibrechannel_rx_words_total{fc_host="host2"} 0
# HELP node_fibrechannel_seconds_since_last_reset_total Number of seconds since last host port reset
# TYPE node_fibrechannel_seconds_since_last_reset_total counter
node_fibrechannel_seconds_since_last_reset_total{fc_host="host0"} 7
node_fibrechannel_seconds_since_last_reset_total{fc_host="host1"} 112
node_fibrechannel_seconds_since_last_reset_total{fc_host="host2"} 0
# HELP node_fibrechannel_tx_frames_total Number of frames transmitted by host port
# TYPE node_fibrechannel_tx_frames_total counter
node_fibrechannel_tx_frames_total{fc_host="host0"} 5
node_fibrechannel_tx_frames_total{fc_host="host1"} 80
node_fibrechannel_tx_frames_total{fc_host="host2"} 0
# HELP node_fibrechannel_tx_words_total Number of words transmitted by host port
# TYPE node_fibrechannel_tx_words_total counter
node_fibrechannel_tx_words_total{fc_host="host0"} 6
node_fibrechannel_tx_words_total{fc_host="host1"} 96
node_fibrechannel_tx_words_total{fc_host="host2"} 0
# HELP node_filefd_allocated File descriptor statistics: allocated.
# TYPE node_filefd_allocated gauge
node_filefd_allocated 1024
Expand Down
Loading
Loading