-
Notifications
You must be signed in to change notification settings - Fork 16
/
mysql.go
160 lines (131 loc) · 4.41 KB
/
mysql.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
package mysql
import (
"database/sql"
"fmt"
"net"
"os"
"path/filepath"
"strings"
"time"
"github.com/sirupsen/logrus"
"github.com/cloudradar-monitoring/cagent/pkg/monitoring"
)
const (
defaultPort = "3306"
)
var log = logrus.WithField("package", "mysql")
type Config struct {
Enabled bool `toml:"enabled" comment:"Set 'false' to disable checking available updates"`
Connect string `toml:"connect" comment:"Use 127.0.0.1 or the path to the mysql.socket, connecting to remote databases is not supported"`
User string `toml:"user" comment:"Create a user with minimal rights\nmysql > GRANT USAGE ON *.* TO cagent@localhost IDENTIFIED BY '<password>';"`
Password string `toml:"password" comment:"Maximum time the package manager is allowed to spend fetching available updates"`
ConnectTimeout float64 `toml:"connect_timeout" comment:"Maximum time to wait for mysql server to connect using provided credentials"`
}
func (cfg *Config) Validate() error {
if cfg.ConnectTimeout < 0 {
return fmt.Errorf("connect_timeout should be equal or greater than 0.0")
}
return nil
}
func CreateModule(config *Config) monitoring.Module {
return &Mysql{
config: config,
}
}
type Mysql struct {
config *Config
client *sql.DB
lastStatus *Status
lastStatusTime time.Time
}
func (r *Mysql) getClient() (*sql.DB, error) {
if r.client != nil {
return r.client, nil
}
if len(r.config.Connect) == 0 {
return nil, fmt.Errorf("connect address is empty")
}
if len(r.config.User) == 0 {
return nil, fmt.Errorf("user is empty")
}
var host, port string
portDelimiter := strings.Index(r.config.Connect, ":")
if portDelimiter > 0 {
if strings.Count(r.config.Connect, ":") > 1 {
// probably ipv6
return nil, fmt.Errorf("connect: IPv6 address not supported")
}
host = r.config.Connect[0:portDelimiter]
if portDelimiter+1 < len(r.config.Connect) {
port = r.config.Connect[portDelimiter+1:]
} else {
port = defaultPort
}
} else {
host = r.config.Connect
port = defaultPort
}
if host == "127.0.0.1" || host == "localhost" {
db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%s)/information_schema?timeout=%.2fs", r.config.User, r.config.Password, host, port, r.config.ConnectTimeout))
if err != nil {
return nil, fmt.Errorf("failed to connect %s: %s", r.config.Connect, err.Error())
}
r.client = db
return db, nil
}
ip := net.ParseIP(host)
if ip != nil {
return nil, fmt.Errorf("connect: only local mysql server is supported. Please use 127.0.0.1, localhost or unix socket path")
}
// this is probably unix socket filepath
absPath, err := filepath.Abs(r.config.Connect)
if err != nil {
return nil, fmt.Errorf("connect: not a unix socket path: %s", err.Error())
}
if _, err := os.Stat(absPath); err == os.ErrNotExist {
return nil, fmt.Errorf("connect: provided unix socket path not found")
} else if err != nil {
return nil, fmt.Errorf("connect: provided unix socket not valid, %s", err.Error())
}
db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@unix(%s)/information_schema?timeout=%.2fs", r.config.User, r.config.Password, absPath, r.config.ConnectTimeout))
if err != nil {
return nil, fmt.Errorf("failed to connect %s: %s", r.config.Connect, err.Error())
}
r.client = db
return db, nil
}
func (r *Mysql) GetDescription() string {
return fmt.Sprintf("MySQL/MariaDB performance for %s", r.config.Connect)
}
func (r *Mysql) IsEnabled() bool {
return r.config.Enabled
}
func (r *Mysql) Run() ([]*monitoring.ModuleReport, error) {
report := monitoring.NewReport(
fmt.Sprintf("MySQL/MariaDB performance metrics for %s", r.config.Connect),
time.Now(),
"",
)
client, err := r.getClient()
if err != nil {
report.AddAlert(err.Error())
return []*monitoring.ModuleReport{&report}, nil
}
statusTime := time.Now()
status, err := getStatus(client)
if err != nil {
report.AddAlert(fmt.Sprintf("failed to get status: %s", err.Error()))
return []*monitoring.ModuleReport{&report}, nil
}
if r.lastStatus == nil {
r.lastStatus = status
r.lastStatusTime = statusTime
// need one more iteration to calculate
// but we need to provide nil measurements for consistency
report.Measurements = emptyResults()
return []*monitoring.ModuleReport{&report}, nil
}
report.Measurements = make(map[string]interface{})
fillResultsPerSecond(status, r.lastStatus, statusTime.Sub(r.lastStatusTime), report.Measurements)
return []*monitoring.ModuleReport{&report}, nil
}