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.

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