This repository has been archived by the owner on Mar 6, 2023. It is now read-only.
forked from NetApp/trident
/
api.go
181 lines (156 loc) · 4.72 KB
/
api.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
// Copyright 2018 NetApp, Inc. All Rights Reserved.
package api
import (
"bytes"
"crypto/tls"
"encoding/json"
"errors"
"io/ioutil"
"math/rand"
"net/http"
"strings"
"time"
log "github.com/sirupsen/logrus"
tridentconfig "github.com/netapp/trident/config"
"github.com/netapp/trident/utils"
)
const httpContentType = "json-rpc"
// Client is used to send API requests to a SolidFire system system
type Client struct {
SVIP string
Endpoint string
Config *Config
DefaultAPIPort int
VolumeTypes *[]VolType
AccessGroups []int64
DefaultBlockSize int64
DebugTraceFlags map[string]bool
AccountID int64
}
// Config holds the configuration data for the Client to communicate with a SolidFire storage system
type Config struct {
TenantName string
EndPoint string
MountPoint string
SVIP string
InitiatorIFace string //iface to use of iSCSI initiator
Types *[]VolType
LegacyNamePrefix string
AccessGroups []int64
DefaultBlockSize int64
DebugTraceFlags map[string]bool
}
// VolType holds quality of service configuration data
type VolType struct {
Type string
QOS QoS
}
// NewFromParameters is a factory method to create a new sfapi.Client object using the supplied parameters
func NewFromParameters(pendpoint string, psvip string, pcfg Config) (c *Client, err error) {
rand.Seed(time.Now().UTC().UnixNano())
SFClient := &Client{
Endpoint: pendpoint,
SVIP: psvip,
Config: &pcfg,
DefaultAPIPort: 443,
VolumeTypes: pcfg.Types,
DefaultBlockSize: pcfg.DefaultBlockSize,
DebugTraceFlags: pcfg.DebugTraceFlags,
}
return SFClient, nil
}
// Request performs a json-rpc POST to the configured endpoint
func (c *Client) Request(method string, params interface{}, id int) ([]byte, error) {
var err error
var request *http.Request
var response *http.Response
var prettyRequestBuffer bytes.Buffer
var prettyResponseBuffer bytes.Buffer
if c.Endpoint == "" {
log.Error("endpoint is not set, unable to issue json-rpc requests")
err = errors.New("no endpoint set")
return nil, err
}
requestBody, err := json.Marshal(map[string]interface{}{
"method": method,
"id": id,
"params": params,
})
// Create the request
request, err = http.NewRequest("POST", c.Endpoint, strings.NewReader(string(requestBody)))
if err != nil {
return nil, err
}
request.Header.Set("Content-Type", httpContentType)
// Log the request
if c.Config.DebugTraceFlags["api"] {
json.Indent(&prettyRequestBuffer, requestBody, "", " ")
utils.LogHTTPRequest(request, prettyRequestBuffer.Bytes())
}
// Send the request
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
httpClient := &http.Client{
Transport: tr,
Timeout: time.Duration(tridentconfig.StorageAPITimeoutSeconds * time.Second),
}
response, err = httpClient.Do(request)
if err != nil {
log.Errorf("Error response from SolidFire API request: %v", err)
return nil, errors.New("device API error")
}
// Handle HTTP errors such as 401 (Unauthorized)
httpError := utils.NewHTTPError(response)
if httpError != nil {
log.WithFields(log.Fields{
"request": method,
"responseCode": response.StatusCode,
"responseStatus": response.Status,
}).Errorf("API request failed.")
return nil, *httpError
}
defer response.Body.Close()
responseBody, err := ioutil.ReadAll(response.Body)
if err != nil {
return responseBody, err
}
// Log the response
if c.Config.DebugTraceFlags["api"] {
if c.shouldLogResponseBody(method) {
json.Indent(&prettyResponseBuffer, responseBody, "", " ")
utils.LogHTTPResponse(response, prettyResponseBuffer.Bytes())
} else {
utils.LogHTTPResponse(response, []byte("<suppressed>"))
}
}
// Look for any errors returned from the controller
apiError := Error{}
json.Unmarshal([]byte(responseBody), &apiError)
if apiError.Fields.Code != 0 {
log.WithFields(log.Fields{
"ID": apiError.ID,
"code": apiError.Fields.Code,
"message": apiError.Fields.Message,
"name": apiError.Fields.Name,
}).Error("Error detected in API response.")
return nil, apiError
}
return responseBody, nil
}
// shouldLogResponseBody prevents logging the REST response body for APIs that are
// extremely lengthy for no good reason or that return sensitive data like iSCSI secrets.
func (c *Client) shouldLogResponseBody(method string) bool {
switch method {
case "GetAccountByName", "GetAccountByID", "ListAccounts":
return c.Config.DebugTraceFlags["sensitive"]
case "GetClusterHardwareInfo":
return c.Config.DebugTraceFlags["hardwareInfo"]
default:
return true
}
}
// NewReqID generates a random id for a request
func NewReqID() int {
return rand.Intn(1000-1) + 1
}