mirror of
https://github.com/mainflux/mainflux.git
synced 2025-04-29 13:49:28 +08:00

* Use normalizer as stream source Renamed 'writer' service to 'normalizer' and dropped Cassandra facilities from it. Extracted the common dependencies to 'mainflux' package for easier sharing. Fixed the API docs and unified environment variables. Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Use docker build arguments to specify build Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Remove cassandra libraries Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Update go-kit version to 0.6.0 Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Fix manager configuration Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Refactor docker-compose Merged individual compose files and dropped external links. Remove CoAP container since it is not referenced from NginX config at the moment. Update port mapping in compose and nginx.conf. Dropped bin scripts. Updated service documentation. Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Drop content-type check Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Implement users data access layer in PostgreSQL Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Bump version to 0.1.0 Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Use go-kit logger everywhere (except CoAP) Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Improve factory methods naming Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Implement clients data access layer on PostgreSQL Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Make tests stateless All tests are refactored to use map-based table-driven tests. No cross-tests dependencies is present anymore. Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Remove gitignore Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Fix nginx proxying Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Mark client-user FK explicit Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Update API documentation Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Update channel model Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Add channel PostgreSQL repository tests Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Implement PostgreSQL channels DAO Replaced update queries with raw SQL. Explicitly defined M2M table due to difficulties of ensuring the referential integrity through GORM. Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Expose connection endpoints Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Fix swagger docs and remove DB logging Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Fix nested query remarks Signed-off-by: Dejan Mijic <dejan@mainflux.com> * Add unique indices Signed-off-by: Dejan Mijic <dejan@mainflux.com>
247 lines
5.7 KiB
Go
247 lines
5.7 KiB
Go
package procfs
|
|
|
|
import (
|
|
"bufio"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"net"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// IPVSStats holds IPVS statistics, as exposed by the kernel in `/proc/net/ip_vs_stats`.
|
|
type IPVSStats struct {
|
|
// Total count of connections.
|
|
Connections uint64
|
|
// Total incoming packages processed.
|
|
IncomingPackets uint64
|
|
// Total outgoing packages processed.
|
|
OutgoingPackets uint64
|
|
// Total incoming traffic.
|
|
IncomingBytes uint64
|
|
// Total outgoing traffic.
|
|
OutgoingBytes uint64
|
|
}
|
|
|
|
// IPVSBackendStatus holds current metrics of one virtual / real address pair.
|
|
type IPVSBackendStatus struct {
|
|
// The local (virtual) IP address.
|
|
LocalAddress net.IP
|
|
// The remote (real) IP address.
|
|
RemoteAddress net.IP
|
|
// The local (virtual) port.
|
|
LocalPort uint16
|
|
// The remote (real) port.
|
|
RemotePort uint16
|
|
// The local firewall mark
|
|
LocalMark string
|
|
// The transport protocol (TCP, UDP).
|
|
Proto string
|
|
// The current number of active connections for this virtual/real address pair.
|
|
ActiveConn uint64
|
|
// The current number of inactive connections for this virtual/real address pair.
|
|
InactConn uint64
|
|
// The current weight of this virtual/real address pair.
|
|
Weight uint64
|
|
}
|
|
|
|
// NewIPVSStats reads the IPVS statistics.
|
|
func NewIPVSStats() (IPVSStats, error) {
|
|
fs, err := NewFS(DefaultMountPoint)
|
|
if err != nil {
|
|
return IPVSStats{}, err
|
|
}
|
|
|
|
return fs.NewIPVSStats()
|
|
}
|
|
|
|
// NewIPVSStats reads the IPVS statistics from the specified `proc` filesystem.
|
|
func (fs FS) NewIPVSStats() (IPVSStats, error) {
|
|
file, err := os.Open(fs.Path("net/ip_vs_stats"))
|
|
if err != nil {
|
|
return IPVSStats{}, err
|
|
}
|
|
defer file.Close()
|
|
|
|
return parseIPVSStats(file)
|
|
}
|
|
|
|
// parseIPVSStats performs the actual parsing of `ip_vs_stats`.
|
|
func parseIPVSStats(file io.Reader) (IPVSStats, error) {
|
|
var (
|
|
statContent []byte
|
|
statLines []string
|
|
statFields []string
|
|
stats IPVSStats
|
|
)
|
|
|
|
statContent, err := ioutil.ReadAll(file)
|
|
if err != nil {
|
|
return IPVSStats{}, err
|
|
}
|
|
|
|
statLines = strings.SplitN(string(statContent), "\n", 4)
|
|
if len(statLines) != 4 {
|
|
return IPVSStats{}, errors.New("ip_vs_stats corrupt: too short")
|
|
}
|
|
|
|
statFields = strings.Fields(statLines[2])
|
|
if len(statFields) != 5 {
|
|
return IPVSStats{}, errors.New("ip_vs_stats corrupt: unexpected number of fields")
|
|
}
|
|
|
|
stats.Connections, err = strconv.ParseUint(statFields[0], 16, 64)
|
|
if err != nil {
|
|
return IPVSStats{}, err
|
|
}
|
|
stats.IncomingPackets, err = strconv.ParseUint(statFields[1], 16, 64)
|
|
if err != nil {
|
|
return IPVSStats{}, err
|
|
}
|
|
stats.OutgoingPackets, err = strconv.ParseUint(statFields[2], 16, 64)
|
|
if err != nil {
|
|
return IPVSStats{}, err
|
|
}
|
|
stats.IncomingBytes, err = strconv.ParseUint(statFields[3], 16, 64)
|
|
if err != nil {
|
|
return IPVSStats{}, err
|
|
}
|
|
stats.OutgoingBytes, err = strconv.ParseUint(statFields[4], 16, 64)
|
|
if err != nil {
|
|
return IPVSStats{}, err
|
|
}
|
|
|
|
return stats, nil
|
|
}
|
|
|
|
// NewIPVSBackendStatus reads and returns the status of all (virtual,real) server pairs.
|
|
func NewIPVSBackendStatus() ([]IPVSBackendStatus, error) {
|
|
fs, err := NewFS(DefaultMountPoint)
|
|
if err != nil {
|
|
return []IPVSBackendStatus{}, err
|
|
}
|
|
|
|
return fs.NewIPVSBackendStatus()
|
|
}
|
|
|
|
// NewIPVSBackendStatus reads and returns the status of all (virtual,real) server pairs from the specified `proc` filesystem.
|
|
func (fs FS) NewIPVSBackendStatus() ([]IPVSBackendStatus, error) {
|
|
file, err := os.Open(fs.Path("net/ip_vs"))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer file.Close()
|
|
|
|
return parseIPVSBackendStatus(file)
|
|
}
|
|
|
|
func parseIPVSBackendStatus(file io.Reader) ([]IPVSBackendStatus, error) {
|
|
var (
|
|
status []IPVSBackendStatus
|
|
scanner = bufio.NewScanner(file)
|
|
proto string
|
|
localMark string
|
|
localAddress net.IP
|
|
localPort uint16
|
|
err error
|
|
)
|
|
|
|
for scanner.Scan() {
|
|
fields := strings.Fields(scanner.Text())
|
|
if len(fields) == 0 {
|
|
continue
|
|
}
|
|
switch {
|
|
case fields[0] == "IP" || fields[0] == "Prot" || fields[1] == "RemoteAddress:Port":
|
|
continue
|
|
case fields[0] == "TCP" || fields[0] == "UDP":
|
|
if len(fields) < 2 {
|
|
continue
|
|
}
|
|
proto = fields[0]
|
|
localMark = ""
|
|
localAddress, localPort, err = parseIPPort(fields[1])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
case fields[0] == "FWM":
|
|
if len(fields) < 2 {
|
|
continue
|
|
}
|
|
proto = fields[0]
|
|
localMark = fields[1]
|
|
localAddress = nil
|
|
localPort = 0
|
|
case fields[0] == "->":
|
|
if len(fields) < 6 {
|
|
continue
|
|
}
|
|
remoteAddress, remotePort, err := parseIPPort(fields[1])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
weight, err := strconv.ParseUint(fields[3], 10, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
activeConn, err := strconv.ParseUint(fields[4], 10, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
inactConn, err := strconv.ParseUint(fields[5], 10, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
status = append(status, IPVSBackendStatus{
|
|
LocalAddress: localAddress,
|
|
LocalPort: localPort,
|
|
LocalMark: localMark,
|
|
RemoteAddress: remoteAddress,
|
|
RemotePort: remotePort,
|
|
Proto: proto,
|
|
Weight: weight,
|
|
ActiveConn: activeConn,
|
|
InactConn: inactConn,
|
|
})
|
|
}
|
|
}
|
|
return status, nil
|
|
}
|
|
|
|
func parseIPPort(s string) (net.IP, uint16, error) {
|
|
var (
|
|
ip net.IP
|
|
err error
|
|
)
|
|
|
|
switch len(s) {
|
|
case 13:
|
|
ip, err = hex.DecodeString(s[0:8])
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
case 46:
|
|
ip = net.ParseIP(s[1:40])
|
|
if ip == nil {
|
|
return nil, 0, fmt.Errorf("invalid IPv6 address: %s", s[1:40])
|
|
}
|
|
default:
|
|
return nil, 0, fmt.Errorf("unexpected IP:Port: %s", s)
|
|
}
|
|
|
|
portString := s[len(s)-4:]
|
|
if len(portString) != 4 {
|
|
return nil, 0, fmt.Errorf("unexpected port string format: %s", portString)
|
|
}
|
|
port, err := strconv.ParseUint(portString, 16, 16)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
return ip, uint16(port), nil
|
|
}
|