mirror of
https://github.com/prometheus/node_exporter.git
synced 2024-11-09 23:24:09 -08:00
selinux collector: Added AVC statistics collection.
Signed-off-by: Jonathan Davies <jpds@protonmail.com>
This commit is contained in:
parent
69a3f73a9b
commit
03d9c9fe27
139
collector/avcstat_linux.go
Normal file
139
collector/avcstat_linux.go
Normal file
|
@ -0,0 +1,139 @@
|
|||
// Copyright 2022 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 linux && !noselinux
|
||||
// +build linux,!noselinux
|
||||
|
||||
package collector
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func getAVCStats(path string) (avcStats map[string]uint64, err error) {
|
||||
file, err := os.Open(sysFilePath(path))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
return parseAVCStats(file)
|
||||
}
|
||||
|
||||
func getAVCHashStats(path string) (avcStats map[string]uint64, err error) {
|
||||
file, err := os.Open(sysFilePath(path))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
return parseAVCHashStats(file)
|
||||
}
|
||||
|
||||
func parseAVCStats(r io.Reader) (stats map[string]uint64, err error) {
|
||||
avcValuesRE := regexp.MustCompile(`\d+`)
|
||||
stats = make(map[string]uint64)
|
||||
scanner := bufio.NewScanner(r)
|
||||
scanner.Scan() // Skip header
|
||||
|
||||
for scanner.Scan() {
|
||||
avcValues := avcValuesRE.FindAllString(scanner.Text(), -1)
|
||||
|
||||
if len(avcValues) != 6 { //
|
||||
return nil, fmt.Errorf("invalid AVC stat line: %s",
|
||||
scanner.Text())
|
||||
}
|
||||
|
||||
lookups, err := strconv.ParseUint(avcValues[0], 0, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse expected integer value for lookups")
|
||||
}
|
||||
|
||||
hits, err := strconv.ParseUint(avcValues[1], 0, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse expected integer value for hits")
|
||||
}
|
||||
|
||||
misses, err := strconv.ParseUint(avcValues[2], 0, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse expected integer value for misses")
|
||||
}
|
||||
|
||||
allocations, err := strconv.ParseUint(avcValues[3], 0, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse expected integer value for allocations")
|
||||
}
|
||||
|
||||
reclaims, err := strconv.ParseUint(avcValues[4], 0, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse expected integer value for reclaims")
|
||||
}
|
||||
|
||||
frees, err := strconv.ParseUint(avcValues[5], 0, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse expected integer value for frees")
|
||||
}
|
||||
|
||||
stats["lookups"] += lookups
|
||||
stats["hits"] += hits
|
||||
stats["misses"] += misses
|
||||
stats["allocations"] += allocations
|
||||
stats["reclaims"] += reclaims
|
||||
stats["frees"] += frees
|
||||
}
|
||||
|
||||
return stats, err
|
||||
}
|
||||
|
||||
func parseAVCHashStats(r io.Reader) (stats map[string]uint64, err error) {
|
||||
stats = make(map[string]uint64)
|
||||
scanner := bufio.NewScanner(r)
|
||||
|
||||
scanner.Scan()
|
||||
entriesValue := strings.TrimPrefix(scanner.Text(), "entries: ")
|
||||
|
||||
scanner.Scan()
|
||||
bucketsValues := strings.Split(scanner.Text(), "buckets used: ")
|
||||
bucketsValuesTuple := strings.Split(bucketsValues[1], "/")
|
||||
|
||||
scanner.Scan()
|
||||
longestChainValue := strings.TrimPrefix(scanner.Text(), "longest chain: ")
|
||||
|
||||
stats["entries"], err = strconv.ParseUint(entriesValue, 0, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse expected integer value for hash entries")
|
||||
}
|
||||
|
||||
stats["buckets_used"], err = strconv.ParseUint(bucketsValuesTuple[0], 0, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse expected integer value for hash buckets used")
|
||||
}
|
||||
|
||||
stats["buckets_available"], err = strconv.ParseUint(bucketsValuesTuple[1], 0, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse expected integer value for hash buckets available")
|
||||
}
|
||||
|
||||
stats["longest_chain"], err = strconv.ParseUint(longestChainValue, 0, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse expected integer value for hash longest chain")
|
||||
}
|
||||
|
||||
return stats, err
|
||||
}
|
72
collector/avcstat_linux_test.go
Normal file
72
collector/avcstat_linux_test.go
Normal file
|
@ -0,0 +1,72 @@
|
|||
// Copyright 2022 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 collector
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAVCStat(t *testing.T) {
|
||||
avcStats, err := getAVCStats("fixtures/sys/fs/selinux/avc/cache_stats")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if want, got := uint64(91590784), avcStats["lookups"]; want != got {
|
||||
t.Errorf("want avcstat lookups %v, got %v", want, got)
|
||||
}
|
||||
|
||||
if want, got := uint64(91569452), avcStats["hits"]; want != got {
|
||||
t.Errorf("want avcstat hits %v, got %v", want, got)
|
||||
}
|
||||
|
||||
if want, got := uint64(21332), avcStats["misses"]; want != got {
|
||||
t.Errorf("want avcstat misses %v, got %v", want, got)
|
||||
}
|
||||
|
||||
if want, got := uint64(21332), avcStats["allocations"]; want != got {
|
||||
t.Errorf("want avcstat allocations %v, got %v", want, got)
|
||||
}
|
||||
|
||||
if want, got := uint64(20400), avcStats["reclaims"]; want != got {
|
||||
t.Errorf("want avcstat reclaims %v, got %v", want, got)
|
||||
}
|
||||
|
||||
if want, got := uint64(20826), avcStats["frees"]; want != got {
|
||||
t.Errorf("want avcstat frees %v, got %v", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAVCHashStat(t *testing.T) {
|
||||
avcHashStats, err := getAVCHashStats("fixtures/sys/fs/selinux/avc/hash_stats")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if want, got := uint64(503), avcHashStats["entries"]; want != got {
|
||||
t.Errorf("want avc hash stat entries %v, got %v", want, got)
|
||||
}
|
||||
|
||||
if want, got := uint64(512), avcHashStats["buckets_available"]; want != got {
|
||||
t.Errorf("want avc hash stat buckets available %v, got %v", want, got)
|
||||
}
|
||||
|
||||
if want, got := uint64(257), avcHashStats["buckets_used"]; want != got {
|
||||
t.Errorf("want avc hash stat buckets used %v, got %v", want, got)
|
||||
}
|
||||
|
||||
if want, got := uint64(8), avcHashStats["longest_chain"]; want != got {
|
||||
t.Errorf("want avc hash stat longest chain %v, got %v", want, got)
|
||||
}
|
||||
}
|
|
@ -4627,6 +4627,37 @@ Lines: 1
|
|||
4096
|
||||
Mode: 444
|
||||
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Directory: sys/fs/selinux/avc
|
||||
Mode: 755
|
||||
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Path: sys/fs/selinux/avc/cache_stats
|
||||
Lines: 17
|
||||
lookups hits misses allocations reclaims frees
|
||||
6603240 6601784 1456 1456 1424 1479
|
||||
5110559 5109586 973 973 1152 1162
|
||||
6720426 6719001 1425 1425 1488 1515
|
||||
4713015 4711743 1272 1272 1200 1211
|
||||
6949114 6947453 1661 1661 1504 1555
|
||||
5624578 5622931 1647 1647 1328 1347
|
||||
5884601 5882982 1619 1619 1488 1525
|
||||
3170931 3170061 870 870 832 858
|
||||
7336127 7334467 1660 1660 1632 1644
|
||||
2853424 2852652 772 772 704 712
|
||||
7596237 7594408 1829 1829 1936 1960
|
||||
4491113 4490186 927 927 832 854
|
||||
7319238 7317738 1500 1500 1504 1538
|
||||
4945119 4944151 968 968 912 943
|
||||
7610170 7608283 1887 1887 1648 1695
|
||||
4662892 4662026 866 866 816 828
|
||||
Mode: 644
|
||||
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Path: sys/fs/selinux/avc/hash_stats
|
||||
Lines: 3
|
||||
entries: 503
|
||||
buckets used: 257/512
|
||||
longest chain: 8
|
||||
Mode: 644
|
||||
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Directory: sys/fs/xfs
|
||||
Mode: 755
|
||||
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
|
|
@ -23,10 +23,21 @@ import (
|
|||
)
|
||||
|
||||
type selinuxCollector struct {
|
||||
configMode *prometheus.Desc
|
||||
currentMode *prometheus.Desc
|
||||
enabled *prometheus.Desc
|
||||
logger log.Logger
|
||||
avcAllocations *prometheus.Desc
|
||||
avcFrees *prometheus.Desc
|
||||
avcHashBucketsAvailable *prometheus.Desc
|
||||
avcHashBucketsUsed *prometheus.Desc
|
||||
avcHashEntries *prometheus.Desc
|
||||
avcHashLongestChain *prometheus.Desc
|
||||
avcHits *prometheus.Desc
|
||||
avcMisses *prometheus.Desc
|
||||
avcLookups *prometheus.Desc
|
||||
avcReclaims *prometheus.Desc
|
||||
avcThreshold *prometheus.Desc
|
||||
configMode *prometheus.Desc
|
||||
currentMode *prometheus.Desc
|
||||
enabled *prometheus.Desc
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
@ -53,6 +64,61 @@ func NewSelinuxCollector(logger log.Logger) (Collector, error) {
|
|||
"SELinux is enabled, 1 is true, 0 is false",
|
||||
nil, nil,
|
||||
),
|
||||
avcAllocations: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, subsystem, "avc_allocations_total"),
|
||||
"SELinux AVC allocations",
|
||||
nil, nil,
|
||||
),
|
||||
avcFrees: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, subsystem, "avc_frees_total"),
|
||||
"SELinux AVC frees",
|
||||
nil, nil,
|
||||
),
|
||||
avcHashBucketsAvailable: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, subsystem, "avc_hash_buckets_available"),
|
||||
"SELinux AVC hash buckets available",
|
||||
nil, nil,
|
||||
),
|
||||
avcHashBucketsUsed: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, subsystem, "avc_hash_buckets_used"),
|
||||
"SELinux AVC hash buckets used",
|
||||
nil, nil,
|
||||
),
|
||||
avcHashEntries: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, subsystem, "avc_hash_entries"),
|
||||
"SELinux AVC hash entries",
|
||||
nil, nil,
|
||||
),
|
||||
avcHashLongestChain: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, subsystem, "avc_hash_longest_chain"),
|
||||
"SELinux AVC hash longest chain",
|
||||
nil, nil,
|
||||
),
|
||||
avcHits: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, subsystem, "avc_hits_total"),
|
||||
"SELinux AVC hits",
|
||||
nil, nil,
|
||||
),
|
||||
avcMisses: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, subsystem, "avc_misses_total"),
|
||||
"SELinux AVC misses",
|
||||
nil, nil,
|
||||
),
|
||||
avcLookups: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, subsystem, "avc_lookups_total"),
|
||||
"SELinux AVC lookups",
|
||||
nil, nil,
|
||||
),
|
||||
avcReclaims: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, subsystem, "avc_reclaims_total"),
|
||||
"SELinux AVC reclaims",
|
||||
nil, nil,
|
||||
),
|
||||
avcThreshold: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(namespace, subsystem, "avc_threshold"),
|
||||
"SELinux AVC threshold",
|
||||
nil, nil,
|
||||
),
|
||||
logger: logger,
|
||||
}, nil
|
||||
}
|
||||
|
@ -74,5 +140,56 @@ func (c *selinuxCollector) Update(ch chan<- prometheus.Metric) error {
|
|||
ch <- prometheus.MustNewConstMetric(
|
||||
c.currentMode, prometheus.GaugeValue, float64(selinux.EnforceMode()))
|
||||
|
||||
avcStats, err := getAVCStats("fs/selinux/avc/cache_stats")
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.avcLookups, prometheus.CounterValue, float64(avcStats["lookups"]))
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.avcHits, prometheus.CounterValue, float64(avcStats["hits"]))
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.avcMisses, prometheus.CounterValue, float64(avcStats["misses"]))
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.avcAllocations, prometheus.CounterValue, float64(avcStats["allocations"]))
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.avcReclaims, prometheus.CounterValue, float64(avcStats["reclaims"]))
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.avcFrees, prometheus.CounterValue, float64(avcStats["frees"]))
|
||||
|
||||
avcHashStats, err := getAVCHashStats("fs/selinux/avc/hash_stats")
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.avcHashEntries, prometheus.GaugeValue, float64(avcHashStats["entries"]))
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.avcHashBucketsAvailable, prometheus.GaugeValue, float64(avcHashStats["buckets_available"]))
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.avcHashBucketsUsed, prometheus.GaugeValue, float64(avcHashStats["buckets_used"]))
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.avcHashLongestChain, prometheus.GaugeValue, float64(avcHashStats["longest_chain"]))
|
||||
|
||||
avcThreshold, err := readUintFromFile(sysFilePath("fs/selinux/avc/cache_threshold"))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.avcThreshold, prometheus.GaugeValue, float64(avcThreshold))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue