-
Notifications
You must be signed in to change notification settings - Fork 37
/
asrockrack.go
183 lines (155 loc) · 4.67 KB
/
asrockrack.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
package asrockrack
import (
"context"
"crypto/x509"
"fmt"
"net/http"
"strings"
"github.com/bmc-toolbox/bmclib/v2/constants"
"github.com/bmc-toolbox/bmclib/v2/internal/httpclient"
"github.com/bmc-toolbox/bmclib/v2/providers"
"github.com/bmc-toolbox/common"
"github.com/go-logr/logr"
"github.com/jacobweinstock/registrar"
"github.com/pkg/errors"
)
const (
// ProviderName for the provider implementation
ProviderName = "asrockrack"
// ProviderProtocol for the provider implementation
ProviderProtocol = "vendorapi"
E3C256D4ID_NL = "E3C256D4ID-NL"
E3C246D4ID_NL = "E3C246D4ID-NL"
E3C246D4I_NL = "E3C246D4I-NL"
)
var (
// Features implemented by asrockrack https
Features = registrar.Features{
providers.FeaturePostCodeRead,
providers.FeatureBmcReset,
providers.FeatureUserCreate,
providers.FeatureUserUpdate,
providers.FeatureFirmwareUpload,
providers.FeatureFirmwareInstallUploaded,
providers.FeatureFirmwareTaskStatus,
providers.FeatureFirmwareInstallSteps,
providers.FeatureInventoryRead,
providers.FeaturePowerSet,
providers.FeaturePowerState,
}
)
// ASRockRack holds the status and properties of a connection to a asrockrack bmc
type ASRockRack struct {
ip string
username string
password string
deviceModel string
loginSession *loginSession
httpClient *http.Client
resetRequired bool // Indicates if the BMC requires a reset
skipLogout bool // A Close() / httpsLogout() request is ignored if the BMC was just flashed - since the sessions are terminated either way
log logr.Logger
httpClientSetupFuncs []func(*http.Client)
}
type Config struct {
Port string
HttpClient *http.Client
}
// ASRockOption is a type that can configure an *ASRockRack
type ASRockOption func(*ASRockRack)
// WithSecureTLS enforces trusted TLS connections, with an optional CA certificate pool.
// Using this option with an nil pool uses the system CAs.
func WithSecureTLS(rootCAs *x509.CertPool) ASRockOption {
return func(r *ASRockRack) {
r.httpClientSetupFuncs = append(r.httpClientSetupFuncs, httpclient.SecureTLSOption(rootCAs))
}
}
// WithHTTPClient sets an HTTP client on the ASRockRack
func WithHTTPClient(c *http.Client) ASRockOption {
return func(ar *ASRockRack) {
ar.httpClient = c
}
}
// New returns a new ASRockRack instance ready to be used
func New(ip string, username string, password string, log logr.Logger) *ASRockRack {
return NewWithOptions(ip, username, password, log)
}
// NewWithOptions returns a new ASRockRack instance with options ready to be used
func NewWithOptions(ip string, username string, password string, log logr.Logger, opts ...ASRockOption) *ASRockRack {
r := &ASRockRack{
ip: ip,
username: username,
password: password,
log: log,
loginSession: &loginSession{},
}
for _, opt := range opts {
opt(r)
}
if r.httpClient == nil {
r.httpClient = httpclient.Build(r.httpClientSetupFuncs...)
} else {
for _, setupFunc := range r.httpClientSetupFuncs {
setupFunc(r.httpClient)
}
}
return r
}
func (a *ASRockRack) Name() string {
return ProviderName
}
// Open a connection to a BMC, implements the Opener interface
func (a *ASRockRack) Open(ctx context.Context) (err error) {
if err := a.httpsLogin(ctx); err != nil {
return err
}
return a.supported(ctx)
}
func (a *ASRockRack) supported(ctx context.Context) error {
supported := []string{
E3C256D4ID_NL,
E3C246D4ID_NL,
E3C246D4I_NL,
}
if a.deviceModel == "" {
device := common.NewDevice()
device.Metadata = map[string]string{}
err := a.fruAttributes(ctx, &device)
if err != nil {
return errors.Wrap(err, "failed to identify device model")
}
if device.Model == "" {
return errors.Wrap(err, "failed to identify device model - empty model attribute")
}
a.deviceModel = device.Model
}
for _, s := range supported {
if strings.EqualFold(a.deviceModel, s) {
return nil
}
}
return fmt.Errorf("device model not supported: %s", a.deviceModel)
}
// Close a connection to a BMC, implements the Closer interface
func (a *ASRockRack) Close(ctx context.Context) (err error) {
if a.skipLogout {
return nil
}
return a.httpsLogout(ctx)
}
// CheckCredentials verify whether the credentials are valid or not
func (a *ASRockRack) CheckCredentials(ctx context.Context) (err error) {
return a.httpsLogin(ctx)
}
func (a *ASRockRack) PostCode(ctx context.Context) (status string, code int, err error) {
postInfo, err := a.postCodeInfo(ctx)
if err != nil {
return status, code, err
}
code = postInfo.PostData
status, exists := knownPOSTCodes[code]
if !exists {
status = constants.POSTCodeUnknown
}
return status, code, nil
}