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
79 changes: 78 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ $ make image-build

When running (by privileged user) along-side active SMB server, `smbmetrics`
exports a set of gauge metrics over HTTP via port `9922`. Most metrics become
visible only when active SMB connections exists. Execute the folowing `curl`
visible only when active SMB connections exists. When Samba is compiled and
run with profile-information enabled (`smb.conf` global section has
`smbd profiling level = on`), you may run `smbmetrics --profile` to export also
various profile stats as Prometheus metrics. Execute the folowing `curl`
command on the same machine where you run `smbmetrics` instance:

```console
Expand All @@ -36,7 +39,15 @@ $ curl --request GET "http://localhost:9922/metrics"
| `smb_share_byremote` | Number of shares used by each remote machine |


## Profile metrics

| Metric name | Description |
|---------------------------|--------------------------------------------------|
| `smb_smb2_request_total` | Number of SMB2 requests |
| `smb_vfs_call_total` | Number of calls to VFS layer |
| `smb_vfs_io_call_total` | Number of I/O calls to VFS layer |

smb_vfs_call_total
## Example

The following example is from a setup with 2 shares and 2 users connected and
Expand Down Expand Up @@ -74,3 +85,69 @@ smb_share_byremote{machine="192.168.122.72"} 1
smb_share_byremote{machine="192.168.122.73"} 2
smb_share_byremote{machine="192.168.122.74"} 1
```

When running with profile enabled, we get also the following metrics:

```console
# HELP smb_smb2_request_total Total number of SMB2 requests
# TYPE smb_smb2_request_total gauge
smb_smb2_request_total{idle="0",inbytes="0",operation="break",outbytes="0",time="0"} 0
smb_smb2_request_total{idle="0",inbytes="0",operation="cancel",outbytes="0",time="0"} 0
smb_smb2_request_total{idle="0",inbytes="0",operation="lock",outbytes="0",time="0"} 0
smb_smb2_request_total{idle="0",inbytes="0",operation="logoff",outbytes="0",time="0"} 0
smb_smb2_request_total{idle="0",inbytes="0",operation="notify",outbytes="0",time="0"} 0
smb_smb2_request_total{idle="0",inbytes="0",operation="tdis",outbytes="0",time="0"} 0
smb_smb2_request_total{idle="0",inbytes="1156",operation="keepalive",outbytes="1156",time="98"} 17
smb_smb2_request_total{idle="0",inbytes="12995",operation="read",outbytes="232793072",time="102307"} 115
smb_smb2_request_total{idle="0",inbytes="16852",operation="getinfo",outbytes="27754",time="26869"} 162
smb_smb2_request_total{idle="0",inbytes="23069904",operation="write",outbytes="880",time="30958"} 11
smb_smb2_request_total{idle="0",inbytes="240",operation="negprot",outbytes="268",time="4049881"} 1
smb_smb2_request_total{idle="0",inbytes="240",operation="tcon",outbytes="160",time="97672"} 2
smb_smb2_request_total{idle="0",inbytes="26312",operation="close",outbytes="37712",time="101378"} 299
smb_smb2_request_total{idle="0",inbytes="295",operation="ioctl",outbytes="337",time="186"} 2
smb_smb2_request_total{idle="0",inbytes="3136",operation="setinfo",outbytes="1740",time="56382"} 26
smb_smb2_request_total{idle="0",inbytes="430",operation="sesssetup",outbytes="264",time="15242"} 2
smb_smb2_request_total{idle="0",inbytes="74352",operation="create",outbytes="82692",time="705058"} 363
smb_smb2_request_total{idle="0",inbytes="968",operation="flush",outbytes="748",time="1609"} 11
smb_smb2_request_total{idle="0",inbytes="980",operation="find",outbytes="3629",time="19929"} 10
# HELP smb_vfs_call_total Total number of calls to underlying VFS layer
# TYPE smb_vfs_call_total gauge
smb_vfs_call_total{operation="chdir",time="9045"} 209
smb_vfs_call_total{operation="chmod",time="0"} 0
smb_vfs_call_total{operation="close",time="12056"} 873
smb_vfs_call_total{operation="closedir",time="3738"} 83
smb_vfs_call_total{operation="createfile",time="0"} 0
smb_vfs_call_total{operation="fallocate",time="0"} 0
smb_vfs_call_total{operation="fchmod",time="603"} 11
smb_vfs_call_total{operation="fchown",time="0"} 0
smb_vfs_call_total{operation="fdopendir",time="1839"} 83
smb_vfs_call_total{operation="fntimes",time="14315"} 22
smb_vfs_call_total{operation="fstat",time="42921"} 1790
smb_vfs_call_total{operation="fstatat",time="0"} 0
smb_vfs_call_total{operation="ftruncate",time="0"} 0
smb_vfs_call_total{operation="getwd",time="7"} 2
smb_vfs_call_total{operation="lchown",time="0"} 0
smb_vfs_call_total{operation="linkat",time="0"} 0
smb_vfs_call_total{operation="lseek",time="0"} 0
smb_vfs_call_total{operation="lstat",time="9"} 1
smb_vfs_call_total{operation="mkdirat",time="2614"} 2
smb_vfs_call_total{operation="mknodat",time="0"} 0
smb_vfs_call_total{operation="open",time="0"} 0
smb_vfs_call_total{operation="openat",time="138723"} 1034
smb_vfs_call_total{operation="opendir",time="0"} 0
smb_vfs_call_total{operation="readdir",time="305110"} 534
smb_vfs_call_total{operation="readlinkat",time="0"} 0
smb_vfs_call_total{operation="realpath",time="17"} 4
smb_vfs_call_total{operation="renameat",time="22538"} 5
smb_vfs_call_total{operation="rewinddir",time="0"} 0
smb_vfs_call_total{operation="stat",time="42770"} 479
smb_vfs_call_total{operation="symlinkat",time="0"} 0
smb_vfs_call_total{operation="unlinkat",time="7843"} 10
# HELP smb_vfs_io_call_total Total number of I/O calls to underlying VFS layer
# TYPE smb_vfs_io_call_total gauge
smb_vfs_io_call_total{bytes="0",idle="0",operation="pread",time="0"} 0
smb_vfs_io_call_total{bytes="0",idle="0",operation="pwrite",time="0"} 0
smb_vfs_io_call_total{bytes="0",idle="14",operation="asys_fsync",time="1416"} 11
smb_vfs_io_call_total{bytes="23068672",idle="23",operation="asys_pwrite",time="29534"} 11
smb_vfs_io_call_total{bytes="232783872",idle="267",operation="asys_pread",time="99338"} 115
```
5 changes: 4 additions & 1 deletion cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ func main() {
var port int
pflag.IntVar(&port, "port", metrics.DefaultMetricsPort,
"Prometheus metrics-exporter port number")
var profile bool
pflag.BoolVar(&profile, "profile", false,
"Run with collect profile information enabled")
pflag.Parse()

log := zap.New(zap.UseDevMode(true))
Expand Down Expand Up @@ -54,7 +57,7 @@ func main() {
}
log.Info("Located smbstatus", "path", loc, "version", ver)

err = metrics.RunSmbMetricsExporter(log, port)
err = metrics.RunSmbMetricsExporter(log, port, profile)
if err != nil {
os.Exit(1)
}
Expand Down
130 changes: 130 additions & 0 deletions internal/metrics/collectors.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
package metrics

import (
"strconv"

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

Expand All @@ -14,6 +16,7 @@ func (sme *smbMetricsExporter) register() error {
cols := []prometheus.Collector{
sme.newSMBVersionsCollector(),
sme.newSMBStatusCollector(),
sme.newSMBProfileCollector(),
}
for _, c := range cols {
if err := sme.reg.Register(c); err != nil {
Expand Down Expand Up @@ -144,6 +147,133 @@ func (sme *smbMetricsExporter) newSMBStatusCollector() prometheus.Collector {
return col
}

type smbProfileCollector struct {
smbCollector
}

func (col *smbProfileCollector) Collect(ch chan<- prometheus.Metric) {
if col.sme.profile {
smbProfileInfo, err := NewUpdatedSMBProfileInfo()
if err == nil {
smb2Calls := smbProfileInfo.profileStatus.SMB2Calls
ch <- col.smb2RequestMetric(&smb2Calls.NegProt, "negprot")
ch <- col.smb2RequestMetric(&smb2Calls.SessSetup, "sesssetup")
ch <- col.smb2RequestMetric(&smb2Calls.LogOff, "logoff")
ch <- col.smb2RequestMetric(&smb2Calls.Tcon, "tcon")
ch <- col.smb2RequestMetric(&smb2Calls.Tdis, "tdis")
ch <- col.smb2RequestMetric(&smb2Calls.Create, "create")
ch <- col.smb2RequestMetric(&smb2Calls.Close, "close")
ch <- col.smb2RequestMetric(&smb2Calls.Flush, "flush")
ch <- col.smb2RequestMetric(&smb2Calls.Read, "read")
ch <- col.smb2RequestMetric(&smb2Calls.Write, "write")
ch <- col.smb2RequestMetric(&smb2Calls.Lock, "lock")
ch <- col.smb2RequestMetric(&smb2Calls.Ioctl, "ioctl")
ch <- col.smb2RequestMetric(&smb2Calls.Cancel, "cancel")
ch <- col.smb2RequestMetric(&smb2Calls.KeepAlive, "keepalive")
ch <- col.smb2RequestMetric(&smb2Calls.Find, "find")
ch <- col.smb2RequestMetric(&smb2Calls.Notify, "notify")
ch <- col.smb2RequestMetric(&smb2Calls.GetInfo, "getinfo")
ch <- col.smb2RequestMetric(&smb2Calls.SetInfo, "setinfo")
ch <- col.smb2RequestMetric(&smb2Calls.Break, "break")

sysCalls := smbProfileInfo.profileStatus.SystemCalls
ch <- col.vfsIORequestMetric(&sysCalls.PRead, "pread")
ch <- col.vfsIORequestMetric(&sysCalls.AsysPRead, "asys_pread")
ch <- col.vfsIORequestMetric(&sysCalls.PWrite, "pwrite")
ch <- col.vfsIORequestMetric(&sysCalls.AsysPWrite, "asys_pwrite")
ch <- col.vfsIORequestMetric(&sysCalls.AsysFSync, "asys_fsync")

ch <- col.vfsRequestMetric(&sysCalls.Opendir, "opendir")
ch <- col.vfsRequestMetric(&sysCalls.FDOpendir, "fdopendir")
ch <- col.vfsRequestMetric(&sysCalls.Readdir, "readdir")
ch <- col.vfsRequestMetric(&sysCalls.Rewinddir, "rewinddir")
ch <- col.vfsRequestMetric(&sysCalls.Mkdirat, "mkdirat")
ch <- col.vfsRequestMetric(&sysCalls.Closedir, "closedir")
ch <- col.vfsRequestMetric(&sysCalls.Open, "open")
ch <- col.vfsRequestMetric(&sysCalls.OpenAt, "openat")
ch <- col.vfsRequestMetric(&sysCalls.CreateFile, "createfile")
ch <- col.vfsRequestMetric(&sysCalls.Close, "close")
ch <- col.vfsRequestMetric(&sysCalls.Lseek, "lseek")
ch <- col.vfsRequestMetric(&sysCalls.RenameAt, "renameat")
ch <- col.vfsRequestMetric(&sysCalls.Stat, "stat")
ch <- col.vfsRequestMetric(&sysCalls.FStat, "fstat")
ch <- col.vfsRequestMetric(&sysCalls.LStat, "lstat")
ch <- col.vfsRequestMetric(&sysCalls.FStatAt, "fstatat")
ch <- col.vfsRequestMetric(&sysCalls.UnlinkAt, "unlinkat")
ch <- col.vfsRequestMetric(&sysCalls.Chmod, "chmod")
ch <- col.vfsRequestMetric(&sysCalls.FChmod, "fchmod")
ch <- col.vfsRequestMetric(&sysCalls.FChown, "fchown")
ch <- col.vfsRequestMetric(&sysCalls.LChown, "lchown")
ch <- col.vfsRequestMetric(&sysCalls.Chdir, "chdir")
ch <- col.vfsRequestMetric(&sysCalls.GetWD, "getwd")
ch <- col.vfsRequestMetric(&sysCalls.Fntimes, "fntimes")
ch <- col.vfsRequestMetric(&sysCalls.FTruncate, "ftruncate")
ch <- col.vfsRequestMetric(&sysCalls.FAllocate, "fallocate")
ch <- col.vfsRequestMetric(&sysCalls.ReadLinkAt, "readlinkat")
ch <- col.vfsRequestMetric(&sysCalls.SymLinkAt, "symlinkat")
ch <- col.vfsRequestMetric(&sysCalls.LinkAt, "linkat")
ch <- col.vfsRequestMetric(&sysCalls.MknodAt, "mknodat")
ch <- col.vfsRequestMetric(&sysCalls.RealPath, "realpath")
}
}
}

func (col *smbProfileCollector) smb2RequestMetric(pce *SMBProfileCallEntry,
operation string) prometheus.Metric {
return prometheus.MustNewConstMetric(
col.dsc[0],
prometheus.GaugeValue,
float64(pce.Count),
strconv.Itoa(pce.Time),
strconv.Itoa(pce.Idle),
strconv.Itoa(pce.Inbytes),
strconv.Itoa(pce.Outbytes),
operation)
}

func (col *smbProfileCollector) vfsIORequestMetric(pioe *SMBProfileIOEntry,
operation string) prometheus.Metric {
return prometheus.MustNewConstMetric(
col.dsc[1],
prometheus.GaugeValue,
float64(pioe.Count),
strconv.Itoa(pioe.Time),
strconv.Itoa(pioe.Idle),
strconv.Itoa(pioe.Bytes),
operation)
}

func (col *smbProfileCollector) vfsRequestMetric(pe *SMBProfileEntry,
operation string) prometheus.Metric {
return prometheus.MustNewConstMetric(
col.dsc[2],
prometheus.GaugeValue,
float64(pe.Count),
strconv.Itoa(pe.Time),
operation)
}

func (sme *smbMetricsExporter) newSMBProfileCollector() prometheus.Collector {
col := &smbProfileCollector{}
col.sme = sme
col.dsc = []*prometheus.Desc{
prometheus.NewDesc(
collectorName("smb2", "request_total"),
"Total number of SMB2 requests",
[]string{"time", "idle", "inbytes", "outbytes", "operation"}, nil),
prometheus.NewDesc(
collectorName("vfs_io", "call_total"),
"Total number of I/O calls to underlying VFS layer",
[]string{"time", "idle", "bytes", "operation"}, nil),
prometheus.NewDesc(
collectorName("vfs", "call_total"),
"Total number of calls to underlying VFS layer",
[]string{"time", "operation"}, nil),
}

return col
}

func collectorName(subsystem, name string) string {
return prometheus.BuildFQName(collectorsNamespace, subsystem, name)
}
24 changes: 13 additions & 11 deletions internal/metrics/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,20 @@ var (
)

type smbMetricsExporter struct {
log logr.Logger
reg *prometheus.Registry
mux *http.ServeMux
port int
log logr.Logger
reg *prometheus.Registry
mux *http.ServeMux
port int
profile bool
}

func newSmbMetricsExporter(log logr.Logger, port int) *smbMetricsExporter {
func newSmbMetricsExporter(log logr.Logger, port int, profile bool) *smbMetricsExporter {
return &smbMetricsExporter{
log: log,
reg: prometheus.NewRegistry(),
mux: http.NewServeMux(),
port: port,
log: log,
reg: prometheus.NewRegistry(),
mux: http.NewServeMux(),
port: port,
profile: profile,
}
}

Expand Down Expand Up @@ -63,11 +65,11 @@ func (sme *smbMetricsExporter) serve() error {

// RunSmbMetricsExporter executes an HTTP server and exports SMB metrics to
// Prometheus.
func RunSmbMetricsExporter(log logr.Logger, port int) error {
func RunSmbMetricsExporter(log logr.Logger, port int, profile bool) error {
if port <= 0 {
port = DefaultMetricsPort
}
sme := newSmbMetricsExporter(log, port)
sme := newSmbMetricsExporter(log, port, profile)
err := sme.init()
if err != nil {
return err
Expand Down
27 changes: 27 additions & 0 deletions internal/metrics/smbinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,30 @@ func (smbinfo *SMBInfo) MapMachineToServies() map[string]map[string]int {
func isInternalServiceID(serviceID string) bool {
return serviceID == "IPC$"
}

// SMBProfileInfo provides a bridge layer between raw smbstatus profile info and
// exported metric counters.
type SMBProfileInfo struct {
profileStatus *SMBProfile
}

func NewSMBProfileInfo() *SMBProfileInfo {
return &SMBProfileInfo{
profileStatus: NewSMBProfile(),
}
}

func NewUpdatedSMBProfileInfo() (*SMBProfileInfo, error) {
smbProfileInfo := NewSMBProfileInfo()
err := smbProfileInfo.Update()
return smbProfileInfo, err
}

func (smbProfileInfo *SMBProfileInfo) Update() error {
profiuleStatus, err := RunSMBStatusProfile()
if err != nil {
return err
}
smbProfileInfo.profileStatus = profiuleStatus
return nil
}
Loading