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
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ $ curl --request GET "http://localhost:9922/metrics"
| `smb_sessions_total` | Number of active SMB sessions |
| `smb_tcon_total` | Number of active SMB tree-connections |
| `smb_users_total` | Number of connected users |
| `smb_openfiles_total` | Number of currently open files |
| `smb_openfiles_access_rw` | Open files with `"RW"` access-mask set |
| `smb_share_activity` | Number of remote machines using each share |
| `smb_share_byremote` | Number of shares used by each remote machine |

Expand Down
88 changes: 24 additions & 64 deletions internal/metrics/collectors.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ var (
func (sme *smbMetricsExporter) register() error {
cols := []prometheus.Collector{
sme.newSMBVersionsCollector(),
sme.newSMBActivityCollector(),
sme.newSMBSharesCollector(),
sme.newSMBStatusCollector(),
}
for _, c := range cols {
if err := sme.reg.Register(c); err != nil {
Expand Down Expand Up @@ -79,42 +78,42 @@ func (sme *smbMetricsExporter) newSMBVersionsCollector() prometheus.Collector {
return col
}

type smbActivityCollector struct {
type smbStatusCollector struct {
smbCollector
}

func (col *smbActivityCollector) Collect(ch chan<- prometheus.Metric) {
totalSessions := 0
totalTreeCons := 0
totalConnectedUsers := 0
totalOpenFiles := 0
totalOpenFilesAccessRW := 0
func (col *smbStatusCollector) Collect(ch chan<- prometheus.Metric) {
smbInfo, err := NewUpdatedSMBInfo()
if err == nil {
totalSessions = smbInfo.TotalSessions()
totalTreeCons = smbInfo.TotalTreeCons()
totalConnectedUsers = smbInfo.TotalConnectedUsers()
totalOpenFiles = smbInfo.TotalOpenFiles()
totalOpenFilesAccessRW = smbInfo.TotalOpenFilesAccessRW()
if err != nil {
return
}
ch <- prometheus.MustNewConstMetric(col.dsc[0],
prometheus.GaugeValue, float64(totalSessions))
prometheus.GaugeValue, float64(smbInfo.TotalSessions()))

ch <- prometheus.MustNewConstMetric(col.dsc[1],
prometheus.GaugeValue, float64(totalTreeCons))
prometheus.GaugeValue, float64(smbInfo.TotalTreeCons()))

ch <- prometheus.MustNewConstMetric(col.dsc[2],
prometheus.GaugeValue, float64(totalConnectedUsers))

ch <- prometheus.MustNewConstMetric(col.dsc[3],
prometheus.GaugeValue, float64(totalOpenFiles))
prometheus.GaugeValue, float64(smbInfo.TotalConnectedUsers()))

ch <- prometheus.MustNewConstMetric(col.dsc[4],
prometheus.GaugeValue, float64(totalOpenFilesAccessRW))
serviceToMachine := smbInfo.MapServiceToMachines()
for service, machines := range serviceToMachine {
ch <- prometheus.MustNewConstMetric(col.dsc[3],
prometheus.GaugeValue,
float64(len(machines)),
service)
}
machineToServices := smbInfo.MapMachineToServies()
for machine, services := range machineToServices {
ch <- prometheus.MustNewConstMetric(col.dsc[4],
prometheus.GaugeValue,
float64(len(services)),
machine)
}
}

func (sme *smbMetricsExporter) newSMBActivityCollector() prometheus.Collector {
col := &smbActivityCollector{}
func (sme *smbMetricsExporter) newSMBStatusCollector() prometheus.Collector {
col := &smbStatusCollector{}
col.sme = sme
col.dsc = []*prometheus.Desc{
prometheus.NewDesc(
Expand All @@ -132,45 +131,6 @@ func (sme *smbMetricsExporter) newSMBActivityCollector() prometheus.Collector {
"Number of currently active SMB users",
[]string{}, nil),

prometheus.NewDesc(
collectorName("openfiles", "total"),
"Number of currently open files",
[]string{}, nil),

prometheus.NewDesc(
collectorName("openfiles", "access_rw"),
"Number of open files with read-write access mode",
[]string{}, nil),
}
return col
}

type smbSharesCollector struct {
smbCollector
}

func (col *smbSharesCollector) Collect(ch chan<- prometheus.Metric) {
smbInfo, _ := NewUpdatedSMBInfo()
serviceToMachine := smbInfo.MapServiceToMachines()
for service, machines := range serviceToMachine {
ch <- prometheus.MustNewConstMetric(col.dsc[0],
prometheus.GaugeValue,
float64(len(machines)),
service)
}
machineToServices := smbInfo.MapMachineToServies()
for machine, services := range machineToServices {
ch <- prometheus.MustNewConstMetric(col.dsc[1],
prometheus.GaugeValue,
float64(len(services)),
machine)
}
}

func (sme *smbMetricsExporter) newSMBSharesCollector() prometheus.Collector {
col := &smbSharesCollector{}
col.sme = sme
col.dsc = []*prometheus.Desc{
prometheus.NewDesc(
collectorName("share", "activity"),
"Number of remote machines currently using a share",
Expand Down
51 changes: 21 additions & 30 deletions internal/metrics/smbinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@

package metrics

import "strings"

// SMBInfo provides a bridge layer between raw smbstatus info and exported
// metric counters. It also implements the more complex logic which requires in
// memory re-mapping of the low-level information (e.g., stats by machine/user).
type SMBInfo struct {
smbstat *SMBStatus
tconsStatus *SMBStatus
sessionsStatus *SMBStatus
}

func NewSMBInfo() *SMBInfo {
return &SMBInfo{smbstat: NewSMBStatus()}
return &SMBInfo{
tconsStatus: NewSMBStatus(),
sessionsStatus: NewSMBStatus(),
}
}

func NewUpdatedSMBInfo() (*SMBInfo, error) {
Expand All @@ -22,21 +24,26 @@ func NewUpdatedSMBInfo() (*SMBInfo, error) {
}

func (smbinfo *SMBInfo) Update() error {
smbstat, err := RunSMBStatus()
tconsStatus, err := RunSMBStatusShares()
if err != nil {
return err
}
smbinfo.smbstat = smbstat
sessionsStatus, err := RunSMBStatusProcesses()
if err != nil {
return err
}
smbinfo.tconsStatus = tconsStatus
smbinfo.sessionsStatus = sessionsStatus
return nil
}

func (smbinfo *SMBInfo) TotalSessions() int {
return len(smbinfo.smbstat.Sessions)
return len(smbinfo.sessionsStatus.Sessions)
}

func (smbinfo *SMBInfo) TotalTreeCons() int {
total := 0
for _, tcon := range smbinfo.smbstat.TCons {
for _, tcon := range smbinfo.tconsStatus.TCons {
serviceID := tcon.Service
if isInternalServiceID(serviceID) {
continue
Expand All @@ -46,25 +53,9 @@ func (smbinfo *SMBInfo) TotalTreeCons() int {
return total
}

func (smbinfo *SMBInfo) TotalOpenFiles() int {
return len(smbinfo.smbstat.OpenFiles)
}

func (smbinfo *SMBInfo) TotalOpenFilesAccessRW() int {
total := 0
for _, openf := range smbinfo.smbstat.OpenFiles {
for _, opens := range openf.Opens {
if strings.Contains(opens.AccessMask.Text, "RW") {
total++
}
}
}
return total
}

func (smbinfo *SMBInfo) TotalConnectedUsers() int {
users := map[string]bool{}
for _, session := range smbinfo.smbstat.Sessions {
for _, session := range smbinfo.sessionsStatus.Sessions {
username := session.Username
if len(username) > 0 {
users[username] = true
Expand All @@ -75,7 +66,7 @@ func (smbinfo *SMBInfo) TotalConnectedUsers() int {

func (smbinfo *SMBInfo) MapMachineToSessions() map[string][]*SMBStatusSession {
ret := map[string][]*SMBStatusSession{}
for _, session := range smbinfo.smbstat.Sessions {
for _, session := range smbinfo.sessionsStatus.Sessions {
machineID := session.RemoteMachine
sessionRef := &session
ret[machineID] = append(ret[machineID], sessionRef)
Expand All @@ -85,7 +76,7 @@ func (smbinfo *SMBInfo) MapMachineToSessions() map[string][]*SMBStatusSession {

func (smbinfo *SMBInfo) MapServiceToTreeCons() map[string][]*SMBStatusTreeCon {
ret := map[string][]*SMBStatusTreeCon{}
for _, tcon := range smbinfo.smbstat.TCons {
for _, tcon := range smbinfo.tconsStatus.TCons {
serviceID := tcon.Service
if isInternalServiceID(serviceID) {
continue
Expand All @@ -98,7 +89,7 @@ func (smbinfo *SMBInfo) MapServiceToTreeCons() map[string][]*SMBStatusTreeCon {

func (smbinfo *SMBInfo) MapMachineToTreeCons() map[string][]*SMBStatusTreeCon {
ret := map[string][]*SMBStatusTreeCon{}
for _, tcon := range smbinfo.smbstat.TCons {
for _, tcon := range smbinfo.tconsStatus.TCons {
serviceID := tcon.Service
if isInternalServiceID(serviceID) {
continue
Expand All @@ -112,7 +103,7 @@ func (smbinfo *SMBInfo) MapMachineToTreeCons() map[string][]*SMBStatusTreeCon {

func (smbinfo *SMBInfo) MapServiceToMachines() map[string]map[string]int {
ret := map[string]map[string]int{}
for _, tcon := range smbinfo.smbstat.TCons {
for _, tcon := range smbinfo.tconsStatus.TCons {
serviceID := tcon.Service
if isInternalServiceID(serviceID) {
continue
Expand All @@ -130,7 +121,7 @@ func (smbinfo *SMBInfo) MapServiceToMachines() map[string]map[string]int {

func (smbinfo *SMBInfo) MapMachineToServies() map[string]map[string]int {
ret := map[string]map[string]int{}
for _, tcon := range smbinfo.smbstat.TCons {
for _, tcon := range smbinfo.tconsStatus.TCons {
serviceID := tcon.Service
if isInternalServiceID(serviceID) {
continue
Expand Down
68 changes: 36 additions & 32 deletions internal/metrics/smbstatus.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,12 +138,11 @@ type SMBStatusOpenLease struct {

// SMBStatus represents output of 'smbstatus --json'
type SMBStatus struct {
Timestamp string `json:"timestamp"`
Version string `json:"version"`
SmbConf string `json:"smb_conf"`
Sessions map[string]SMBStatusSession `json:"sessions"`
TCons map[string]SMBStatusTreeCon `json:"tcons"`
OpenFiles map[string]SMBStatusOpenFile `json:"open_files"`
Timestamp string `json:"timestamp"`
Version string `json:"version"`
SmbConf string `json:"smb_conf"`
Sessions map[string]SMBStatusSession `json:"sessions"`
TCons map[string]SMBStatusTreeCon `json:"tcons"`
}

// SMBStatusLocks represents output of 'smbstatus -L --json'
Expand Down Expand Up @@ -175,15 +174,6 @@ func LocateSMBStatus() (string, error) {
return "", errors.New("failed to locate smbstatus")
}

// RunSMBStatus executes 'smbstatus --json' on host machine
func RunSMBStatus() (*SMBStatus, error) {
dat, err := executeSMBStatusCommand("--json")
if err != nil {
return &SMBStatus{}, err
}
return parseSMBStatus(dat)
}

// RunSMBStatusVersion executes 'smbstatus --version' on host container
func RunSMBStatusVersion() (string, error) {
ver, err := executeSMBStatusCommand("--version")
Expand All @@ -193,30 +183,27 @@ func RunSMBStatusVersion() (string, error) {
return ver, nil
}

// RunSMBStatusShares executes 'smbstatus --shares --json' on host
func RunSMBStatusShares() ([]SMBStatusTreeCon, error) {
dat, err := executeSMBStatusCommand("--shares --json")
// RunSMBStatusShares executes 'smbstatus --processes --json' on host
func RunSMBStatusProcesses() (*SMBStatus, error) {
dat, err := executeSMBStatusCommand("--processes", "--json")
if err != nil {
return []SMBStatusTreeCon{}, err
return &SMBStatus{}, err
}
return parseSMBStatusTreeCons(dat)
return parseSMBStatus(dat)
}

func parseSMBStatusTreeCons(dat string) ([]SMBStatusTreeCon, error) {
tcons := []SMBStatusTreeCon{}
res, err := parseSMBStatus(dat)
// RunSMBStatusShares executes 'smbstatus --shares --json' on host
func RunSMBStatusShares() (*SMBStatus, error) {
dat, err := executeSMBStatusCommand("--shares", "--json")
if err != nil {
return tcons, err
}
for _, share := range res.TCons {
tcons = append(tcons, share)
return &SMBStatus{}, err
}
return tcons, nil
return parseSMBStatus(dat)
}

// RunSMBStatusLocks executes 'smbstatus --locks --json' on host
func RunSMBStatusLocks() ([]SMBStatusOpenFile, error) {
dat, err := executeSMBStatusCommand("--locks --json")
dat, err := executeSMBStatusCommand("--locks", "--json")
if err != nil {
return []SMBStatusOpenFile{}, err
}
Expand All @@ -238,11 +225,11 @@ func parseSMBStatusLockedFiles(dat string) ([]SMBStatusOpenFile, error) {
// SMBStatusSharesByMachine converts the output of RunSMBStatusShares into map
// indexed by machine's host
func SMBStatusSharesByMachine() (map[string][]SMBStatusTreeCon, error) {
tcons, err := RunSMBStatusShares()
smbstat, err := RunSMBStatusShares()
if err != nil {
return map[string][]SMBStatusTreeCon{}, err
}
return makeSmbSharesMap(tcons), nil
return makeSmbSharesMap(smbstat.ListTreeCons()), nil
}

func makeSmbSharesMap(tcons []SMBStatusTreeCon) map[string][]SMBStatusTreeCon {
Expand Down Expand Up @@ -295,7 +282,24 @@ func NewSMBStatus() *SMBStatus {
SmbConf: "",
Sessions: map[string]SMBStatusSession{},
TCons: map[string]SMBStatusTreeCon{},
OpenFiles: map[string]SMBStatusOpenFile{},
}
return &smbStatus
}

// ListSessions returns a slice for mapped sessions
func (smbstat *SMBStatus) ListSessions() []SMBStatusSession {
sessions := []SMBStatusSession{}
for _, session := range smbstat.Sessions {
sessions = append(sessions, session)
}
return sessions
}

// ListTreeCons returns a slice for mapped tree-connection
func (smbstat *SMBStatus) ListTreeCons() []SMBStatusTreeCon {
tcons := []SMBStatusTreeCon{}
for _, share := range smbstat.TCons {
tcons = append(tcons, share)
}
return tcons
}
Loading
Loading