diff --git a/alarm/alarm.go b/alarm/alarm.go new file mode 100644 index 0000000..4713907 --- /dev/null +++ b/alarm/alarm.go @@ -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) + } +} diff --git a/alarm/alarm_email.go b/alarm/alarm_email.go new file mode 100644 index 0000000..2a57b1b --- /dev/null +++ b/alarm/alarm_email.go @@ -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 +} diff --git a/alrmrc b/alrmrc index ba6db9c..1b426eb 100644 --- a/alrmrc +++ b/alrmrc @@ -1,10 +1,10 @@ set interval 30s -#alarm people email -# to test1@localhost -# to test2@localhost -# smtp localhost -# from alrm@localhost +alarm people email + to test1@localhost + to test2@localhost + smtp localhost + from alrm@localhost monitor group webservers host www1.example.com address 10.11.135.101 diff --git a/check/check.go b/check/check.go index 2c80ea8..c34086e 100644 --- a/check/check.go +++ b/check/check.go @@ -4,12 +4,12 @@ import ( "fmt" ) -type AlrmCheck interface { +type Check interface { Parse(string) (bool, error) Check(int) error } -func NewCheck(name string, addr string) (AlrmCheck, error) { +func NewCheck(name string, addr string) (Check, error) { switch name { case "ping": return NewCheckPing(addr), nil diff --git a/config/config.go b/config/config.go index 819ad45..88d34ec 100644 --- a/config/config.go +++ b/config/config.go @@ -3,45 +3,65 @@ package config import ( "fmt" "time" + "alrm/alarm" ) -type AlrmConfig struct { - Groups map[string]*AlrmGroup +type Config struct { + Groups map[string]*Group + Alarms map[string]alarm.Alarm Interval time.Duration } -func NewConfig() *AlrmConfig { - return &AlrmConfig{ +func NewConfig() *Config { + return &Config{ // Default check interval, 30 seconds Interval: time.Second * 30, } } -func (ac *AlrmConfig) NewGroup(name string) (*AlrmGroup, error) { - if ac.Groups == nil { - ac.Groups = make(map[string]*AlrmGroup) +func (c *Config) NewAlarm(name string, typename string) (alarm.Alarm, error) { + if c.Alarms == nil { + 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) } - group := &AlrmGroup{Name: name} - ac.Groups[name] = group + group := &Group{Name: name} + c.Groups[name] = group return group, nil } -func (ac *AlrmConfig) SetInterval(val string) error { +func (c *Config) SetInterval(val string) error { interval, err := time.ParseDuration(val) if err != nil { return err } - ac.Interval = interval + c.Interval = interval return nil } -func ReadConfig(fn string, debuglvl int) (*AlrmConfig, error) { +func ReadConfig(fn string, debuglvl int) (*Config, error) { parser := &Parser{DebugLevel: debuglvl} config, err := parser.Parse(fn) if err != nil { diff --git a/config/group.go b/config/group.go index 0e97fb8..f5c4082 100644 --- a/config/group.go +++ b/config/group.go @@ -4,26 +4,26 @@ import ( "fmt" ) -type AlrmGroup struct { +type Group struct { 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 { - ag.Hosts = make(map[string]*AlrmHost) + ag.Hosts = make(map[string]*Host) } if _, exists := ag.Hosts[name]; exists { return nil, fmt.Errorf("host %s already exists", name) } - host := &AlrmHost{Name: name} + host := &Host{Name: name} ag.Hosts[name] = host return host, nil } -func (ag *AlrmGroup) Check(debuglvl int) error { +func (ag *Group) Check(debuglvl int) error { for _, host := range ag.Hosts { for _, chk := range host.Checks { err := chk.Check(debuglvl) diff --git a/config/host.go b/config/host.go index bf97e42..e0157f0 100644 --- a/config/host.go +++ b/config/host.go @@ -4,20 +4,20 @@ import ( "alrm/check" ) -type AlrmHost struct { +type Host struct { Name string Address string - Checks []check.AlrmCheck + Checks []check.Check } -func (ah *AlrmHost) GetAddress() string { +func (ah *Host) GetAddress() string { if ah.Address != "" { return ah.Address } 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()) if err != nil { return nil, err diff --git a/config/parser.go b/config/parser.go index 1dcb14b..80fb864 100644 --- a/config/parser.go +++ b/config/parser.go @@ -1,6 +1,7 @@ package config import ( + "alrm/alarm" "alrm/check" "bufio" "fmt" @@ -15,18 +16,21 @@ const ( TK_GROUP TK_HOST TK_CHECK + TK_ALARM ) type Parser struct { - DebugLevel int - Line int - states []int - lasthost *AlrmHost - lastgroup *AlrmGroup - lastcheck check.AlrmCheck + DebugLevel int + Line int + states []int + lastHost *Host + lastGroup *Group + 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) if err != nil { 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) case "set": p.setState(TK_SET) + case "alarm": + p.setState(TK_ALARM) default: return nil, fmt.Errorf("invalid token in %s, line %d: \"%s\"", fn, p.Line, tk) @@ -90,8 +96,8 @@ func (p *Parser) Parse(fn string) (*AlrmConfig, error) { } case TK_GROUP: - if p.lastgroup == nil { - p.lastgroup, err = config.NewGroup(tk) + if p.lastGroup == nil { + p.lastGroup, err = config.NewGroup(tk) if err != nil { return nil, fmt.Errorf("%s in %s, line %d", err.Error(), fn, p.Line, @@ -111,8 +117,8 @@ func (p *Parser) Parse(fn string) (*AlrmConfig, error) { case TK_HOST: // If a host has no group, inherit the host name - if p.lastgroup == nil { - p.lastgroup, err = config.NewGroup(tk) + if p.lastGroup == nil { + p.lastGroup, err = config.NewGroup(tk) if err != nil { return nil, fmt.Errorf("%s in %s, line %d", err.Error(), fn, p.Line, @@ -120,8 +126,8 @@ func (p *Parser) Parse(fn string) (*AlrmConfig, error) { } } - if p.lasthost == nil { - p.lasthost, err = p.lastgroup.NewHost(tk) + if p.lastHost == nil { + p.lastHost, err = p.lastGroup.NewHost(tk) if err != nil { return nil, fmt.Errorf("%s in %s, line %d", 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", fn, p.Line) } - p.lasthost.Address = scan.Text() + p.lastHost.Address = scan.Text() case "check": p.setState(TK_CHECK) @@ -147,21 +153,47 @@ func (p *Parser) Parse(fn string) (*AlrmConfig, error) { } case TK_CHECK: - if p.lastcheck == nil { - p.lastcheck, err = p.lasthost.NewCheck(tk) + if p.lastCheck == nil { + p.lastCheck, err = p.lastHost.NewCheck(tk) if err != nil { return nil, fmt.Errorf("%s in %s, line %d", err.Error(), fn, p.Line) } continue } - cont, err := p.lastcheck.Parse(tk) + cont, err := p.lastCheck.Parse(tk) if err != nil { return nil, fmt.Errorf("%s in %s, line %d", err.Error(), fn, p.Line) } 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() goto stateswitch } @@ -188,11 +220,11 @@ func (p *Parser) setState(state int) { case TK_SET, TK_MONITOR: fallthrough case TK_GROUP: - p.lastgroup = nil + p.lastGroup = nil fallthrough case TK_HOST: - p.lasthost = nil - p.lastcheck = nil + p.lastHost = nil + p.lastCheck = nil } if p.DebugLevel > 1 { @@ -225,6 +257,8 @@ func (p *Parser) stateName() string { return "TK_HOST" case TK_CHECK: return "TK_CHECK" + case TK_ALARM: + return "TK_ALARM" default: return "UNKNOWN" }