Skip to content

Commit 0e0ec91

Browse files
Shachar Sharonsynarete
authored andcommitted
metrics: support 'smbstatus --json' format
Samba's 'smbstatus' command now supports output in JSON format [1]. When possible, prefer this format to parse metrics informations. [1] https://wiki.samba.org/index.php/ Samba_4.17_Features_added/changed#JSON_support_for_smbstatus Signed-off-by: Shachar Sharon <[email protected]>
1 parent 6c3299f commit 0e0ec91

File tree

2 files changed

+562
-55
lines changed

2 files changed

+562
-55
lines changed

internal/metrics/smbstatus.go

Lines changed: 113 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,87 @@
33
package metrics
44

55
import (
6+
"encoding/json"
67
"errors"
78
"os"
89
"os/exec"
9-
"strconv"
1010
"strings"
1111
"time"
1212
)
1313

14+
// SmbStatusServerID represents a server_id output field
15+
type SmbStatusServerID struct {
16+
PID string `json:"pid"`
17+
TaskID string `json:"task_id"`
18+
VNN string `json:"vnn"`
19+
UniqueID string `json:"unique_id"`
20+
}
21+
22+
// SmbStatusEncryption represents a encryption output field
23+
type SmbStatusEncryption struct {
24+
Cipher string `json:"cipher"`
25+
Degree string `json:"degree"`
26+
}
27+
28+
// SmbStatusSigning represents a signing output field
29+
type SmbStatusSigning struct {
30+
Cipher string `json:"cipher"`
31+
Degree string `json:"degree"`
32+
}
33+
1434
// SmbStatusShare represents a single entry from the output of 'smbstatus -S'
1535
type SmbStatusShare struct {
16-
Service string
17-
PID int64
18-
Machine string
19-
ConnectedAt string
20-
ConnectedTime time.Time
21-
Encryption string
22-
Signing string
36+
Service string `json:"service"`
37+
ServerID SmbStatusServerID `json:"server_id"`
38+
Machine string `json:"machine"`
39+
ConnectedAt string `json:"connected_at"`
40+
Encryption SmbStatusEncryption `json:"encryption"`
41+
Signing SmbStatusSigning `json:"signing"`
42+
}
43+
44+
// SmbStatusSession represents a session output field
45+
type SmbStatusSession struct {
46+
SessionID string `json:"session_id"`
47+
ServerID SmbStatusServerID `json:"server_id"`
48+
UID int `json:"uid"`
49+
GID int `json:"gid"`
50+
Username string `json:"username"`
51+
Groupname string `json:"groupname"`
52+
RemoteMachine string `json:"remote_machine"`
53+
Hostname string `json:"hostname"`
54+
SessionDialect string `json:"session_dialect"`
55+
Encryption SmbStatusEncryption `json:"encryption"`
56+
Signing SmbStatusSigning `json:"signing"`
57+
}
58+
59+
// SmbStatusFileID represents a fileid output field
60+
type SmbStatusFileID struct {
61+
DevID int64 `json:"devid"`
62+
Inode int64 `json:"inode"`
63+
Extid int32 `json:"extid"`
64+
}
65+
66+
// SmbStatusLockedFile represents a locked-file output field
67+
type SmbStatusLockedFile struct {
68+
ServicePath string `json:"service_path"`
69+
Filename string `json:"filename"`
70+
FileID SmbStatusFileID `json:"fileid"`
71+
NumPendingDeletes int `json:"num_pending_deletes"`
72+
}
73+
74+
// SmbStatusJSON represents output of 'smbstatus --json'
75+
type SmbStatusJSON struct {
76+
Timestamp string `json:"timestamp"`
77+
Version string `json:"version"`
78+
SmbConf string `json:"smb_conf"`
79+
Sessions map[string]SmbStatusSession `json:"sessions"`
80+
TCons map[string]SmbStatusShare `json:"tcons"`
81+
LockedFiles map[string]SmbStatusLockedFile `json:"locked_files"`
2382
}
2483

2584
// SmbStatusProc represents a single entry from the output of 'smbstatus -p'
2685
type SmbStatusProc struct {
27-
PID int64
86+
PID string
2887
Username string
2988
Group string
3089
Machine string
@@ -35,8 +94,8 @@ type SmbStatusProc struct {
3594

3695
// SmbStatusLock represents a single entry from the output of 'smbstatus -L'
3796
type SmbStatusLock struct {
38-
PID int64
39-
UserID int64
97+
PID string
98+
UserID string
4099
DenyMode string
41100
Access string
42101
RW string
@@ -78,11 +137,29 @@ func RunSmbStatusVersion() (string, error) {
78137

79138
// RunSmbStatusShares executes 'smbstatus -S' on host container
80139
func RunSmbStatusShares() ([]SmbStatusShare, error) {
81-
dat, err := executeSmbStatusCommand("-S")
140+
// Case 1: using new json output
141+
dat, err := executeSmbStatusCommand("-S --json")
142+
if err == nil {
143+
return parseSmbStatusSharesAsJSON(dat)
144+
}
145+
// Case 2: fallback to raw-text output
146+
dat, err = executeSmbStatusCommand("-S")
147+
if err == nil {
148+
return parseSmbStatusShares(dat)
149+
}
150+
return []SmbStatusShare{}, err
151+
}
152+
153+
func parseSmbStatusSharesAsJSON(dat string) ([]SmbStatusShare, error) {
154+
shares := []SmbStatusShare{}
155+
res, err := parseSmbStatusJSON(dat)
82156
if err != nil {
83-
return []SmbStatusShare{}, err
157+
return shares, err
158+
}
159+
for _, share := range res.TCons {
160+
shares = append(shares, share)
84161
}
85-
return parseSmbStatusShares(dat)
162+
return shares, nil
86163
}
87164

88165
// RunSmbStatusLocks executes 'smbstatus -L' on host container
@@ -106,15 +183,19 @@ func RunSmbStatusProcs() ([]SmbStatusProc, error) {
106183
// SmbStatusSharesByMachine converts the output of RunSmbStatusShares into map
107184
// indexed by machine's host
108185
func SmbStatusSharesByMachine() (map[string][]SmbStatusShare, error) {
109-
ret := map[string][]SmbStatusShare{}
110186
shares, err := RunSmbStatusShares()
111187
if err != nil {
112-
return ret, err
188+
return map[string][]SmbStatusShare{}, err
113189
}
190+
return makeSmbSharesMap(shares), nil
191+
}
192+
193+
func makeSmbSharesMap(shares []SmbStatusShare) map[string][]SmbStatusShare {
194+
ret := map[string][]SmbStatusShare{}
114195
for _, share := range shares {
115196
ret[share.Machine] = append(ret[share.Machine], share)
116197
}
117-
return ret, nil
198+
return ret
118199
}
119200

120201
func executeSmbStatusCommand(args ...string) (string, error) {
@@ -175,18 +256,11 @@ func parseSmbStatusShares(data string) ([]SmbStatusShare, error) {
175256
// Parse data into internal repr
176257
share := SmbStatusShare{}
177258
share.Service = parseSubstr(ln, serviceIndex)
178-
pid, err := parseInt64(ln, pidIndex)
179-
if err != nil {
180-
return shares, err
181-
}
182-
share.PID = pid
259+
share.ServerID.PID = parseSubstr(ln, pidIndex)
183260
share.Machine = parseSubstr(ln, machineIndex)
184261
share.ConnectedAt = parseSubstr2(ln, connectedAtIndex, encryptionIndex)
185-
share.Encryption = parseSubstr(ln, encryptionIndex)
186-
share.Signing = parseSubstr(ln, signingIndex)
187-
if t, err := parseTime(share.ConnectedAt); err == nil {
188-
share.ConnectedTime = t
189-
}
262+
share.Encryption.Cipher = parseSubstr(ln, encryptionIndex)
263+
share.Signing.Cipher = parseSubstr(ln, signingIndex)
190264

191265
// Ignore "IPC$"
192266
if share.Service == "IPC$" {
@@ -239,11 +313,7 @@ func parseSmbStatusProcs(data string) ([]SmbStatusProc, error) {
239313
}
240314
// Parse data into internal repr
241315
proc := SmbStatusProc{}
242-
pid, err := parseInt64(ln, pidIndex)
243-
if err != nil {
244-
return procs, err
245-
}
246-
proc.PID = pid
316+
proc.PID = parseSubstr(ln, pidIndex)
247317
proc.Username = parseSubstr(ln, usernameIndex)
248318
proc.Group = parseSubstr(ln, groupIndex)
249319
proc.Machine = parseSubstr(ln, machineIndex)
@@ -300,16 +370,8 @@ func parseSmbStatusLocks(data string) ([]SmbStatusLock, error) {
300370
}
301371
// Parse data into internal repr
302372
lock := SmbStatusLock{}
303-
pid, err := parseInt64(ln, pidIndex)
304-
if err != nil {
305-
return locks, err
306-
}
307-
lock.PID = pid
308-
user, err := parseInt64(ln, userIndex)
309-
if err != nil {
310-
return locks, err
311-
}
312-
lock.UserID = user
373+
lock.PID = parseSubstr(ln, pidIndex)
374+
lock.UserID = parseSubstr(ln, userIndex)
313375
lock.DenyMode = parseSubstr(ln, denyModeIndex)
314376
lock.Access = parseSubstr(ln, accessIndex)
315377
lock.RW = parseSubstr(ln, rwIndex)
@@ -320,10 +382,6 @@ func parseSmbStatusLocks(data string) ([]SmbStatusLock, error) {
320382
return locks, nil
321383
}
322384

323-
func parseInt64(s string, startIndex int) (int64, error) {
324-
return strconv.ParseInt(parseSubstr(s, startIndex), 10, 64)
325-
}
326-
327385
func parseSubstr(s string, startIndex int) string {
328386
sub := strings.TrimSpace(s[startIndex:])
329387
fields := strings.Fields(sub)
@@ -337,7 +395,7 @@ func parseSubstr2(s string, startIndex, endIndex int) string {
337395
return strings.TrimSpace(s[startIndex:endIndex])
338396
}
339397

340-
func parseTime(s string) (time.Time, error) {
398+
func ParseTime(s string) (time.Time, error) {
341399
layouts := []string{
342400
time.ANSIC,
343401
time.UnixDate,
@@ -351,3 +409,11 @@ func parseTime(s string) (time.Time, error) {
351409
// samba's lib/util/time.c uses non standad layout...
352410
return time.Time{}, errors.New("unknow time format " + s)
353411
}
412+
413+
// parseSmbStatusJSON parses to output of 'smbstatus --json' into internal
414+
// representation.
415+
func parseSmbStatusJSON(data string) (*SmbStatusJSON, error) {
416+
res := SmbStatusJSON{}
417+
err := json.Unmarshal([]byte(data), &res)
418+
return &res, err
419+
}

0 commit comments

Comments
 (0)