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.

267 lines
5.0 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
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. "fmt"
  4. "git.binarythought.com/cdramey/alrm/alarm"
  5. "git.binarythought.com/cdramey/alrm/check"
  6. "strings"
  7. )
  8. const (
  9. PR_NONE = iota
  10. PR_SET
  11. PR_MONITOR
  12. PR_GROUP
  13. PR_HOST
  14. PR_CHECK
  15. PR_ALARM
  16. )
  17. type Parser struct {
  18. DebugLevel int
  19. states []int
  20. lastHost *Host
  21. lastGroup *Group
  22. lastCheck check.Check
  23. lastAlarm alarm.Alarm
  24. lastAlarmName string
  25. }
  26. func (p *Parser) Parse(fn string) (*Config, error) {
  27. config := NewConfig()
  28. tok, err := NewTokenizer(fn)
  29. if err != nil {
  30. return nil, err
  31. }
  32. defer tok.Close()
  33. for tok.Scan() {
  34. tk := tok.Text()
  35. stateswitch:
  36. switch p.state() {
  37. case PR_NONE:
  38. switch strings.ToLower(tk) {
  39. case "monitor":
  40. p.setState(PR_MONITOR)
  41. case "set":
  42. p.setState(PR_SET)
  43. case "alarm":
  44. p.setState(PR_ALARM)
  45. default:
  46. return nil, fmt.Errorf("invalid token in %s, line %d: \"%s\"",
  47. fn, tok.Line(), tk)
  48. }
  49. case PR_SET:
  50. key := strings.ToLower(tk)
  51. if !tok.Scan() {
  52. return nil, fmt.Errorf("empty value name for set in %s, line %d",
  53. fn, tok.Line())
  54. }
  55. value := tok.Text()
  56. switch key {
  57. case "interval":
  58. err := config.SetInterval(value)
  59. if err != nil {
  60. return nil, fmt.Errorf(
  61. "invalid duration for interval in %s, line %d: \"%s\"",
  62. fn, tok.Line(), value,
  63. )
  64. }
  65. case "threads":
  66. err := config.SetThreads(value)
  67. if err != nil {
  68. return nil, fmt.Errorf(
  69. "invalid number for interval in %s, line %d: \"%s\"",
  70. fn, tok.Line(), value,
  71. )
  72. }
  73. default:
  74. return nil, fmt.Errorf("unknown key for set in %s, line %d: \"%s\"",
  75. fn, tok.Line(), tk,
  76. )
  77. }
  78. p.prevState()
  79. case PR_MONITOR:
  80. switch strings.ToLower(tk) {
  81. case "host":
  82. p.setState(PR_HOST)
  83. case "group":
  84. p.setState(PR_GROUP)
  85. default:
  86. p.prevState()
  87. goto stateswitch
  88. }
  89. case PR_GROUP:
  90. if p.lastGroup == nil {
  91. p.lastGroup, err = config.NewGroup(tk)
  92. if err != nil {
  93. return nil, fmt.Errorf("%s in %s, line %d",
  94. err.Error(), fn, tok.Line(),
  95. )
  96. }
  97. continue
  98. }
  99. switch strings.ToLower(tk) {
  100. case "host":
  101. p.setState(PR_HOST)
  102. default:
  103. p.prevState()
  104. goto stateswitch
  105. }
  106. case PR_HOST:
  107. // If a host has no group, inherit the host name
  108. if p.lastGroup == nil {
  109. p.lastGroup, err = config.NewGroup(tk)
  110. if err != nil {
  111. return nil, fmt.Errorf("%s in %s, line %d",
  112. err.Error(), fn, tok.Line(),
  113. )
  114. }
  115. }
  116. if p.lastHost == nil {
  117. p.lastHost, err = p.lastGroup.NewHost(tk)
  118. if err != nil {
  119. return nil, fmt.Errorf("%s in %s, line %d",
  120. err.Error(), fn, tok.Line(),
  121. )
  122. }
  123. continue
  124. }
  125. switch strings.ToLower(tk) {
  126. case "address":
  127. if !tok.Scan() {
  128. return nil, fmt.Errorf("empty address for host in %s, line %d",
  129. fn, tok.Line())
  130. }
  131. p.lastHost.Address = tok.Text()
  132. case "check":
  133. p.setState(PR_CHECK)
  134. default:
  135. p.prevState()
  136. goto stateswitch
  137. }
  138. case PR_CHECK:
  139. if p.lastCheck == nil {
  140. p.lastCheck, err = p.lastHost.NewCheck(tk)
  141. if err != nil {
  142. return nil, fmt.Errorf("%s in %s, line %d",
  143. err.Error(), fn, tok.Line())
  144. }
  145. continue
  146. }
  147. cont, err := p.lastCheck.Parse(tk)
  148. if err != nil {
  149. return nil, fmt.Errorf("%s in %s, line %d",
  150. err.Error(), fn, tok.Line())
  151. }
  152. if !cont {
  153. p.lastCheck = nil
  154. p.prevState()
  155. goto stateswitch
  156. }
  157. case PR_ALARM:
  158. if p.lastAlarm == nil {
  159. if p.lastAlarmName == "" {
  160. p.lastAlarmName = tk
  161. continue
  162. }
  163. p.lastAlarm, err = config.NewAlarm(p.lastAlarmName, tk)
  164. if err != nil {
  165. return nil, fmt.Errorf("%s in %s, line %d",
  166. err.Error(), fn, tok.Line())
  167. }
  168. p.lastAlarmName = ""
  169. continue
  170. }
  171. cont, err := p.lastAlarm.Parse(tk)
  172. if err != nil {
  173. return nil, fmt.Errorf("%s in %s, line %d",
  174. err.Error(), fn, tok.Line())
  175. }
  176. if !cont {
  177. p.lastAlarm = nil
  178. p.prevState()
  179. goto stateswitch
  180. }
  181. default:
  182. return nil, fmt.Errorf("unknown parser state: %d", p.state())
  183. }
  184. }
  185. if err := tok.Err(); err != nil {
  186. return nil, err
  187. }
  188. return config, nil
  189. }
  190. func (p *Parser) state() int {
  191. if len(p.states) < 1 {
  192. return PR_NONE
  193. }
  194. return p.states[len(p.states)-1]
  195. }
  196. func (p *Parser) setState(state int) {
  197. switch state {
  198. case PR_SET, PR_MONITOR:
  199. fallthrough
  200. case PR_GROUP:
  201. p.lastGroup = nil
  202. fallthrough
  203. case PR_HOST:
  204. p.lastHost = nil
  205. p.lastCheck = nil
  206. }
  207. if p.DebugLevel > 1 {
  208. fmt.Printf("Parser state: %s", p.stateName())
  209. }
  210. p.states = append(p.states, state)
  211. if p.DebugLevel > 1 {
  212. fmt.Printf(" -> %s\n", p.stateName())
  213. }
  214. }
  215. func (p *Parser) prevState() int {
  216. if len(p.states) > 0 {
  217. p.states = p.states[:len(p.states)-1]
  218. }
  219. return p.state()
  220. }
  221. func (p *Parser) stateName() string {
  222. switch p.state() {
  223. case PR_NONE:
  224. return "PR_NONE"
  225. case PR_SET:
  226. return "PR_SET"
  227. case PR_MONITOR:
  228. return "PR_MONITOR"
  229. case PR_GROUP:
  230. return "PR_GROUP"
  231. case PR_HOST:
  232. return "PR_HOST"
  233. case PR_CHECK:
  234. return "PR_CHECK"
  235. case PR_ALARM:
  236. return "PR_ALARM"
  237. default:
  238. return "UNKNOWN"
  239. }
  240. }