Skip to content

Commit 13026a9

Browse files
Generic logging interface (#135)
* use generic logging interface
1 parent da661ea commit 13026a9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1745
-1966
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,6 @@ Session.vim
2727
tags
2828
# Persistent undo
2929
[._]*.un~
30+
31+
# vscode
32+
*.code-workspace

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ env:
88
install:
99
- curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.31.0
1010
before_script:
11-
- golangci-lint run --no-config --disable-all --enable=vet --enable=gofmt --enable=gocyclo --enable=golint --enable=ineffassign --enable=misspell --enable=deadcode --tests=false ./...
11+
- golangci-lint run --no-config --disable-all --enable=vet --enable=gofmt --enable=gocyclo --enable=golint --enable=ineffassign --enable=misspell --enable=deadcode --tests=false ./...
1212
script:
1313
- go test ./...
1414
notifications:

discover/discover.go

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
package discover
22

33
import (
4+
"context"
45
"os"
56

6-
log "github.com/sirupsen/logrus"
7-
87
"github.com/bmc-toolbox/bmclib/errors"
98
"github.com/bmc-toolbox/bmclib/internal/httpclient"
10-
_ "github.com/bmc-toolbox/bmclib/logging" // this make possible to setup logging and properties at any stage
9+
"github.com/bmc-toolbox/bmclib/logging"
10+
1111
"github.com/bmc-toolbox/bmclib/providers/dummy/ibmc"
12+
"github.com/go-logr/logr"
1213
)
1314

1415
const (
@@ -24,16 +25,19 @@ const (
2425

2526
// ScanAndConnect will scan the bmc trying to learn the device type and return a working connection.
2627
func ScanAndConnect(host string, username string, password string, options ...Option) (bmcConnection interface{}, err error) {
27-
log.WithFields(log.Fields{"step": "ScanAndConnect", "host": host}).Debug("detecting vendor")
28-
2928
opts := &Options{HintCallback: func(_ string) error { return nil }}
3029
for _, optFn := range options {
3130
optFn(opts)
3231
}
32+
if opts.Logger == nil {
33+
// create a default logger
34+
opts.Logger = logging.DefaultLogger()
35+
}
36+
opts.Logger.V(1).Info("detecting vendor", "step", "ScanAndConnect", "host", host)
3337

3438
// return a connection to our dummy device.
3539
if os.Getenv("BMCLIB_TEST") == "1" {
36-
log.WithFields(log.Fields{"step": "ScanAndConnect", "host": host}).Debug("returning connection to dummy ibmc device.")
40+
opts.Logger.V(1).Info("returning connection to dummy ibmc device.", "step", "ScanAndConnect", "host", host)
3741
bmc, err := ibmc.New(host, username, password)
3842
return bmc, err
3943
}
@@ -45,7 +49,7 @@ func ScanAndConnect(host string, username string, password string, options ...Op
4549

4650
var probe = Probe{client: client, username: username, password: password, host: host}
4751

48-
var devices = map[string]func() (interface{}, error){
52+
var devices = map[string]func(context.Context, logr.Logger) (interface{}, error){
4953
ProbeHpIlo: probe.hpIlo,
5054
ProbeIdrac8: probe.idrac8,
5155
ProbeIdrac9: probe.idrac9,
@@ -73,9 +77,9 @@ func ScanAndConnect(host string, username string, password string, options ...Op
7377
for _, probeID := range order {
7478
probeDevice := devices[probeID]
7579

76-
log.WithFields(log.Fields{"step": "ScanAndConnect", "host": host}).Debug("probing to identify device")
80+
opts.Logger.V(1).Info("probing to identify device", "step", "ScanAndConnect", "host", host)
7781

78-
bmcConnection, err := probeDevice()
82+
bmcConnection, err := probeDevice(opts.Context, opts.Logger)
7983

8084
// if the device didn't match continue to probe
8185
if err != nil && (err == errors.ErrDeviceNotMatched) {
@@ -98,6 +102,7 @@ func ScanAndConnect(host string, username string, password string, options ...Op
98102
return nil, errors.ErrVendorUnknown
99103
}
100104

105+
// Options to pass in
101106
type Options struct {
102107
// Hint is a probe ID that hints which probe should be probed first.
103108
Hint string
@@ -107,6 +112,8 @@ type Options struct {
107112
// If your code persists the hint as "best effort", always return a nil error. Callback is
108113
// synchronous.
109114
HintCallback func(string) error
115+
Logger logr.Logger
116+
Context context.Context
110117
}
111118

112119
// Option is part of the functional options pattern, see the `With*` functions and
@@ -121,6 +128,12 @@ func WithHintCallBack(fn func(string) error) Option {
121128
return func(args *Options) { args.HintCallback = fn }
122129
}
123130

131+
// WithLogger sets the Options.Logger option
132+
func WithLogger(log logr.Logger) Option { return func(args *Options) { args.Logger = log } }
133+
134+
// WithContext sets the Options.Context option
135+
func WithContext(ctx context.Context) Option { return func(args *Options) { args.Context = ctx } }
136+
124137
func swapProbe(order []string, hint string) {
125138
// With so few elements and since `==` uses SIMD,
126139
// looping is faster than having yet another hash map.

discover/probe.go

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package discover
22

33
import (
44
"bytes"
5+
"context"
56
"encoding/xml"
67
"fmt"
78
"io"
@@ -18,7 +19,7 @@ import (
1819
"github.com/bmc-toolbox/bmclib/providers/hp/c7000"
1920
"github.com/bmc-toolbox/bmclib/providers/hp/ilo"
2021
"github.com/bmc-toolbox/bmclib/providers/supermicro/supermicrox"
21-
log "github.com/sirupsen/logrus"
22+
"github.com/go-logr/logr"
2223
)
2324

2425
var (
@@ -34,7 +35,7 @@ type Probe struct {
3435
password string
3536
}
3637

37-
func (p *Probe) hpIlo() (bmcConnection interface{}, err error) {
38+
func (p *Probe) hpIlo(ctx context.Context, log logr.Logger) (bmcConnection interface{}, err error) {
3839

3940
resp, err := p.client.Get(fmt.Sprintf("https://%s/xmldata?item=all", p.host))
4041
if err != nil {
@@ -68,7 +69,8 @@ func (p *Probe) hpIlo() (bmcConnection interface{}, err error) {
6869

6970
if iloXML.HSI != nil {
7071
if strings.HasPrefix(iloXML.MP.Pn, "Integrated Lights-Out") {
71-
return ilo.New(p.host, p.username, p.password)
72+
log.V(1).Info("step", "ScanAndConnect", "host", p.host, "vendor", string(devices.HP), "msg", "it's a HP with iLo")
73+
return ilo.New(ctx, p.host, p.username, p.password, log)
7274
}
7375

7476
return bmcConnection, fmt.Errorf("it's an HP, but I cound't not identify the hardware type. Please verify")
@@ -78,7 +80,7 @@ func (p *Probe) hpIlo() (bmcConnection interface{}, err error) {
7880
return bmcConnection, errors.ErrDeviceNotMatched
7981
}
8082

81-
func (p *Probe) hpC7000() (bmcConnection interface{}, err error) {
83+
func (p *Probe) hpC7000(ctx context.Context, log logr.Logger) (bmcConnection interface{}, err error) {
8284

8385
resp, err := p.client.Get(fmt.Sprintf("https://%s/xmldata?item=all", p.host))
8486
if err != nil {
@@ -106,16 +108,16 @@ func (p *Probe) hpC7000() (bmcConnection interface{}, err error) {
106108
}
107109

108110
if iloXMLC.Infra2 != nil {
109-
log.WithFields(log.Fields{"step": "ScanAndConnect", "host": p.host, "vendor": devices.HP}).Debug("it's a chassis")
110-
return c7000.New(p.host, p.username, p.password)
111+
log.V(1).Info("step", "ScanAndConnect", "host", p.host, "vendor", string(devices.HP), "msg", "it's a chassis")
112+
return c7000.New(ctx, p.host, p.username, p.password, log)
111113
}
112114

113115
}
114116
return bmcConnection, errors.ErrDeviceNotMatched
115117
}
116118

117119
// hpCl100 attempts to identify a cloudline device
118-
func (p *Probe) hpCl100() (bmcConnection interface{}, err error) {
120+
func (p *Probe) hpCl100(ctx context.Context, log logr.Logger) (bmcConnection interface{}, err error) {
119121

120122
// HPE Cloudline CL100
121123
resp, err := p.client.Get(fmt.Sprintf("https://%s/res/ok.png", p.host))
@@ -133,15 +135,15 @@ func (p *Probe) hpCl100() (bmcConnection interface{}, err error) {
133135
}
134136
// ensure the response we got included a png
135137
if resp.StatusCode == 200 && bytes.Contains(firstBytes, []byte("PNG")) {
136-
log.WithFields(log.Fields{"step": "ScanAndConnect", "host": p.host, "vendor": devices.Cloudline}).Debug("it's a discrete")
138+
log.V(1).Info("step", "ScanAndConnect", "host", p.host, "vendor", string(devices.Cloudline), "msg", "it's a discrete")
137139
return bmcConnection, errors.NewErrUnsupportedHardware("hpe cl100 not supported")
138140
}
139141

140142
return bmcConnection, errors.ErrDeviceNotMatched
141143

142144
}
143145

144-
func (p *Probe) idrac8() (bmcConnection interface{}, err error) {
146+
func (p *Probe) idrac8(ctx context.Context, log logr.Logger) (bmcConnection interface{}, err error) {
145147

146148
resp, err := p.client.Get(fmt.Sprintf("https://%s/session?aimGetProp=hostname,gui_str_title_bar,OEMHostName,fwVersion,sysDesc", p.host))
147149
if err != nil {
@@ -157,14 +159,14 @@ func (p *Probe) idrac8() (bmcConnection interface{}, err error) {
157159
}
158160

159161
if resp.StatusCode == 200 && containsAnySubStr(payload, idrac8SysDesc) {
160-
log.WithFields(log.Fields{"step": "connection", "host": p.host, "vendor": devices.Dell}).Debug("it's a idrac8")
161-
return idrac8.New(p.host, p.username, p.password)
162+
log.V(1).Info("step", "connection", "host", p.host, "vendor", string(devices.Dell), "msg", "it's a idrac8")
163+
return idrac8.New(ctx, p.host, p.username, p.password, log)
162164
}
163165

164166
return bmcConnection, errors.ErrDeviceNotMatched
165167
}
166168

167-
func (p *Probe) idrac9() (bmcConnection interface{}, err error) {
169+
func (p *Probe) idrac9(ctx context.Context, log logr.Logger) (bmcConnection interface{}, err error) {
168170

169171
resp, err := p.client.Get(fmt.Sprintf("https://%s/sysmgmt/2015/bmc/info", p.host))
170172
if err != nil {
@@ -180,14 +182,14 @@ func (p *Probe) idrac9() (bmcConnection interface{}, err error) {
180182
}
181183

182184
if resp.StatusCode == 200 && containsAnySubStr(payload, idrac9SysDesc) {
183-
log.WithFields(log.Fields{"step": "connection", "host": p.host, "vendor": devices.Dell}).Debug("it's a idrac9")
184-
return idrac9.New(p.host, p.username, p.password)
185+
log.V(1).Info("step", "connection", "host", p.host, "vendor", string(devices.Dell), "msg", "it's a idrac9")
186+
return idrac9.New(ctx, p.host, p.username, p.password, log)
185187
}
186188

187189
return bmcConnection, errors.ErrDeviceNotMatched
188190
}
189191

190-
func (p *Probe) m1000e() (bmcConnection interface{}, err error) {
192+
func (p *Probe) m1000e(ctx context.Context, log logr.Logger) (bmcConnection interface{}, err error) {
191193
resp, err := p.client.Get(fmt.Sprintf("https://%s/cgi-bin/webcgi/login", p.host))
192194
if err != nil {
193195
return bmcConnection, err
@@ -202,14 +204,14 @@ func (p *Probe) m1000e() (bmcConnection interface{}, err error) {
202204
}
203205

204206
if resp.StatusCode == 200 && containsAnySubStr(payload, m1000eSysDesc) {
205-
log.WithFields(log.Fields{"step": "connection", "host": p.host, "vendor": devices.Dell}).Debug("it's a m1000e chassis")
206-
return m1000e.New(p.host, p.username, p.password)
207+
log.V(1).Info("step", "connection", "host", p.host, "vendor", string(devices.Dell), "msg", "it's a m1000e chassis")
208+
return m1000e.New(ctx, p.host, p.username, p.password, log)
207209
}
208210

209211
return bmcConnection, errors.ErrDeviceNotMatched
210212
}
211213

212-
func (p *Probe) supermicrox() (bmcConnection interface{}, err error) {
214+
func (p *Probe) supermicrox(ctx context.Context, log logr.Logger) (bmcConnection interface{}, err error) {
213215
resp, err := p.client.Get(fmt.Sprintf("https://%s/cgi/login.cgi", p.host))
214216
if err != nil {
215217
return bmcConnection, err
@@ -225,14 +227,14 @@ func (p *Probe) supermicrox() (bmcConnection interface{}, err error) {
225227

226228
// looking for ATEN in the response payload isn't the most ideal way, although it is unique to Supermicros
227229
if resp.StatusCode == 200 && bytes.Contains(payload, []byte("ATEN International")) {
228-
log.WithFields(log.Fields{"step": "connection", "host": p.host, "vendor": devices.Supermicro}).Debug("it's a supermicro")
229-
return supermicrox.New(p.host, p.username, p.password)
230+
log.V(1).Info("step", "connection", "host", p.host, "vendor", string(devices.Supermicro), "msg", "it's a supermicro")
231+
return supermicrox.New(ctx, p.host, p.username, p.password, log)
230232
}
231233

232234
return bmcConnection, errors.ErrDeviceNotMatched
233235
}
234236

235-
func (p *Probe) quanta() (bmcConnection interface{}, err error) {
237+
func (p *Probe) quanta(ctx context.Context, log logr.Logger) (bmcConnection interface{}, err error) {
236238
resp, err := p.client.Get(fmt.Sprintf("https://%s/page/login.html", p.host))
237239
if err != nil {
238240
return bmcConnection, err
@@ -248,7 +250,7 @@ func (p *Probe) quanta() (bmcConnection interface{}, err error) {
248250

249251
// ensure the response we got included a png
250252
if resp.StatusCode == 200 && bytes.Contains(payload, []byte("Quanta")) {
251-
log.WithFields(log.Fields{"step": "ScanAndConnect", "host": p.host, "vendor": devices.Quanta}).Debug("it's a quanta")
253+
log.V(1).Info("step", "ScanAndConnect", "host", p.host, "vendor", string(devices.Quanta), "msg", "it's a quanta")
252254
return bmcConnection, errors.NewErrUnsupportedHardware("quanta hardware not supported")
253255
}
254256

examples/main.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package main
2+
3+
import (
4+
"os"
5+
6+
"github.com/bmc-toolbox/bmclib/devices"
7+
"github.com/bmc-toolbox/bmclib/discover"
8+
"github.com/bombsimon/logrusr"
9+
"github.com/sirupsen/logrus"
10+
)
11+
12+
// bmc lib takes in its opts a logger (https://github.com/go-logr/logr).
13+
// If you do not define one, by default, it uses logrus (https://github.com/go-logr/logr)
14+
// See the logr docs for more details, but the following implementations already exist:
15+
// github.com/google/glog: glogr
16+
// k8s.io/klog: klogr
17+
// go.uber.org/zap: zapr
18+
// log (the Go standard library logger): stdr
19+
// github.com/sirupsen/logrus: logrusr
20+
// github.com/wojas/genericr: genericr
21+
func main() {
22+
23+
ip := "<bmc_ip>"
24+
user := "user"
25+
pass := "password"
26+
27+
logger := logrus.New()
28+
logger.SetLevel(logrus.DebugLevel)
29+
//logger.SetFormatter(&logrus.JSONFormatter{})
30+
31+
logger.Info("printing status with a user defined logger")
32+
conn, err := withUserDefinedLogger(ip, user, pass, logger)
33+
if err != nil {
34+
logger.Fatal(err)
35+
}
36+
printStatus(conn, logger)
37+
38+
logger.Info("printing status with the default builtin logger")
39+
os.Setenv("BMCLIB_LOG_LEVEL", "debug")
40+
conn, err = withDefaultBuiltinLogger(ip, user, pass)
41+
if err != nil {
42+
logger.Fatal(err)
43+
}
44+
printStatus(conn, logger)
45+
}
46+
47+
func withUserDefinedLogger(ip, user, pass string, logger *logrus.Logger) (interface{}, error) {
48+
myLog := logrusr.NewLogger(logger)
49+
opts := func(o *discover.Options) {
50+
o.Logger = myLog
51+
}
52+
53+
return discover.ScanAndConnect(ip, user, pass, opts)
54+
}
55+
56+
func withDefaultBuiltinLogger(ip, user, pass string) (interface{}, error) {
57+
return discover.ScanAndConnect(ip, user, pass)
58+
}
59+
60+
func printStatus(connection interface{}, logger *logrus.Logger) {
61+
switch connection.(type) {
62+
case devices.Bmc:
63+
conn := connection.(devices.Bmc)
64+
defer conn.Close()
65+
66+
sr, err := conn.Serial()
67+
if err != nil {
68+
logger.Fatal(err)
69+
}
70+
logger.WithFields(logrus.Fields{"serial": sr}).Info("serial")
71+
72+
md, err := conn.Model()
73+
if err != nil {
74+
logger.Fatal(err)
75+
}
76+
logger.WithFields(logrus.Fields{"model": md}).Info("model")
77+
78+
mm, err := conn.Memory()
79+
if err != nil {
80+
logger.Fatal(err)
81+
}
82+
logger.WithFields(logrus.Fields{"memory": mm}).Info("memory")
83+
84+
st, err := conn.Status()
85+
if err != nil {
86+
logger.Fatal(err)
87+
}
88+
logger.WithFields(logrus.Fields{"status": st}).Info("status")
89+
90+
hw := conn.HardwareType()
91+
logger.WithFields(logrus.Fields{"hwType": hw}).Info("hwType")
92+
93+
state, err := conn.PowerState()
94+
if err != nil {
95+
logger.Fatal(err)
96+
}
97+
logger.WithFields(logrus.Fields{"state": state}).Info("state")
98+
99+
case devices.Cmc:
100+
cmc := connection.(devices.Cmc)
101+
sts, err := cmc.Status()
102+
if err != nil {
103+
logger.Fatal(err)
104+
}
105+
logger.WithFields(logrus.Fields{"status": sts}).Info("status")
106+
default:
107+
logger.Fatal("Unknown device")
108+
}
109+
}

0 commit comments

Comments
 (0)