master
go 174 lines 4.11 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package nagios
4
5 import (
6 "fmt"
7 "maps"
8 "strings"
9 "time"
10
11 "github.com/netdata/netdata/go/plugins/plugin/framework/vnodes"
12 )
13
14 type macroSet struct {
15 CommandArgs []string
16 Env map[string]string
17 }
18
19 func buildMacroSet(job JobConfig, vnode vnodeInfo, state macroState, now time.Time) macroSet {
20 env := make(map[string]string)
21 set := func(key, value string) {
22 if value != "" {
23 env[key] = value
24 }
25 }
26
27 set("NAGIOS_PLUGIN", job.Plugin)
28 set("NAGIOS_JOB", job.Name)
29 set("NAGIOS_HOSTNAME", firstNonEmpty(job.Vnode, vnode.Hostname))
30 set("NAGIOS_HOSTADDRESS", vnode.Labels["_address"])
31 set("NAGIOS_HOSTALIAS", vnode.Labels["_alias"])
32 set("NAGIOS_SERVICEDESC", job.Name)
33 set("NAGIOS_SERVICESTATE", state.ServiceState)
34 set("NAGIOS_SERVICESTATEID", stateID(state.ServiceState))
35 if state.ServiceAttempt > 0 {
36 set("NAGIOS_SERVICEATTEMPT", fmt.Sprintf("%d", state.ServiceAttempt))
37 }
38 if state.ServiceMaxAttempts > 0 {
39 set("NAGIOS_MAXSERVICEATTEMPTS", fmt.Sprintf("%d", state.ServiceMaxAttempts))
40 }
41 set("NAGIOS_HOSTSTATE", nagiosHostStateUp)
42 set("NAGIOS_HOSTSTATEID", nagiosHostStateUpID)
43 set("NAGIOS_LONGDATETIME", now.Format(time.RFC1123))
44 set("NAGIOS_SHORTDATETIME", now.Format("2006-01-02 15:04"))
45 set("NAGIOS_DATE", now.Format("2006-01-02"))
46 set("NAGIOS_TIME", now.Format("15:04:05"))
47 set("NAGIOS_TIMET", fmt.Sprintf("%d", now.Unix()))
48
49 for k, v := range vnode.Labels {
50 if strings.HasPrefix(k, "_") && k != "_address" && k != "_alias" {
51 env[fmt.Sprintf("NAGIOS__HOST%s", strings.ToUpper(k[1:]))] = v
52 } else if !strings.HasPrefix(k, "_") {
53 env[fmt.Sprintf("NAGIOS__HOSTLABEL_%s", strings.ToUpper(k))] = v
54 }
55 }
56 for k, v := range job.CustomVars {
57 key := fmt.Sprintf("NAGIOS__SERVICE%s", strings.ToUpper(k))
58 env[key] = v
59 }
60 for idx := 0; idx < len(job.ArgValues) && idx < maxArgMacros; idx++ {
61 env[fmt.Sprintf("NAGIOS_ARG%d", idx+1)] = job.ArgValues[idx]
62 }
63
64 cmdArgs := make([]string, len(job.Args))
65 for i, arg := range job.Args {
66 cmdArgs[i] = replaceMacro(arg, env)
67 }
68 return macroSet{CommandArgs: cmdArgs, Env: env}
69 }
70
71 func vnodeInfoFromVirtualNode(vn *vnodes.VirtualNode, fallbackHostname string) vnodeInfo {
72 info := vnodeInfo{
73 Hostname: fallbackHostname,
74 Labels: make(map[string]string),
75 }
76 if vn == nil {
77 return info
78 }
79 if vn.Hostname != "" {
80 info.Hostname = vn.Hostname
81 }
82 maps.Copy(info.Labels, vn.Labels)
83 return info
84 }
85
86 func replaceMacro(value string, env map[string]string) string {
87 return replaceMacroWithStack(value, env, nil)
88 }
89
90 func replaceMacroWithStack(value string, env map[string]string, stack map[string]struct{}) string {
91 if value == "" || len(env) == 0 {
92 return value
93 }
94
95 var b strings.Builder
96 b.Grow(len(value))
97
98 for i := 0; i < len(value); {
99 if value[i] != '$' {
100 b.WriteByte(value[i])
101 i++
102 continue
103 }
104
105 end := strings.IndexByte(value[i+1:], '$')
106 if end < 0 {
107 b.WriteByte(value[i])
108 i++
109 continue
110 }
111
112 token := value[i+1 : i+1+end]
113 if token == "" {
114 b.WriteString("$$")
115 i += 2
116 continue
117 }
118
119 macroKey := "NAGIOS_" + token
120 macroValue, ok := env[macroKey]
121 if !ok {
122 b.WriteString(value[i : i+end+2])
123 i += end + 2
124 continue
125 }
126
127 b.WriteString(resolveMacroValue(macroKey, macroValue, env, stack))
128 i += end + 2
129 }
130
131 return b.String()
132 }
133
134 func resolveMacroValue(key, raw string, env map[string]string, stack map[string]struct{}) string {
135 if stack == nil {
136 stack = make(map[string]struct{})
137 }
138 if _, ok := stack[key]; ok {
139 return macroTokenForKey(key)
140 }
141
142 stack[key] = struct{}{}
143 resolved := replaceMacroWithStack(raw, env, stack)
144 delete(stack, key)
145 return resolved
146 }
147
148 func macroTokenForKey(key string) string {
149 return "$" + strings.TrimPrefix(key, "NAGIOS_") + "$"
150 }
151
152 func firstNonEmpty(values ...string) string {
153 for _, v := range values {
154 if v != "" {
155 return v
156 }
157 }
158 return ""
159 }
160
161 func stateID(state string) string {
162 switch strings.ToUpper(state) {
163 case nagiosStateOK:
164 return nagiosStateIDOK
165 case nagiosStateWarning:
166 return nagiosStateIDWarning
167 case nagiosStateCritical:
168 return nagiosStateIDCritical
169 case nagiosStateUnknown:
170 return nagiosStateIDUnknown
171 default:
172 return nagiosStateIDUnknown
173 }
174 }