|
| 1 | +// Copyright 2021 The Prometheus Authors |
| 2 | +// Licensed under the Apache License, Version 2.0 (the "License"); |
| 3 | +// you may not use this file except in compliance with the License. |
| 4 | +// You may obtain a copy of the License at |
| 5 | +// |
| 6 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 7 | +// |
| 8 | +// Unless required by applicable law or agreed to in writing, software |
| 9 | +// distributed under the License is distributed on an "AS IS" BASIS, |
| 10 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 11 | +// See the License for the specific language governing permissions and |
| 12 | +// limitations under the License. |
| 13 | + |
| 14 | +// +build linux |
| 15 | +// +build !nofibrechannel |
| 16 | + |
| 17 | +package collector |
| 18 | + |
| 19 | +import ( |
| 20 | + "fmt" |
| 21 | + "os" |
| 22 | + |
| 23 | + "github.com/go-kit/kit/log" |
| 24 | + "github.com/go-kit/kit/log/level" |
| 25 | + "github.com/prometheus/client_golang/prometheus" |
| 26 | + "github.com/prometheus/procfs/sysfs" |
| 27 | +) |
| 28 | + |
| 29 | +const maxUint64 = ^uint64(0) |
| 30 | + |
| 31 | +type fibrechannelCollector struct { |
| 32 | + fs sysfs.FS |
| 33 | + metricDescs map[string]*prometheus.Desc |
| 34 | + logger log.Logger |
| 35 | + subsystem string |
| 36 | +} |
| 37 | + |
| 38 | +func init() { |
| 39 | + registerCollector("fibrechannel", defaultEnabled, NewFibreChannelCollector) |
| 40 | +} |
| 41 | + |
| 42 | +// NewFibreChannelCollector returns a new Collector exposing FibreChannel stats. |
| 43 | +func NewFibreChannelCollector(logger log.Logger) (Collector, error) { |
| 44 | + var i fibrechannelCollector |
| 45 | + var err error |
| 46 | + |
| 47 | + i.fs, err = sysfs.NewFS(*sysPath) |
| 48 | + if err != nil { |
| 49 | + return nil, fmt.Errorf("failed to open sysfs: %w", err) |
| 50 | + } |
| 51 | + i.logger = logger |
| 52 | + |
| 53 | + // Detailed description for all metrics. |
| 54 | + descriptions := map[string]string{ |
| 55 | + "dumped_frames_total": "Number of dumped frames", |
| 56 | + "loss_of_signal_total": "Number of times signal has been lost", |
| 57 | + "loss_of_sync_total": "Number of failures on either bit or transmission word boundaries", |
| 58 | + "rx_frames_total": "Number of frames received", |
| 59 | + "error_frames_total": "Number of errors in frames", |
| 60 | + "invalid_tx_words_total": "Number of invalid words transmitted by host port", |
| 61 | + "seconds_since_last_reset_total": "Number of seconds since last host port reset", |
| 62 | + "tx_words_total": "Number of words transmitted by host port", |
| 63 | + "invalid_crc_total": "Invalid Cyclic Redundancy Check count", |
| 64 | + "nos_total": "Number Not_Operational Primitive Sequence received by host port", |
| 65 | + "fcp_packet_aborts_total": "Number of aborted packets", |
| 66 | + "rx_words_total": "Number of words received by host port", |
| 67 | + "tx_frames_total": "Number of frames transmitted by host port", |
| 68 | + "link_failure_total": "Number of times the host port link has failed", |
| 69 | + "name": "Name of Fibre Channel HBA", |
| 70 | + "speed": "Current operating speed", |
| 71 | + "port_state": "Current port state", |
| 72 | + "port_type": "Port type, what the port is connected to", |
| 73 | + "symbolic_name": "Symoblic Name", |
| 74 | + "node_name": "Node Name as hexadecimal string", |
| 75 | + "port_id": "Port ID as string", |
| 76 | + "port_name": "Port Name as hexadecimal string", |
| 77 | + "fabric_name": "Fabric Name; 0 if PTP", |
| 78 | + "dev_loss_tmo": "Device Loss Timeout in seconds", |
| 79 | + "supported_classes": "The FC classes supported", |
| 80 | + "supported_speeds": "The FC speeds supported", |
| 81 | + } |
| 82 | + |
| 83 | + i.metricDescs = make(map[string]*prometheus.Desc) |
| 84 | + i.subsystem = "fibrechannel" |
| 85 | + |
| 86 | + for metricName, description := range descriptions { |
| 87 | + i.metricDescs[metricName] = prometheus.NewDesc( |
| 88 | + prometheus.BuildFQName(namespace, i.subsystem, metricName), |
| 89 | + description, |
| 90 | + []string{"fc_host"}, |
| 91 | + nil, |
| 92 | + ) |
| 93 | + } |
| 94 | + |
| 95 | + return &i, nil |
| 96 | +} |
| 97 | + |
| 98 | +func (c *fibrechannelCollector) pushMetric(ch chan<- prometheus.Metric, name string, value uint64, host string, valueType prometheus.ValueType) { |
| 99 | + ch <- prometheus.MustNewConstMetric(c.metricDescs[name], valueType, float64(value), host) |
| 100 | +} |
| 101 | + |
| 102 | +func (c *fibrechannelCollector) pushCounter(ch chan<- prometheus.Metric, name string, value uint64, host string) { |
| 103 | + // Don't push counters that aren't implemented (a counter equal to maxUint64 is unimplemented by the HBA firmware) |
| 104 | + if value != maxUint64 { |
| 105 | + c.pushMetric(ch, name, value, host, prometheus.CounterValue) |
| 106 | + } |
| 107 | +} |
| 108 | + |
| 109 | +func (c *fibrechannelCollector) Update(ch chan<- prometheus.Metric) error { |
| 110 | + hosts, err := c.fs.FibreChannelClass() |
| 111 | + if err != nil { |
| 112 | + if os.IsNotExist(err) { |
| 113 | + level.Debug(c.logger).Log("msg", "fibrechannel statistics not found, skipping") |
| 114 | + return ErrNoData |
| 115 | + } |
| 116 | + return fmt.Errorf("error obtaining FibreChannel class info: %s", err) |
| 117 | + } |
| 118 | + |
| 119 | + for _, host := range hosts { |
| 120 | + infoDesc := prometheus.NewDesc( |
| 121 | + prometheus.BuildFQName(namespace, c.subsystem, "info"), |
| 122 | + "Non-numeric data from /sys/class/fc_host/<host>, value is always 1.", |
| 123 | + []string{"fc_host", "speed", "port_state", "port_type", "port_id", "port_name", "fabric_name", "symbolic_name", "supported_classes", "supported_speeds", "dev_loss_tmo"}, |
| 124 | + nil, |
| 125 | + ) |
| 126 | + infoValue := 1.0 |
| 127 | + |
| 128 | + // First push the Host values |
| 129 | + ch <- prometheus.MustNewConstMetric(infoDesc, prometheus.GaugeValue, infoValue, host.Name, host.Speed, host.PortState, host.PortType, host.PortID, host.PortName, host.FabricName, host.SymbolicName, host.SupportedClasses, host.SupportedSpeeds, host.DevLossTMO) |
| 130 | + |
| 131 | + // Then the counters |
| 132 | + c.pushCounter(ch, "dumped_frames_total", host.Counters.DumpedFrames, host.Name) |
| 133 | + c.pushCounter(ch, "error_frames_total", host.Counters.ErrorFrames, host.Name) |
| 134 | + c.pushCounter(ch, "invalid_crc_total", host.Counters.InvalidCRCCount, host.Name) |
| 135 | + c.pushCounter(ch, "rx_frames_total", host.Counters.RXFrames, host.Name) |
| 136 | + c.pushCounter(ch, "rx_words_total", host.Counters.RXWords, host.Name) |
| 137 | + c.pushCounter(ch, "tx_frames_total", host.Counters.TXFrames, host.Name) |
| 138 | + c.pushCounter(ch, "tx_words_total", host.Counters.TXWords, host.Name) |
| 139 | + c.pushCounter(ch, "seconds_since_last_reset_total", host.Counters.SecondsSinceLastReset, host.Name) |
| 140 | + c.pushCounter(ch, "invalid_tx_words_total", host.Counters.InvalidTXWordCount, host.Name) |
| 141 | + c.pushCounter(ch, "link_failure_total", host.Counters.LinkFailureCount, host.Name) |
| 142 | + c.pushCounter(ch, "loss_of_sync_total", host.Counters.LossOfSyncCount, host.Name) |
| 143 | + c.pushCounter(ch, "loss_of_signal_total", host.Counters.LossOfSignalCount, host.Name) |
| 144 | + c.pushCounter(ch, "nos_total", host.Counters.NosCount, host.Name) |
| 145 | + c.pushCounter(ch, "fcp_packet_aborts_total", host.Counters.FCPPacketAborts, host.Name) |
| 146 | + } |
| 147 | + |
| 148 | + return nil |
| 149 | +} |
0 commit comments