Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / github.com / prometheus / procfs / ipvs.go
1 // Copyright 2018 The Prometheus Authors
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 package procfs
15
16 import (
17         "bufio"
18         "encoding/hex"
19         "errors"
20         "fmt"
21         "io"
22         "io/ioutil"
23         "net"
24         "os"
25         "strconv"
26         "strings"
27 )
28
29 // IPVSStats holds IPVS statistics, as exposed by the kernel in `/proc/net/ip_vs_stats`.
30 type IPVSStats struct {
31         // Total count of connections.
32         Connections uint64
33         // Total incoming packages processed.
34         IncomingPackets uint64
35         // Total outgoing packages processed.
36         OutgoingPackets uint64
37         // Total incoming traffic.
38         IncomingBytes uint64
39         // Total outgoing traffic.
40         OutgoingBytes uint64
41 }
42
43 // IPVSBackendStatus holds current metrics of one virtual / real address pair.
44 type IPVSBackendStatus struct {
45         // The local (virtual) IP address.
46         LocalAddress net.IP
47         // The remote (real) IP address.
48         RemoteAddress net.IP
49         // The local (virtual) port.
50         LocalPort uint16
51         // The remote (real) port.
52         RemotePort uint16
53         // The local firewall mark
54         LocalMark string
55         // The transport protocol (TCP, UDP).
56         Proto string
57         // The current number of active connections for this virtual/real address pair.
58         ActiveConn uint64
59         // The current number of inactive connections for this virtual/real address pair.
60         InactConn uint64
61         // The current weight of this virtual/real address pair.
62         Weight uint64
63 }
64
65 // NewIPVSStats reads the IPVS statistics.
66 func NewIPVSStats() (IPVSStats, error) {
67         fs, err := NewFS(DefaultMountPoint)
68         if err != nil {
69                 return IPVSStats{}, err
70         }
71
72         return fs.NewIPVSStats()
73 }
74
75 // NewIPVSStats reads the IPVS statistics from the specified `proc` filesystem.
76 func (fs FS) NewIPVSStats() (IPVSStats, error) {
77         file, err := os.Open(fs.Path("net/ip_vs_stats"))
78         if err != nil {
79                 return IPVSStats{}, err
80         }
81         defer file.Close()
82
83         return parseIPVSStats(file)
84 }
85
86 // parseIPVSStats performs the actual parsing of `ip_vs_stats`.
87 func parseIPVSStats(file io.Reader) (IPVSStats, error) {
88         var (
89                 statContent []byte
90                 statLines   []string
91                 statFields  []string
92                 stats       IPVSStats
93         )
94
95         statContent, err := ioutil.ReadAll(file)
96         if err != nil {
97                 return IPVSStats{}, err
98         }
99
100         statLines = strings.SplitN(string(statContent), "\n", 4)
101         if len(statLines) != 4 {
102                 return IPVSStats{}, errors.New("ip_vs_stats corrupt: too short")
103         }
104
105         statFields = strings.Fields(statLines[2])
106         if len(statFields) != 5 {
107                 return IPVSStats{}, errors.New("ip_vs_stats corrupt: unexpected number of fields")
108         }
109
110         stats.Connections, err = strconv.ParseUint(statFields[0], 16, 64)
111         if err != nil {
112                 return IPVSStats{}, err
113         }
114         stats.IncomingPackets, err = strconv.ParseUint(statFields[1], 16, 64)
115         if err != nil {
116                 return IPVSStats{}, err
117         }
118         stats.OutgoingPackets, err = strconv.ParseUint(statFields[2], 16, 64)
119         if err != nil {
120                 return IPVSStats{}, err
121         }
122         stats.IncomingBytes, err = strconv.ParseUint(statFields[3], 16, 64)
123         if err != nil {
124                 return IPVSStats{}, err
125         }
126         stats.OutgoingBytes, err = strconv.ParseUint(statFields[4], 16, 64)
127         if err != nil {
128                 return IPVSStats{}, err
129         }
130
131         return stats, nil
132 }
133
134 // NewIPVSBackendStatus reads and returns the status of all (virtual,real) server pairs.
135 func NewIPVSBackendStatus() ([]IPVSBackendStatus, error) {
136         fs, err := NewFS(DefaultMountPoint)
137         if err != nil {
138                 return []IPVSBackendStatus{}, err
139         }
140
141         return fs.NewIPVSBackendStatus()
142 }
143
144 // NewIPVSBackendStatus reads and returns the status of all (virtual,real) server pairs from the specified `proc` filesystem.
145 func (fs FS) NewIPVSBackendStatus() ([]IPVSBackendStatus, error) {
146         file, err := os.Open(fs.Path("net/ip_vs"))
147         if err != nil {
148                 return nil, err
149         }
150         defer file.Close()
151
152         return parseIPVSBackendStatus(file)
153 }
154
155 func parseIPVSBackendStatus(file io.Reader) ([]IPVSBackendStatus, error) {
156         var (
157                 status       []IPVSBackendStatus
158                 scanner      = bufio.NewScanner(file)
159                 proto        string
160                 localMark    string
161                 localAddress net.IP
162                 localPort    uint16
163                 err          error
164         )
165
166         for scanner.Scan() {
167                 fields := strings.Fields(scanner.Text())
168                 if len(fields) == 0 {
169                         continue
170                 }
171                 switch {
172                 case fields[0] == "IP" || fields[0] == "Prot" || fields[1] == "RemoteAddress:Port":
173                         continue
174                 case fields[0] == "TCP" || fields[0] == "UDP":
175                         if len(fields) < 2 {
176                                 continue
177                         }
178                         proto = fields[0]
179                         localMark = ""
180                         localAddress, localPort, err = parseIPPort(fields[1])
181                         if err != nil {
182                                 return nil, err
183                         }
184                 case fields[0] == "FWM":
185                         if len(fields) < 2 {
186                                 continue
187                         }
188                         proto = fields[0]
189                         localMark = fields[1]
190                         localAddress = nil
191                         localPort = 0
192                 case fields[0] == "->":
193                         if len(fields) < 6 {
194                                 continue
195                         }
196                         remoteAddress, remotePort, err := parseIPPort(fields[1])
197                         if err != nil {
198                                 return nil, err
199                         }
200                         weight, err := strconv.ParseUint(fields[3], 10, 64)
201                         if err != nil {
202                                 return nil, err
203                         }
204                         activeConn, err := strconv.ParseUint(fields[4], 10, 64)
205                         if err != nil {
206                                 return nil, err
207                         }
208                         inactConn, err := strconv.ParseUint(fields[5], 10, 64)
209                         if err != nil {
210                                 return nil, err
211                         }
212                         status = append(status, IPVSBackendStatus{
213                                 LocalAddress:  localAddress,
214                                 LocalPort:     localPort,
215                                 LocalMark:     localMark,
216                                 RemoteAddress: remoteAddress,
217                                 RemotePort:    remotePort,
218                                 Proto:         proto,
219                                 Weight:        weight,
220                                 ActiveConn:    activeConn,
221                                 InactConn:     inactConn,
222                         })
223                 }
224         }
225         return status, nil
226 }
227
228 func parseIPPort(s string) (net.IP, uint16, error) {
229         var (
230                 ip  net.IP
231                 err error
232         )
233
234         switch len(s) {
235         case 13:
236                 ip, err = hex.DecodeString(s[0:8])
237                 if err != nil {
238                         return nil, 0, err
239                 }
240         case 46:
241                 ip = net.ParseIP(s[1:40])
242                 if ip == nil {
243                         return nil, 0, fmt.Errorf("invalid IPv6 address: %s", s[1:40])
244                 }
245         default:
246                 return nil, 0, fmt.Errorf("unexpected IP:Port: %s", s)
247         }
248
249         portString := s[len(s)-4:]
250         if len(portString) != 4 {
251                 return nil, 0, fmt.Errorf("unexpected port string format: %s", portString)
252         }
253         port, err := strconv.ParseUint(portString, 16, 16)
254         if err != nil {
255                 return nil, 0, err
256         }
257
258         return ip, uint16(port), nil
259 }