A simple monitoring solution written in Go (work in progress)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

326 lines
5.8 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. package config
  2. import (
  3. "alrm/alarm"
  4. "alrm/check"
  5. "bufio"
  6. "fmt"
  7. "os"
  8. "strings"
  9. )
  10. const (
  11. TK_NONE = iota
  12. TK_SET
  13. TK_MONITOR
  14. TK_GROUP
  15. TK_HOST
  16. TK_CHECK
  17. TK_ALARM
  18. )
  19. type Parser struct {
  20. DebugLevel int
  21. Line int
  22. states []int
  23. lastHost *Host
  24. lastGroup *Group
  25. lastCheck check.Check
  26. lastAlarm alarm.Alarm
  27. lastAlarmName string
  28. }
  29. func (p *Parser) Parse(fn string) (*Config, error) {
  30. file, err := os.Open(fn)
  31. if err != nil {
  32. return nil, fmt.Errorf("cannot open config \"%s\": %s", fn, err.Error())
  33. }
  34. defer file.Close()
  35. config := NewConfig()
  36. scan := bufio.NewScanner(file)
  37. scan.Split(p.Split)
  38. for scan.Scan() {
  39. tk := scan.Text()
  40. stateswitch:
  41. switch p.state() {
  42. case TK_NONE:
  43. switch strings.ToLower(tk) {
  44. case "monitor":
  45. p.setState(TK_MONITOR)
  46. case "set":
  47. p.setState(TK_SET)
  48. case "alarm":
  49. p.setState(TK_ALARM)
  50. default:
  51. return nil, fmt.Errorf("invalid token in %s, line %d: \"%s\"",
  52. fn, p.Line, tk)
  53. }
  54. case TK_SET:
  55. key := strings.ToLower(tk)
  56. if !scan.Scan() {
  57. return nil, fmt.Errorf("empty value name for set in %s, line %d",
  58. fn, p.Line)
  59. }
  60. value := scan.Text()
  61. switch key {
  62. case "interval":
  63. err := config.SetInterval(value)
  64. if err != nil {
  65. return nil, fmt.Errorf(
  66. "invalid number for interval in %s, line %d: \"%s\"",
  67. fn, p.Line, value,
  68. )
  69. }
  70. default:
  71. return nil, fmt.Errorf("unknown key for set in %s, line %d: \"%s\"",
  72. fn, p.Line, tk,
  73. )
  74. }
  75. p.prevState()
  76. case TK_MONITOR:
  77. switch strings.ToLower(tk) {
  78. case "host":
  79. p.setState(TK_HOST)
  80. case "group":
  81. p.setState(TK_GROUP)
  82. default:
  83. p.prevState()
  84. goto stateswitch
  85. }
  86. case TK_GROUP:
  87. if p.lastGroup == nil {
  88. p.lastGroup, err = config.NewGroup(tk)
  89. if err != nil {
  90. return nil, fmt.Errorf("%s in %s, line %d",
  91. err.Error(), fn, p.Line,
  92. )
  93. }
  94. continue
  95. }
  96. switch strings.ToLower(tk) {
  97. case "host":
  98. p.setState(TK_HOST)
  99. default:
  100. p.prevState()
  101. goto stateswitch
  102. }
  103. case TK_HOST:
  104. // If a host has no group, inherit the host name
  105. if p.lastGroup == nil {
  106. p.lastGroup, err = config.NewGroup(tk)
  107. if err != nil {
  108. return nil, fmt.Errorf("%s in %s, line %d",
  109. err.Error(), fn, p.Line,
  110. )
  111. }
  112. }
  113. if p.lastHost == nil {
  114. p.lastHost, err = p.lastGroup.NewHost(tk)
  115. if err != nil {
  116. return nil, fmt.Errorf("%s in %s, line %d",
  117. err.Error(), fn, p.Line,
  118. )
  119. }
  120. continue
  121. }
  122. switch strings.ToLower(tk) {
  123. case "address":
  124. if !scan.Scan() {
  125. return nil, fmt.Errorf("empty address for host in %s, line %d",
  126. fn, p.Line)
  127. }
  128. p.lastHost.Address = scan.Text()
  129. case "check":
  130. p.setState(TK_CHECK)
  131. default:
  132. p.prevState()
  133. goto stateswitch
  134. }
  135. case TK_CHECK:
  136. if p.lastCheck == nil {
  137. p.lastCheck, err = p.lastHost.NewCheck(tk)
  138. if err != nil {
  139. return nil, fmt.Errorf("%s in %s, line %d",
  140. err.Error(), fn, p.Line)
  141. }
  142. continue
  143. }
  144. cont, err := p.lastCheck.Parse(tk)
  145. if err != nil {
  146. return nil, fmt.Errorf("%s in %s, line %d",
  147. err.Error(), fn, p.Line)
  148. }
  149. if !cont {
  150. p.lastCheck = nil
  151. p.prevState()
  152. goto stateswitch
  153. }
  154. case TK_ALARM:
  155. if p.lastAlarm == nil {
  156. if p.lastAlarmName == "" {
  157. p.lastAlarmName = tk
  158. continue
  159. }
  160. p.lastAlarm, err = config.NewAlarm(p.lastAlarmName, tk)
  161. if err != nil {
  162. return nil, fmt.Errorf("%s in %s, line %d",
  163. err.Error(), fn, p.Line)
  164. }
  165. p.lastAlarmName = ""
  166. continue
  167. }
  168. cont, err := p.lastAlarm.Parse(tk)
  169. if err != nil {
  170. return nil, fmt.Errorf("%s in %s, line %d",
  171. err.Error(), fn, p.Line)
  172. }
  173. if !cont {
  174. p.lastAlarm = nil
  175. p.prevState()
  176. goto stateswitch
  177. }
  178. default:
  179. return nil, fmt.Errorf("unknown parser state: %d", p.state())
  180. }
  181. }
  182. if err := scan.Err(); err != nil {
  183. return nil, err
  184. }
  185. return config, nil
  186. }
  187. func (p *Parser) state() int {
  188. if len(p.states) < 1 {
  189. return TK_NONE
  190. }
  191. return p.states[len(p.states)-1]
  192. }
  193. func (p *Parser) setState(state int) {
  194. switch state {
  195. case TK_SET, TK_MONITOR:
  196. fallthrough
  197. case TK_GROUP:
  198. p.lastGroup = nil
  199. fallthrough
  200. case TK_HOST:
  201. p.lastHost = nil
  202. p.lastCheck = nil
  203. }
  204. if p.DebugLevel > 1 {
  205. fmt.Printf("Parser state: %s", p.stateName())
  206. }
  207. p.states = append(p.states, state)
  208. if p.DebugLevel > 1 {
  209. fmt.Printf(" -> %s\n", p.stateName())
  210. }
  211. }
  212. func (p *Parser) prevState() int {
  213. if len(p.states) > 0 {
  214. p.states = p.states[:len(p.states)-1]
  215. }
  216. return p.state()
  217. }
  218. func (p *Parser) stateName() string {
  219. switch p.state() {
  220. case TK_NONE:
  221. return "TK_NONE"
  222. case TK_SET:
  223. return "TK_SET"
  224. case TK_MONITOR:
  225. return "TK_MONTIOR"
  226. case TK_GROUP:
  227. return "TK_GROUP"
  228. case TK_HOST:
  229. return "TK_HOST"
  230. case TK_CHECK:
  231. return "TK_CHECK"
  232. case TK_ALARM:
  233. return "TK_ALARM"
  234. default:
  235. return "UNKNOWN"
  236. }
  237. }
  238. func (p *Parser) Split(data []byte, atEOF bool) (int, []byte, error) {
  239. if atEOF && len(data) == 0 {
  240. return 0, nil, nil
  241. }
  242. var ignoreline bool
  243. var started bool
  244. var startidx int
  245. var quote byte
  246. for i := 0; i < len(data); i++ {
  247. c := data[i]
  248. // fmt.Printf("%c (%t) (%t)\n", c, started, ignoreline)
  249. switch c {
  250. case '\f', '\n', '\r':
  251. p.Line++
  252. if ignoreline {
  253. ignoreline = false
  254. continue
  255. }
  256. fallthrough
  257. case ' ', '\t', '\v':
  258. if started && quote == 0 {
  259. return i + 1, data[startidx:i], nil
  260. }
  261. case '\'', '"', '`':
  262. if started && quote == c {
  263. return i + 1, data[startidx:i], nil
  264. }
  265. if !ignoreline && quote == 0 {
  266. quote = c
  267. }
  268. case '#':
  269. if !started {
  270. ignoreline = true
  271. }
  272. default:
  273. if !ignoreline && !started {
  274. started = true
  275. startidx = i
  276. }
  277. }
  278. }
  279. if atEOF {
  280. if ignoreline {
  281. return len(data), nil, nil
  282. }
  283. if started {
  284. return len(data), data[startidx:], nil
  285. }
  286. }
  287. return 0, nil, nil
  288. }