master
go 205 lines 6.32 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 //go:build cgo && ibm_mq
4
5 package main
6
7 import (
8 "fmt"
9 "log/slog"
10 "os"
11 "os/user"
12 "path/filepath"
13 "strconv"
14 "strings"
15 "time"
16
17 "github.com/jessevdk/go-flags"
18 "github.com/netdata/netdata/go/plugins/cmd/internal/agenthost"
19 "github.com/netdata/netdata/go/plugins/cmd/internal/discoveryproviders"
20 "github.com/netdata/netdata/go/plugins/plugin/agent"
21 "github.com/netdata/netdata/go/plugins/plugin/agent/discovery"
22 "github.com/netdata/netdata/go/plugins/plugin/agent/policy"
23 "go.uber.org/automaxprocs/maxprocs"
24 "golang.org/x/net/http/httpproxy"
25
26 "github.com/netdata/netdata/go/plugins/logger"
27 "github.com/netdata/netdata/go/plugins/pkg/buildinfo"
28 "github.com/netdata/netdata/go/plugins/pkg/cli"
29 "github.com/netdata/netdata/go/plugins/pkg/executable"
30 "github.com/netdata/netdata/go/plugins/pkg/hostinfo"
31 "github.com/netdata/netdata/go/plugins/pkg/pluginconfig"
32 "github.com/netdata/netdata/go/plugins/pkg/terminal"
33 "github.com/netdata/netdata/go/plugins/plugin/framework/collectorapi"
34 // Register IBM ecosystem collectors
35 _ "github.com/netdata/netdata/go/plugins/plugin/ibm.d/modules/as400" // Requires CGO
36 _ "github.com/netdata/netdata/go/plugins/plugin/ibm.d/modules/db2" // Requires CGO
37 _ "github.com/netdata/netdata/go/plugins/plugin/ibm.d/modules/mq" // MQ monitoring with PCF protocol
38 _ "github.com/netdata/netdata/go/plugins/plugin/ibm.d/modules/websphere/jmx" // Requires CGO
39 _ "github.com/netdata/netdata/go/plugins/plugin/ibm.d/modules/websphere/mp" // Pure Go
40 _ "github.com/netdata/netdata/go/plugins/plugin/ibm.d/modules/websphere/pmi" // Requires CGO
41 )
42
43 const pluginName = "ibm.d.plugin"
44
45 func init() {
46 // https://github.com/netdata/netdata/issues/8949#issuecomment-638294959
47 if v := os.Getenv("TZ"); strings.HasPrefix(v, ":") {
48 _ = os.Unsetenv("TZ")
49 }
50 }
51
52 func main() {
53 _, _ = maxprocs.Set(maxprocs.Logger(func(s string, args ...interface{}) {}))
54
55 opts, err := parseCLI()
56 if err != nil {
57 if cli.IsHelp(err) {
58 os.Exit(0)
59 }
60 os.Exit(1)
61 }
62
63 if opts.Version {
64 fmt.Printf("%s.plugin, version: %s\n", executable.Name, buildinfo.Version)
65 return
66 }
67
68 if opts.MetricsAuditDataDir != "" && opts.MetricsAuditDuration == "" {
69 opts.MetricsAuditDuration = "10m"
70 }
71
72 pluginconfig.MustInit(pluginconfig.InitInput{
73 ConfDir: opts.ConfDir,
74 WatchPath: opts.WatchPath,
75 })
76
77 if lvl := pluginconfig.EnvLogLevel(); lvl != "" {
78 logger.Level.SetByName(lvl)
79 }
80 if opts.Debug {
81 logger.Level.Set(slog.LevelDebug)
82 }
83 isTerminal := terminal.IsTerminal()
84
85 // Parse metrics-audit duration if provided.
86 var auditDuration time.Duration
87 if opts.MetricsAuditDuration != "" {
88 var err error
89 auditDuration, err = time.ParseDuration(opts.MetricsAuditDuration)
90 if err != nil {
91 logger.Errorf("error: invalid --metrics-audit duration '%s': %v", opts.MetricsAuditDuration, err)
92 os.Exit(1)
93 }
94 if auditDuration <= 0 {
95 logger.Errorf("error: invalid --metrics-audit duration '%s': duration must be > 0", opts.MetricsAuditDuration)
96 os.Exit(1)
97 }
98 }
99
100 if opts.MetricsAuditDataDir != "" && auditDuration <= 0 {
101 logger.Errorf("error: --metrics-audit-data requires positive --metrics-audit duration")
102 os.Exit(1)
103 }
104
105 metricsAuditDataDir := ""
106 if opts.MetricsAuditDataDir != "" {
107 var err error
108 metricsAuditDataDir, err = prepareMetricsAuditDataDir(opts.MetricsAuditDataDir, pluginconfig.VarLibDir())
109 if err != nil {
110 logger.Errorf("error preparing --metrics-audit-data directory: %v", err)
111 os.Exit(1)
112 }
113 }
114
115 a := agent.New(agent.Config{
116 Name: executable.Name,
117 PluginConfigDir: pluginconfig.ConfigDir(),
118 CollectorsConfigDir: pluginconfig.CollectorsDir(),
119 VarLibDir: pluginconfig.VarLibDir(),
120 ModuleRegistry: collectorapi.DefaultRegistry,
121 IsInsideK8s: hostinfo.IsInsideK8sCluster(),
122 RunModePolicy: policy.Agent(isTerminal),
123 DiscoveryProviders: []discovery.ProviderFactory{
124 discoveryproviders.File(),
125 discoveryproviders.Dummy(),
126 },
127 RunModule: opts.Module,
128 RunJob: opts.Job,
129 MinUpdateEvery: opts.UpdateEvery,
130 AuditDuration: auditDuration,
131 AuditSummary: opts.MetricsAuditSummary,
132 AuditDataDir: metricsAuditDataDir,
133 DisableServiceDiscovery: true,
134 })
135
136 a.Debugf("plugin: name=%s, %s", a.Name, buildinfo.Info())
137 if u, err := user.Current(); err == nil {
138 a.Debugf("current user: name=%s, uid=%s", u.Username, u.Uid)
139 }
140
141 proxyCfg := httpproxy.FromEnvironment()
142 a.Infof("env HTTP_PROXY '%s', HTTPS_PROXY '%s'", proxyCfg.HTTPProxy, proxyCfg.HTTPSProxy)
143
144 a.Infof("directories → config: %s | collectors: %s | sd: %s | varlib: %s",
145 a.ConfigDir, a.CollectorsConfDir, a.ServiceDiscoveryConfigDir, a.VarLibDir)
146
147 agenthost.Run(a)
148 }
149
150 type options struct {
151 cli.Option
152 MetricsAuditDuration string `long:"metrics-audit" description:"run metrics-audit mode for specified duration (e.g. 30s, 5m) and analyze metric structure"`
153 MetricsAuditSummary bool `long:"metrics-audit-summary" description:"show consolidated metrics-audit summary across all jobs"`
154 MetricsAuditDataDir string `long:"metrics-audit-data" description:"write structured metrics-audit artifacts for the selected module to the given directory"`
155 }
156
157 func parseCLI() (*options, error) {
158 opt := &options{
159 Option: cli.Option{
160 UpdateEvery: 1,
161 },
162 }
163
164 parser := flags.NewParser(opt, flags.Default)
165 parser.Name = executable.Name
166 parser.Usage = "[OPTIONS] [update every]"
167
168 rest, err := parser.ParseArgs(os.Args)
169 if err != nil {
170 return nil, err
171 }
172
173 if len(rest) > 1 {
174 if opt.UpdateEvery, err = strconv.Atoi(rest[1]); err != nil {
175 return nil, err
176 }
177 }
178
179 return opt, nil
180 }
181
182 func prepareMetricsAuditDataDir(path string, varLibDir string) (string, error) {
183 if path == "" {
184 return "", nil
185 }
186 clean := strings.TrimSpace(path)
187 if !filepath.IsAbs(clean) && varLibDir != "" {
188 clean = filepath.Join(varLibDir, clean)
189 }
190 absolute, err := filepath.Abs(clean)
191 if err != nil {
192 return "", err
193 }
194 if absolute == "" || absolute == "/" {
195 return "", fmt.Errorf("refusing to use unsafe metrics-audit-data directory '%s'", absolute)
196 }
197 if err := os.MkdirAll(absolute, 0o755); err != nil {
198 return "", err
199 }
200 runDir := filepath.Join(absolute, fmt.Sprintf("run-%s", time.Now().UTC().Format("20060102-150405.000000000")))
201 if err := os.MkdirAll(runDir, 0o755); err != nil {
202 return "", err
203 }
204 return runDir, nil
205 }