Added framework for alarms, code cleanup

This commit is contained in:
Christopher Ramey 2021-01-17 09:04:08 -09:00
parent f108090967
commit 2d99960f06
8 changed files with 190 additions and 51 deletions

19
alarm/alarm.go Normal file
View File

@ -0,0 +1,19 @@
package alarm
import (
"fmt"
)
type Alarm interface {
Parse(string) (bool, error)
Alarm() error
}
func NewAlarm(name string, typename string) (Alarm, error) {
switch typename {
case "email":
return NewAlarmEmail(name), nil
default:
return nil, fmt.Errorf("unknown alarm name \"%s\"", name)
}
}

66
alarm/alarm_email.go Normal file
View File

@ -0,0 +1,66 @@
package alarm
import (
"fmt"
"strings"
)
const (
TK_NONE = iota
TK_TO
TK_SMTP
TK_FROM
)
type AlarmEmail struct {
Type string
Name string
From string
SMTP string
To []string
state int
}
func NewAlarmEmail(name string) *AlarmEmail {
return &AlarmEmail{
Type: "email", Name: name,
}
}
func (a *AlarmEmail) Alarm() error {
fmt.Printf("email alarm")
return nil
}
func (a *AlarmEmail) Parse(tk string) (bool, error) {
switch a.state {
case TK_NONE:
switch strings.ToLower(tk){
case "to":
a.state = TK_TO
case "from":
a.state = TK_FROM
case "smtp":
a.state = TK_SMTP
default:
return false, nil
}
case TK_FROM:
a.From = tk
a.state = TK_NONE
case TK_SMTP:
a.SMTP = tk
a.state = TK_NONE
case TK_TO:
a.To = append(a.To, tk)
a.state = TK_NONE
default:
return false, fmt.Errorf("invalid state in alarm_email")
}
return true, nil
}

10
alrmrc
View File

@ -1,10 +1,10 @@
set interval 30s set interval 30s
#alarm people email alarm people email
# to test1@localhost to test1@localhost
# to test2@localhost to test2@localhost
# smtp localhost smtp localhost
# from alrm@localhost from alrm@localhost
monitor group webservers monitor group webservers
host www1.example.com address 10.11.135.101 host www1.example.com address 10.11.135.101

View File

@ -4,12 +4,12 @@ import (
"fmt" "fmt"
) )
type AlrmCheck interface { type Check interface {
Parse(string) (bool, error) Parse(string) (bool, error)
Check(int) error Check(int) error
} }
func NewCheck(name string, addr string) (AlrmCheck, error) { func NewCheck(name string, addr string) (Check, error) {
switch name { switch name {
case "ping": case "ping":
return NewCheckPing(addr), nil return NewCheckPing(addr), nil

View File

@ -3,45 +3,65 @@ package config
import ( import (
"fmt" "fmt"
"time" "time"
"alrm/alarm"
) )
type AlrmConfig struct { type Config struct {
Groups map[string]*AlrmGroup Groups map[string]*Group
Alarms map[string]alarm.Alarm
Interval time.Duration Interval time.Duration
} }
func NewConfig() *AlrmConfig { func NewConfig() *Config {
return &AlrmConfig{ return &Config{
// Default check interval, 30 seconds // Default check interval, 30 seconds
Interval: time.Second * 30, Interval: time.Second * 30,
} }
} }
func (ac *AlrmConfig) NewGroup(name string) (*AlrmGroup, error) { func (c *Config) NewAlarm(name string, typename string) (alarm.Alarm, error) {
if ac.Groups == nil { if c.Alarms == nil {
ac.Groups = make(map[string]*AlrmGroup) c.Alarms = make(map[string]alarm.Alarm)
} }
if _, exists := ac.Groups[name]; exists { if _, exists := c.Alarms[name]; exists {
return nil, fmt.Errorf("alarm %s already exists", name)
}
a, err := alarm.NewAlarm(name, typename)
if err != nil {
return nil, err
}
c.Alarms[name] = a
return a, nil
}
func (c *Config) NewGroup(name string) (*Group, error) {
if c.Groups == nil {
c.Groups = make(map[string]*Group)
}
if _, exists := c.Groups[name]; exists {
return nil, fmt.Errorf("group %s already exists", name) return nil, fmt.Errorf("group %s already exists", name)
} }
group := &AlrmGroup{Name: name} group := &Group{Name: name}
ac.Groups[name] = group c.Groups[name] = group
return group, nil return group, nil
} }
func (ac *AlrmConfig) SetInterval(val string) error { func (c *Config) SetInterval(val string) error {
interval, err := time.ParseDuration(val) interval, err := time.ParseDuration(val)
if err != nil { if err != nil {
return err return err
} }
ac.Interval = interval c.Interval = interval
return nil return nil
} }
func ReadConfig(fn string, debuglvl int) (*AlrmConfig, error) { func ReadConfig(fn string, debuglvl int) (*Config, error) {
parser := &Parser{DebugLevel: debuglvl} parser := &Parser{DebugLevel: debuglvl}
config, err := parser.Parse(fn) config, err := parser.Parse(fn)
if err != nil { if err != nil {

View File

@ -4,26 +4,26 @@ import (
"fmt" "fmt"
) )
type AlrmGroup struct { type Group struct {
Name string Name string
Hosts map[string]*AlrmHost Hosts map[string]*Host
} }
func (ag *AlrmGroup) NewHost(name string) (*AlrmHost, error) { func (ag *Group) NewHost(name string) (*Host, error) {
if ag.Hosts == nil { if ag.Hosts == nil {
ag.Hosts = make(map[string]*AlrmHost) ag.Hosts = make(map[string]*Host)
} }
if _, exists := ag.Hosts[name]; exists { if _, exists := ag.Hosts[name]; exists {
return nil, fmt.Errorf("host %s already exists", name) return nil, fmt.Errorf("host %s already exists", name)
} }
host := &AlrmHost{Name: name} host := &Host{Name: name}
ag.Hosts[name] = host ag.Hosts[name] = host
return host, nil return host, nil
} }
func (ag *AlrmGroup) Check(debuglvl int) error { func (ag *Group) Check(debuglvl int) error {
for _, host := range ag.Hosts { for _, host := range ag.Hosts {
for _, chk := range host.Checks { for _, chk := range host.Checks {
err := chk.Check(debuglvl) err := chk.Check(debuglvl)

View File

@ -4,20 +4,20 @@ import (
"alrm/check" "alrm/check"
) )
type AlrmHost struct { type Host struct {
Name string Name string
Address string Address string
Checks []check.AlrmCheck Checks []check.Check
} }
func (ah *AlrmHost) GetAddress() string { func (ah *Host) GetAddress() string {
if ah.Address != "" { if ah.Address != "" {
return ah.Address return ah.Address
} }
return ah.Name return ah.Name
} }
func (ah *AlrmHost) NewCheck(name string) (check.AlrmCheck, error) { func (ah *Host) NewCheck(name string) (check.Check, error) {
chk, err := check.NewCheck(name, ah.GetAddress()) chk, err := check.NewCheck(name, ah.GetAddress())
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -1,6 +1,7 @@
package config package config
import ( import (
"alrm/alarm"
"alrm/check" "alrm/check"
"bufio" "bufio"
"fmt" "fmt"
@ -15,18 +16,21 @@ const (
TK_GROUP TK_GROUP
TK_HOST TK_HOST
TK_CHECK TK_CHECK
TK_ALARM
) )
type Parser struct { type Parser struct {
DebugLevel int DebugLevel int
Line int Line int
states []int states []int
lasthost *AlrmHost lastHost *Host
lastgroup *AlrmGroup lastGroup *Group
lastcheck check.AlrmCheck lastCheck check.Check
lastAlarm alarm.Alarm
lastAlarmName string
} }
func (p *Parser) Parse(fn string) (*AlrmConfig, error) { func (p *Parser) Parse(fn string) (*Config, error) {
file, err := os.Open(fn) file, err := os.Open(fn)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot open config \"%s\": %s", fn, err.Error()) return nil, fmt.Errorf("cannot open config \"%s\": %s", fn, err.Error())
@ -47,6 +51,8 @@ func (p *Parser) Parse(fn string) (*AlrmConfig, error) {
p.setState(TK_MONITOR) p.setState(TK_MONITOR)
case "set": case "set":
p.setState(TK_SET) p.setState(TK_SET)
case "alarm":
p.setState(TK_ALARM)
default: default:
return nil, fmt.Errorf("invalid token in %s, line %d: \"%s\"", return nil, fmt.Errorf("invalid token in %s, line %d: \"%s\"",
fn, p.Line, tk) fn, p.Line, tk)
@ -90,8 +96,8 @@ func (p *Parser) Parse(fn string) (*AlrmConfig, error) {
} }
case TK_GROUP: case TK_GROUP:
if p.lastgroup == nil { if p.lastGroup == nil {
p.lastgroup, err = config.NewGroup(tk) p.lastGroup, err = config.NewGroup(tk)
if err != nil { if err != nil {
return nil, fmt.Errorf("%s in %s, line %d", return nil, fmt.Errorf("%s in %s, line %d",
err.Error(), fn, p.Line, err.Error(), fn, p.Line,
@ -111,8 +117,8 @@ func (p *Parser) Parse(fn string) (*AlrmConfig, error) {
case TK_HOST: case TK_HOST:
// If a host has no group, inherit the host name // If a host has no group, inherit the host name
if p.lastgroup == nil { if p.lastGroup == nil {
p.lastgroup, err = config.NewGroup(tk) p.lastGroup, err = config.NewGroup(tk)
if err != nil { if err != nil {
return nil, fmt.Errorf("%s in %s, line %d", return nil, fmt.Errorf("%s in %s, line %d",
err.Error(), fn, p.Line, err.Error(), fn, p.Line,
@ -120,8 +126,8 @@ func (p *Parser) Parse(fn string) (*AlrmConfig, error) {
} }
} }
if p.lasthost == nil { if p.lastHost == nil {
p.lasthost, err = p.lastgroup.NewHost(tk) p.lastHost, err = p.lastGroup.NewHost(tk)
if err != nil { if err != nil {
return nil, fmt.Errorf("%s in %s, line %d", return nil, fmt.Errorf("%s in %s, line %d",
err.Error(), fn, p.Line, err.Error(), fn, p.Line,
@ -136,7 +142,7 @@ func (p *Parser) Parse(fn string) (*AlrmConfig, error) {
return nil, fmt.Errorf("empty address for host in %s, line %d", return nil, fmt.Errorf("empty address for host in %s, line %d",
fn, p.Line) fn, p.Line)
} }
p.lasthost.Address = scan.Text() p.lastHost.Address = scan.Text()
case "check": case "check":
p.setState(TK_CHECK) p.setState(TK_CHECK)
@ -147,21 +153,47 @@ func (p *Parser) Parse(fn string) (*AlrmConfig, error) {
} }
case TK_CHECK: case TK_CHECK:
if p.lastcheck == nil { if p.lastCheck == nil {
p.lastcheck, err = p.lasthost.NewCheck(tk) p.lastCheck, err = p.lastHost.NewCheck(tk)
if err != nil { if err != nil {
return nil, fmt.Errorf("%s in %s, line %d", return nil, fmt.Errorf("%s in %s, line %d",
err.Error(), fn, p.Line) err.Error(), fn, p.Line)
} }
continue continue
} }
cont, err := p.lastcheck.Parse(tk) cont, err := p.lastCheck.Parse(tk)
if err != nil { if err != nil {
return nil, fmt.Errorf("%s in %s, line %d", return nil, fmt.Errorf("%s in %s, line %d",
err.Error(), fn, p.Line) err.Error(), fn, p.Line)
} }
if !cont { if !cont {
p.lastcheck = nil p.lastCheck = nil
p.prevState()
goto stateswitch
}
case TK_ALARM:
if p.lastAlarm == nil {
if p.lastAlarmName == "" {
p.lastAlarmName = tk
continue
}
p.lastAlarm, err = config.NewAlarm(p.lastAlarmName, tk)
if err != nil {
return nil, fmt.Errorf("%s in %s, line %d",
err.Error(), fn, p.Line)
}
p.lastAlarmName = ""
continue
}
cont, err := p.lastAlarm.Parse(tk)
if err != nil {
return nil, fmt.Errorf("%s in %s, line %d",
err.Error(), fn, p.Line)
}
if !cont {
p.lastAlarm = nil
p.prevState() p.prevState()
goto stateswitch goto stateswitch
} }
@ -188,11 +220,11 @@ func (p *Parser) setState(state int) {
case TK_SET, TK_MONITOR: case TK_SET, TK_MONITOR:
fallthrough fallthrough
case TK_GROUP: case TK_GROUP:
p.lastgroup = nil p.lastGroup = nil
fallthrough fallthrough
case TK_HOST: case TK_HOST:
p.lasthost = nil p.lastHost = nil
p.lastcheck = nil p.lastCheck = nil
} }
if p.DebugLevel > 1 { if p.DebugLevel > 1 {
@ -225,6 +257,8 @@ func (p *Parser) stateName() string {
return "TK_HOST" return "TK_HOST"
case TK_CHECK: case TK_CHECK:
return "TK_CHECK" return "TK_CHECK"
case TK_ALARM:
return "TK_ALARM"
default: default:
return "UNKNOWN" return "UNKNOWN"
} }