Browse Source
Bump to v0.0.10 procfs library. Signed-off-by: Ben Kochie <superq@gmail.com>pull/1612/head
Ben Kochie
5 years ago
committed by
GitHub
17 changed files with 849 additions and 250 deletions
@ -0,0 +1,62 @@
|
||||
// Copyright 2019 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.
|
||||
|
||||
package procfs |
||||
|
||||
import ( |
||||
"fmt" |
||||
"strconv" |
||||
"strings" |
||||
|
||||
"github.com/prometheus/procfs/internal/util" |
||||
) |
||||
|
||||
// LoadAvg represents an entry in /proc/loadavg
|
||||
type LoadAvg struct { |
||||
Load1 float64 |
||||
Load5 float64 |
||||
Load15 float64 |
||||
} |
||||
|
||||
// LoadAvg returns loadavg from /proc.
|
||||
func (fs FS) LoadAvg() (*LoadAvg, error) { |
||||
path := fs.proc.Path("loadavg") |
||||
|
||||
data, err := util.ReadFileNoStat(path) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return parseLoad(data) |
||||
} |
||||
|
||||
// Parse /proc loadavg and return 1m, 5m and 15m.
|
||||
func parseLoad(loadavgBytes []byte) (*LoadAvg, error) { |
||||
loads := make([]float64, 3) |
||||
parts := strings.Fields(string(loadavgBytes)) |
||||
if len(parts) < 3 { |
||||
return nil, fmt.Errorf("malformed loadavg line: too few fields in loadavg string: %s", string(loadavgBytes)) |
||||
} |
||||
|
||||
var err error |
||||
for i, load := range parts[0:3] { |
||||
loads[i], err = strconv.ParseFloat(load, 64) |
||||
if err != nil { |
||||
return nil, fmt.Errorf("could not parse load '%s': %s", load, err) |
||||
} |
||||
} |
||||
return &LoadAvg{ |
||||
Load1: loads[0], |
||||
Load5: loads[1], |
||||
Load15: loads[2], |
||||
}, nil |
||||
} |
@ -0,0 +1,153 @@
|
||||
// Copyright 2020 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.
|
||||
|
||||
package procfs |
||||
|
||||
import ( |
||||
"bufio" |
||||
"bytes" |
||||
"fmt" |
||||
"io" |
||||
"strconv" |
||||
"strings" |
||||
|
||||
"github.com/prometheus/procfs/internal/util" |
||||
) |
||||
|
||||
// A ConntrackStatEntry represents one line from net/stat/nf_conntrack
|
||||
// and contains netfilter conntrack statistics at one CPU core
|
||||
type ConntrackStatEntry struct { |
||||
Entries uint64 |
||||
Found uint64 |
||||
Invalid uint64 |
||||
Ignore uint64 |
||||
Insert uint64 |
||||
InsertFailed uint64 |
||||
Drop uint64 |
||||
EarlyDrop uint64 |
||||
SearchRestart uint64 |
||||
} |
||||
|
||||
// Retrieves netfilter's conntrack statistics, split by CPU cores
|
||||
func (fs FS) ConntrackStat() ([]ConntrackStatEntry, error) { |
||||
return readConntrackStat(fs.proc.Path("net", "stat", "nf_conntrack")) |
||||
} |
||||
|
||||
// Parses a slice of ConntrackStatEntries from the given filepath
|
||||
func readConntrackStat(path string) ([]ConntrackStatEntry, error) { |
||||
// This file is small and can be read with one syscall.
|
||||
b, err := util.ReadFileNoStat(path) |
||||
if err != nil { |
||||
// Do not wrap this error so the caller can detect os.IsNotExist and
|
||||
// similar conditions.
|
||||
return nil, err |
||||
} |
||||
|
||||
stat, err := parseConntrackStat(bytes.NewReader(b)) |
||||
if err != nil { |
||||
return nil, fmt.Errorf("failed to read conntrack stats from %q: %v", path, err) |
||||
} |
||||
|
||||
return stat, nil |
||||
} |
||||
|
||||
// Reads the contents of a conntrack statistics file and parses a slice of ConntrackStatEntries
|
||||
func parseConntrackStat(r io.Reader) ([]ConntrackStatEntry, error) { |
||||
var entries []ConntrackStatEntry |
||||
|
||||
scanner := bufio.NewScanner(r) |
||||
scanner.Scan() |
||||
for scanner.Scan() { |
||||
fields := strings.Fields(scanner.Text()) |
||||
conntrackEntry, err := parseConntrackStatEntry(fields) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
entries = append(entries, *conntrackEntry) |
||||
} |
||||
|
||||
return entries, nil |
||||
} |
||||
|
||||
// Parses a ConntrackStatEntry from given array of fields
|
||||
func parseConntrackStatEntry(fields []string) (*ConntrackStatEntry, error) { |
||||
if len(fields) != 17 { |
||||
return nil, fmt.Errorf("invalid conntrackstat entry, missing fields") |
||||
} |
||||
entry := &ConntrackStatEntry{} |
||||
|
||||
entries, err := parseConntrackStatField(fields[0]) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
entry.Entries = entries |
||||
|
||||
found, err := parseConntrackStatField(fields[2]) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
entry.Found = found |
||||
|
||||
invalid, err := parseConntrackStatField(fields[4]) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
entry.Invalid = invalid |
||||
|
||||
ignore, err := parseConntrackStatField(fields[5]) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
entry.Ignore = ignore |
||||
|
||||
insert, err := parseConntrackStatField(fields[8]) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
entry.Insert = insert |
||||
|
||||
insertFailed, err := parseConntrackStatField(fields[9]) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
entry.InsertFailed = insertFailed |
||||
|
||||
drop, err := parseConntrackStatField(fields[10]) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
entry.Drop = drop |
||||
|
||||
earlyDrop, err := parseConntrackStatField(fields[11]) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
entry.EarlyDrop = earlyDrop |
||||
|
||||
searchRestart, err := parseConntrackStatField(fields[16]) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
entry.SearchRestart = searchRestart |
||||
|
||||
return entry, nil |
||||
} |
||||
|
||||
// Parses a uint64 from given hex in string
|
||||
func parseConntrackStatField(field string) (uint64, error) { |
||||
val, err := strconv.ParseUint(field, 16, 64) |
||||
if err != nil { |
||||
return 0, fmt.Errorf("couldn't parse \"%s\" field: %s", field, err) |
||||
} |
||||
return val, err |
||||
} |
@ -0,0 +1,229 @@
|
||||
// Copyright 2020 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.
|
||||
|
||||
package procfs |
||||
|
||||
import ( |
||||
"bufio" |
||||
"encoding/hex" |
||||
"fmt" |
||||
"io" |
||||
"net" |
||||
"os" |
||||
"strconv" |
||||
"strings" |
||||
) |
||||
|
||||
const ( |
||||
// readLimit is used by io.LimitReader while reading the content of the
|
||||
// /proc/net/udp{,6} files. The number of lines inside such a file is dynamic
|
||||
// as each line represents a single used socket.
|
||||
// In theory, the number of available sockets is 65535 (2^16 - 1) per IP.
|
||||
// With e.g. 150 Byte per line and the maximum number of 65535,
|
||||
// the reader needs to handle 150 Byte * 65535 =~ 10 MB for a single IP.
|
||||
readLimit = 4294967296 // Byte -> 4 GiB
|
||||
) |
||||
|
||||
type ( |
||||
// NetUDP represents the contents of /proc/net/udp{,6} file without the header.
|
||||
NetUDP []*netUDPLine |
||||
|
||||
// NetUDPSummary provides already computed values like the total queue lengths or
|
||||
// the total number of used sockets. In contrast to NetUDP it does not collect
|
||||
// the parsed lines into a slice.
|
||||
NetUDPSummary struct { |
||||
// TxQueueLength shows the total queue length of all parsed tx_queue lengths.
|
||||
TxQueueLength uint64 |
||||
// RxQueueLength shows the total queue length of all parsed rx_queue lengths.
|
||||
RxQueueLength uint64 |
||||
// UsedSockets shows the total number of parsed lines representing the
|
||||
// number of used sockets.
|
||||
UsedSockets uint64 |
||||
} |
||||
|
||||
// netUDPLine represents the fields parsed from a single line
|
||||
// in /proc/net/udp{,6}. Fields which are not used by UDP are skipped.
|
||||
// For the proc file format details, see https://linux.die.net/man/5/proc.
|
||||
netUDPLine struct { |
||||
Sl uint64 |
||||
LocalAddr net.IP |
||||
LocalPort uint64 |
||||
RemAddr net.IP |
||||
RemPort uint64 |
||||
St uint64 |
||||
TxQueue uint64 |
||||
RxQueue uint64 |
||||
UID uint64 |
||||
} |
||||
) |
||||
|
||||
// NetUDP returns the IPv4 kernel/networking statistics for UDP datagrams
|
||||
// read from /proc/net/udp.
|
||||
func (fs FS) NetUDP() (NetUDP, error) { |
||||
return newNetUDP(fs.proc.Path("net/udp")) |
||||
} |
||||
|
||||
// NetUDP6 returns the IPv6 kernel/networking statistics for UDP datagrams
|
||||
// read from /proc/net/udp6.
|
||||
func (fs FS) NetUDP6() (NetUDP, error) { |
||||
return newNetUDP(fs.proc.Path("net/udp6")) |
||||
} |
||||
|
||||
// NetUDPSummary returns already computed statistics like the total queue lengths
|
||||
// for UDP datagrams read from /proc/net/udp.
|
||||
func (fs FS) NetUDPSummary() (*NetUDPSummary, error) { |
||||
return newNetUDPSummary(fs.proc.Path("net/udp")) |
||||
} |
||||
|
||||
// NetUDP6Summary returns already computed statistics like the total queue lengths
|
||||
// for UDP datagrams read from /proc/net/udp6.
|
||||
func (fs FS) NetUDP6Summary() (*NetUDPSummary, error) { |
||||
return newNetUDPSummary(fs.proc.Path("net/udp6")) |
||||
} |
||||
|
||||
// newNetUDP creates a new NetUDP{,6} from the contents of the given file.
|
||||
func newNetUDP(file string) (NetUDP, error) { |
||||
f, err := os.Open(file) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
defer f.Close() |
||||
|
||||
netUDP := NetUDP{} |
||||
|
||||
lr := io.LimitReader(f, readLimit) |
||||
s := bufio.NewScanner(lr) |
||||
s.Scan() // skip first line with headers
|
||||
for s.Scan() { |
||||
fields := strings.Fields(s.Text()) |
||||
line, err := parseNetUDPLine(fields) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
netUDP = append(netUDP, line) |
||||
} |
||||
if err := s.Err(); err != nil { |
||||
return nil, err |
||||
} |
||||
return netUDP, nil |
||||
} |
||||
|
||||
// newNetUDPSummary creates a new NetUDP{,6} from the contents of the given file.
|
||||
func newNetUDPSummary(file string) (*NetUDPSummary, error) { |
||||
f, err := os.Open(file) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
defer f.Close() |
||||
|
||||
netUDPSummary := &NetUDPSummary{} |
||||
|
||||
lr := io.LimitReader(f, readLimit) |
||||
s := bufio.NewScanner(lr) |
||||
s.Scan() // skip first line with headers
|
||||
for s.Scan() { |
||||
fields := strings.Fields(s.Text()) |
||||
line, err := parseNetUDPLine(fields) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
netUDPSummary.TxQueueLength += line.TxQueue |
||||
netUDPSummary.RxQueueLength += line.RxQueue |
||||
netUDPSummary.UsedSockets++ |
||||
} |
||||
if err := s.Err(); err != nil { |
||||
return nil, err |
||||
} |
||||
return netUDPSummary, nil |
||||
} |
||||
|
||||
// parseNetUDPLine parses a single line, represented by a list of fields.
|
||||
func parseNetUDPLine(fields []string) (*netUDPLine, error) { |
||||
line := &netUDPLine{} |
||||
if len(fields) < 8 { |
||||
return nil, fmt.Errorf( |
||||
"cannot parse net udp socket line as it has less then 8 columns: %s", |
||||
strings.Join(fields, " "), |
||||
) |
||||
} |
||||
var err error // parse error
|
||||
|
||||
// sl
|
||||
s := strings.Split(fields[0], ":") |
||||
if len(s) != 2 { |
||||
return nil, fmt.Errorf( |
||||
"cannot parse sl field in udp socket line: %s", fields[0]) |
||||
} |
||||
|
||||
if line.Sl, err = strconv.ParseUint(s[0], 0, 64); err != nil { |
||||
return nil, fmt.Errorf("cannot parse sl value in udp socket line: %s", err) |
||||
} |
||||
// local_address
|
||||
l := strings.Split(fields[1], ":") |
||||
if len(l) != 2 { |
||||
return nil, fmt.Errorf( |
||||
"cannot parse local_address field in udp socket line: %s", fields[1]) |
||||
} |
||||
if line.LocalAddr, err = hex.DecodeString(l[0]); err != nil { |
||||
return nil, fmt.Errorf( |
||||
"cannot parse local_address value in udp socket line: %s", err) |
||||
} |
||||
if line.LocalPort, err = strconv.ParseUint(l[1], 16, 64); err != nil { |
||||
return nil, fmt.Errorf( |
||||
"cannot parse local_address port value in udp socket line: %s", err) |
||||
} |
||||
|
||||
// remote_address
|
||||
r := strings.Split(fields[2], ":") |
||||
if len(r) != 2 { |
||||
return nil, fmt.Errorf( |
||||
"cannot parse rem_address field in udp socket line: %s", fields[1]) |
||||
} |
||||
if line.RemAddr, err = hex.DecodeString(r[0]); err != nil { |
||||
return nil, fmt.Errorf( |
||||
"cannot parse rem_address value in udp socket line: %s", err) |
||||
} |
||||
if line.RemPort, err = strconv.ParseUint(r[1], 16, 64); err != nil { |
||||
return nil, fmt.Errorf( |
||||
"cannot parse rem_address port value in udp socket line: %s", err) |
||||
} |
||||
|
||||
// st
|
||||
if line.St, err = strconv.ParseUint(fields[3], 16, 64); err != nil { |
||||
return nil, fmt.Errorf( |
||||
"cannot parse st value in udp socket line: %s", err) |
||||
} |
||||
|
||||
// tx_queue and rx_queue
|
||||
q := strings.Split(fields[4], ":") |
||||
if len(q) != 2 { |
||||
return nil, fmt.Errorf( |
||||
"cannot parse tx/rx queues in udp socket line as it has a missing colon: %s", |
||||
fields[4], |
||||
) |
||||
} |
||||
if line.TxQueue, err = strconv.ParseUint(q[0], 16, 64); err != nil { |
||||
return nil, fmt.Errorf("cannot parse tx_queue value in udp socket line: %s", err) |
||||
} |
||||
if line.RxQueue, err = strconv.ParseUint(q[1], 16, 64); err != nil { |
||||
return nil, fmt.Errorf("cannot parse rx_queue value in udp socket line: %s", err) |
||||
} |
||||
|
||||
// uid
|
||||
if line.UID, err = strconv.ParseUint(fields[7], 0, 64); err != nil { |
||||
return nil, fmt.Errorf( |
||||
"cannot parse uid value in udp socket line: %s", err) |
||||
} |
||||
|
||||
return line, nil |
||||
} |
@ -0,0 +1,89 @@
|
||||
// Copyright 2019 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.
|
||||
|
||||
package procfs |
||||
|
||||
import ( |
||||
"bufio" |
||||
"bytes" |
||||
"fmt" |
||||
"strconv" |
||||
"strings" |
||||
|
||||
"github.com/prometheus/procfs/internal/util" |
||||
) |
||||
|
||||
// Swap represents an entry in /proc/swaps.
|
||||
type Swap struct { |
||||
Filename string |
||||
Type string |
||||
Size int |
||||
Used int |
||||
Priority int |
||||
} |
||||
|
||||
// Swaps returns a slice of all configured swap devices on the system.
|
||||
func (fs FS) Swaps() ([]*Swap, error) { |
||||
data, err := util.ReadFileNoStat(fs.proc.Path("swaps")) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return parseSwaps(data) |
||||
} |
||||
|
||||
func parseSwaps(info []byte) ([]*Swap, error) { |
||||
swaps := []*Swap{} |
||||
scanner := bufio.NewScanner(bytes.NewReader(info)) |
||||
scanner.Scan() // ignore header line
|
||||
for scanner.Scan() { |
||||
swapString := scanner.Text() |
||||
parsedSwap, err := parseSwapString(swapString) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
swaps = append(swaps, parsedSwap) |
||||
} |
||||
|
||||
err := scanner.Err() |
||||
return swaps, err |
||||
} |
||||
|
||||
func parseSwapString(swapString string) (*Swap, error) { |
||||
var err error |
||||
|
||||
swapFields := strings.Fields(swapString) |
||||
swapLength := len(swapFields) |
||||
if swapLength < 5 { |
||||
return nil, fmt.Errorf("too few fields in swap string: %s", swapString) |
||||
} |
||||
|
||||
swap := &Swap{ |
||||
Filename: swapFields[0], |
||||
Type: swapFields[1], |
||||
} |
||||
|
||||
swap.Size, err = strconv.Atoi(swapFields[2]) |
||||
if err != nil { |
||||
return nil, fmt.Errorf("invalid swap size: %s", swapFields[2]) |
||||
} |
||||
swap.Used, err = strconv.Atoi(swapFields[3]) |
||||
if err != nil { |
||||
return nil, fmt.Errorf("invalid swap used: %s", swapFields[3]) |
||||
} |
||||
swap.Priority, err = strconv.Atoi(swapFields[4]) |
||||
if err != nil { |
||||
return nil, fmt.Errorf("invalid swap priority: %s", swapFields[4]) |
||||
} |
||||
|
||||
return swap, nil |
||||
} |
Loading…
Reference in new issue