diff --git a/main.go b/main.go index 5a7d08758..8ab97759d 100644 --- a/main.go +++ b/main.go @@ -24,6 +24,7 @@ import ( "github.com/prometheus/client_golang/extraction" "github.com/prometheus/prometheus/config" + "github.com/prometheus/prometheus/notification" "github.com/prometheus/prometheus/retrieval" "github.com/prometheus/prometheus/rules" "github.com/prometheus/prometheus/storage/metric" @@ -62,6 +63,9 @@ var ( arenaFlushInterval = flag.Duration("arena.flushInterval", 15*time.Minute, "The period at which the in-memory arena is flushed to disk.") arenaTTL = flag.Duration("arena.ttl", 10*time.Minute, "The relative age of values to purge to disk from memory.") + + alertmanagerUrl = flag.String("alertmanager.url", "", "The URL of the alert manager to send notifications to.") + notificationQueueCapacity = flag.Int("alertmanager.notificationQueueCapacity", 100, "The size of the queue for pending alert manager notifications.") ) type prometheus struct { @@ -77,7 +81,9 @@ type prometheus struct { unwrittenSamples chan *extraction.Result - storage *metric.TieredStorage + ruleManager rules.RuleManager + notifications chan rules.NotificationReqs + storage *metric.TieredStorage } func (p *prometheus) interruptHandler() { @@ -146,7 +152,10 @@ func (p *prometheus) close() { p.curationMutex.Lock() + p.ruleManager.Stop() p.storage.Close() + + close(p.notifications) close(p.stopBackgroundOperations) close(p.curationState) close(p.databaseStates) @@ -209,14 +218,20 @@ func main() { targetManager := retrieval.NewTargetManager(unwrittenSamples, *concurrentRetrievalAllowance) targetManager.AddTargetsFromConfig(conf) + notifications := make(chan rules.NotificationReqs, *notificationQueueCapacity) + // Queue depth will need to be exposed - ruleManager := rules.NewRuleManager(unwrittenSamples, conf.EvaluationInterval(), ts) + ruleManager := rules.NewRuleManager(unwrittenSamples, notifications, conf.EvaluationInterval(), ts) err = ruleManager.AddRulesFromConfig(conf) if err != nil { log.Fatalf("Error loading rule files: %v", err) } go ruleManager.Run() + // Queue depth will need to be exposed + notificationHandler := notification.NewNotificationHandler(*alertmanagerUrl, notifications) + go notificationHandler.Run() + flags := map[string]string{} flag.VisitAll(func(f *flag.Flag) { @@ -272,7 +287,9 @@ func main() { stopBackgroundOperations: make(chan bool, 1), - storage: ts, + ruleManager: ruleManager, + notifications: notifications, + storage: ts, } defer prometheus.close() diff --git a/notification/notification.go b/notification/notification.go new file mode 100644 index 000000000..633b80f2a --- /dev/null +++ b/notification/notification.go @@ -0,0 +1,105 @@ +// Copyright 2013 Prometheus Team +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package notification + +import ( + "bytes" + "encoding/json" + "flag" + "io/ioutil" + "log" + "net/http" + "time" + + clientmodel "github.com/prometheus/client_golang/model" + + "github.com/prometheus/prometheus/rules" + "github.com/prometheus/prometheus/utility" +) + +const ( + alertmanagerApiEventsPath = "/api/events" + contentTypeJson = "application/json" +) + +var ( + deadline = flag.Duration("alertmanager.httpDeadline", 10*time.Second, "Alert manager HTTP API timeout.") +) + +// NotificationHandler is responsible for dispatching alert notifications to an +// alert manager service. +type NotificationHandler struct { + // The URL of the alert manager to send notifications to. + alertmanagerUrl string + // Buffer of notifications that have not yet been sent. + pendingNotifications <-chan rules.NotificationReqs + // HTTP client with custom timeout settings. + httpClient http.Client +} + +// Construct a new NotificationHandler. +func NewNotificationHandler(alertmanagerUrl string, notificationReqs <-chan rules.NotificationReqs) *NotificationHandler { + return &NotificationHandler{ + alertmanagerUrl: alertmanagerUrl, + pendingNotifications: notificationReqs, + httpClient: utility.NewDeadlineClient(*deadline), + } +} + +// Send a list of notifications to the configured alert manager. +func (n *NotificationHandler) sendNotifications(reqs rules.NotificationReqs) error { + alerts := make([]map[string]interface{}, 0, len(reqs)) + for _, req := range reqs { + alerts = append(alerts, map[string]interface{}{ + "Summary": req.Rule.Summary, + "Description": req.Rule.Description, + "Labels": req.ActiveAlert.Labels.Merge(clientmodel.LabelSet{ + rules.AlertNameLabel: clientmodel.LabelValue(req.Rule.Name()), + }), + "Payload": map[string]interface{}{ + "Value": req.ActiveAlert.Value, + "ActiveSince": req.ActiveAlert.ActiveSince, + }, + }) + } + buf, err := json.Marshal(alerts) + if err != nil { + return err + } + resp, err := n.httpClient.Post( + n.alertmanagerUrl+alertmanagerApiEventsPath, + contentTypeJson, + bytes.NewBuffer(buf), + ) + if err != nil { + return err + } + defer resp.Body.Close() + + _, err = ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + // BUG: Do we need to check the response code? + return nil +} + +// Continusouly dispatch notifications. +func (n *NotificationHandler) Run() { + for reqs := range n.pendingNotifications { + if err := n.sendNotifications(reqs); err != nil { + log.Println("Error sending notification:", err) + } + } +} diff --git a/retrieval/deadline_client.go b/retrieval/deadline_client.go deleted file mode 100644 index 5bed4a833..000000000 --- a/retrieval/deadline_client.go +++ /dev/null @@ -1,30 +0,0 @@ -package retrieval - -import ( - "net" - "net/http" - "time" -) - -// NewDeadlineClient returns a new http.Client which will time out long running -// requests. -func NewDeadlineClient(timeout time.Duration) http.Client { - return http.Client{ - Transport: &http.Transport{ - // We need to disable keepalive, becasue we set a deadline on the - // underlying connection. - DisableKeepAlives: true, - Dial: func(netw, addr string) (c net.Conn, err error) { - start := time.Now() - - c, err = net.DialTimeout(netw, addr, timeout) - - if err == nil { - c.SetDeadline(start.Add(timeout)) - } - - return - }, - }, - } -} diff --git a/retrieval/target.go b/retrieval/target.go index b2875f4e0..51076c1cb 100644 --- a/retrieval/target.go +++ b/retrieval/target.go @@ -25,6 +25,8 @@ import ( "github.com/prometheus/client_golang/extraction" clientmodel "github.com/prometheus/client_golang/model" + + "github.com/prometheus/prometheus/utility" ) const ( @@ -143,7 +145,7 @@ func NewTarget(address string, deadline time.Duration, baseLabels clientmodel.La address: address, Deadline: deadline, baseLabels: baseLabels, - client: NewDeadlineClient(deadline), + client: utility.NewDeadlineClient(deadline), } scheduler := &healthScheduler{ diff --git a/rules/alerting.go b/rules/alerting.go index 579b2ccc2..1654ec974 100644 --- a/rules/alerting.go +++ b/rules/alerting.go @@ -101,7 +101,11 @@ type AlertingRule struct { // output vector before an alert transitions from PENDING to FIRING state. holdDuration time.Duration // Extra labels to attach to the resulting alert sample vectors. - labels clientmodel.LabelSet + Labels clientmodel.LabelSet + // Short alert summary, suitable for email subjects. + Summary string + // More detailed alert description. + Description string // Protects the below. mutex sync.Mutex @@ -110,7 +114,9 @@ type AlertingRule struct { activeAlerts map[clientmodel.Fingerprint]*Alert } -func (rule *AlertingRule) Name() string { return rule.name } +func (rule *AlertingRule) Name() string { + return rule.name +} func (rule *AlertingRule) EvalRaw(timestamp time.Time, storage *metric.TieredStorage) (ast.Vector, error) { return ast.EvalVectorInstant(rule.vector, timestamp, storage, stats.NewTimerGroup()) @@ -137,6 +143,7 @@ func (rule *AlertingRule) Eval(timestamp time.Time, storage *metric.TieredStorag if alert, ok := rule.activeAlerts[*fp]; !ok { labels := clientmodel.LabelSet{} labels.MergeFromMetric(sample.Metric) + labels = labels.Merge(rule.Labels) if _, ok := labels[clientmodel.MetricNameLabel]; ok { delete(labels, clientmodel.MetricNameLabel) } @@ -183,7 +190,7 @@ func (rule *AlertingRule) ToDotGraph() string { } func (rule *AlertingRule) String() string { - return fmt.Sprintf("ALERT %s IF %s FOR %s WITH %s", rule.name, rule.vector, utility.DurationToString(rule.holdDuration), rule.labels) + return fmt.Sprintf("ALERT %s IF %s FOR %s WITH %s", rule.name, rule.vector, utility.DurationToString(rule.holdDuration), rule.Labels) } func (rule *AlertingRule) HTMLSnippet() template.HTML { @@ -198,7 +205,7 @@ func (rule *AlertingRule) HTMLSnippet() template.HTML { ConsoleLinkForExpression(rule.vector.String()), rule.vector, utility.DurationToString(rule.holdDuration), - rule.labels)) + rule.Labels)) } func (rule *AlertingRule) State() AlertState { @@ -226,12 +233,15 @@ func (rule *AlertingRule) ActiveAlerts() []Alert { } // Construct a new AlertingRule. -func NewAlertingRule(name string, vector ast.VectorNode, holdDuration time.Duration, labels clientmodel.LabelSet) *AlertingRule { +func NewAlertingRule(name string, vector ast.VectorNode, holdDuration time.Duration, labels clientmodel.LabelSet, summary string, description string) *AlertingRule { return &AlertingRule{ name: name, vector: vector, holdDuration: holdDuration, - labels: labels, + Labels: labels, + Summary: summary, + Description: description, + activeAlerts: map[clientmodel.Fingerprint]*Alert{}, } } diff --git a/rules/fixtures/mixed.rules b/rules/fixtures/mixed.rules index ca50cd96f..7b9520607 100644 --- a/rules/fixtures/mixed.rules +++ b/rules/fixtures/mixed.rules @@ -3,11 +3,14 @@ dc_http_request_rate5m = sum(rate(http_request_count[5m])) by (dc) // A simple test alerting rule. ALERT GlobalRequestRateLow IF(dc_http_request_rate5m < 10000) FOR 5m WITH { - description = "Global HTTP request rate low!", - summary = "Request rate low" - /* ... more fields here ... */ -} + service = "testservice" + /* ... more fields here ... */ + } + SUMMARY "Global request rate low" + DESCRIPTION "The global request rate is low" foo = bar{label1="value1"} ALERT BazAlert IF(foo > 10) WITH {} + SUMMARY "Baz" + DESCRIPTION "BazAlert" diff --git a/rules/fixtures/syntax_error.rules b/rules/fixtures/syntax_error.rules index 5e0aff861..d44c8c9a8 100644 --- a/rules/fixtures/syntax_error.rules +++ b/rules/fixtures/syntax_error.rules @@ -3,11 +3,13 @@ dc_http_request_rate5m = sum(rate(http_request_count[5m])) by (dc) // A simple test alerting rule with a syntax error (invalid duration string "5"). ALERT GlobalRequestRateLow IF(dc_http_request_rate5m < 10000) FOR 5 WITH { - description = "Global HTTP request rate low!", - summary = "Request rate low" - /* ... more fields here ... */ -} + description = "Global HTTP request rate low!", + summary = "Request rate low" + /* ... more fields here ... */ + } + SUMMARY "summary" + DESCRIPTION "description" foo = bar{label1="value1"} -ALERT BazAlert IF(foo > 10) WITH {} +ALERT BazAlert IF(foo > 10) WITH {} SUMMARY "summary" DESCRIPTION "description" diff --git a/rules/helpers.go b/rules/helpers.go index cc59875ed..511f6d811 100644 --- a/rules/helpers.go +++ b/rules/helpers.go @@ -30,7 +30,7 @@ func CreateRecordingRule(name string, labels clientmodel.LabelSet, expr ast.Node return NewRecordingRule(name, labels, expr.(ast.VectorNode), permanent), nil } -func CreateAlertingRule(name string, expr ast.Node, holdDurationStr string, labels clientmodel.LabelSet) (*AlertingRule, error) { +func CreateAlertingRule(name string, expr ast.Node, holdDurationStr string, labels clientmodel.LabelSet, summary string, description string) (*AlertingRule, error) { if _, ok := expr.(ast.VectorNode); !ok { return nil, fmt.Errorf("Alert rule expression %v does not evaluate to vector type", expr) } @@ -38,7 +38,7 @@ func CreateAlertingRule(name string, expr ast.Node, holdDurationStr string, labe if err != nil { return nil, err } - return NewAlertingRule(name, expr.(ast.VectorNode), holdDuration, labels), nil + return NewAlertingRule(name, expr.(ast.VectorNode), holdDuration, labels, summary, description), nil } func NewFunctionCall(name string, args []ast.Node) (ast.Node, error) { diff --git a/rules/lexer.l b/rules/lexer.l index 55b68c42b..4a22066f7 100644 --- a/rules/lexer.l +++ b/rules/lexer.l @@ -1,15 +1,15 @@ -// Copyright 2013 Prometheus Team -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* Copyright 2013 Prometheus Team + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ %{ package rules @@ -73,6 +73,8 @@ ALERT|alert return ALERT IF|if return IF FOR|for return FOR WITH|with return WITH +SUMMARY|summary return SUMMARY +DESCRIPTION|description return DESCRIPTION PERMANENT|permanent return PERMANENT BY|by return GROUP_OP diff --git a/rules/lexer.l.go b/rules/lexer.l.go index d8b435d64..60f03e778 100644 --- a/rules/lexer.l.go +++ b/rules/lexer.l.go @@ -1,3 +1,15 @@ +/* Copyright 2013 Prometheus Team + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package rules import ( @@ -8,6 +20,9 @@ import ( clientmodel "github.com/prometheus/client_golang/model" ) +// Lex is called by the parser generated by "go tool yacc" to obtain each +// token. The method is opened before the matching rules block and closed at +// the end of the file. func (lexer *RulesLexer) Lex(lval *yySymType) int { // Internal lexer states. const ( @@ -43,7 +58,7 @@ panic(fmt.Errorf(`invalid start condition %d`, yyt)) case 0: // start condition: INITIAL goto yystart1 case 1: // start condition: S_COMMENTS -goto yystart97 +goto yystart127 } goto yystate1 // silence unused label error @@ -67,7 +82,7 @@ case c == '-': goto yystate14 case c == '/': goto yystate17 -case c == ':' || c == 'D' || c == 'E' || c == 'G' || c == 'H' || c >= 'J' && c <= 'L' || c == 'N' || c == 'Q' || c == 'R' || c >= 'T' && c <= 'V' || c >= 'X' && c <= 'Z' || c == '_' || c == 'd' || c == 'e' || c == 'g' || c == 'h' || c >= 'j' && c <= 'l' || c == 'n' || c == 'q' || c == 'r' || c >= 't' && c <= 'v' || c >= 'x' && c <= 'z': +case c == ':' || c == 'E' || c == 'G' || c == 'H' || c >= 'J' && c <= 'L' || c == 'N' || c == 'Q' || c == 'R' || c >= 'T' && c <= 'V' || c >= 'X' && c <= 'Z' || c == '_' || c == 'e' || c == 'g' || c == 'h' || c >= 'j' && c <= 'l' || c == 'n' || c == 'q' || c == 'r' || c >= 't' && c <= 'v' || c >= 'x' && c <= 'z': goto yystate23 case c == '<' || c == '>': goto yystate24 @@ -79,51 +94,55 @@ case c == 'B': goto yystate35 case c == 'C': goto yystate37 -case c == 'F': +case c == 'D': goto yystate41 +case c == 'F': +goto yystate52 case c == 'I': -goto yystate44 +goto yystate55 case c == 'M': -goto yystate46 +goto yystate57 case c == 'O': -goto yystate49 +goto yystate60 case c == 'P': -goto yystate50 -case c == 'S': -goto yystate59 -case c == 'W': goto yystate61 +case c == 'S': +goto yystate70 +case c == 'W': +goto yystate77 case c == '\'': goto yystate9 case c == '\t' || c == '\n' || c == '\r' || c == ' ': goto yystate2 case c == 'a': -goto yystate65 +goto yystate81 case c == 'b': -goto yystate72 +goto yystate88 case c == 'c': -goto yystate73 +goto yystate89 +case c == 'd': +goto yystate93 case c == 'f': -goto yystate77 +goto yystate103 case c == 'i': -goto yystate79 +goto yystate105 case c == 'm': -goto yystate80 +goto yystate106 case c == 'o': -goto yystate83 +goto yystate109 case c == 'p': -goto yystate84 +goto yystate110 case c == 's': -goto yystate92 +goto yystate118 case c == 'w': -goto yystate94 +goto yystate124 case c >= '0' && c <= '9': goto yystate21 } yystate2: c = lexer.getChar() -goto yyrule23 +goto yyrule25 yystate3: c = lexer.getChar() @@ -136,7 +155,7 @@ goto yystate4 yystate4: c = lexer.getChar() -goto yyrule14 +goto yyrule16 yystate5: c = lexer.getChar() @@ -153,7 +172,7 @@ goto yystate5 yystate6: c = lexer.getChar() -goto yyrule20 +goto yyrule22 yystate7: c = lexer.getChar() @@ -166,7 +185,7 @@ goto yystate5 yystate8: c = lexer.getChar() -goto yyrule16 +goto yyrule18 yystate9: c = lexer.getChar() @@ -183,7 +202,7 @@ goto yystate9 yystate10: c = lexer.getChar() -goto yyrule21 +goto yyrule23 yystate11: c = lexer.getChar() @@ -196,17 +215,17 @@ goto yystate9 yystate12: c = lexer.getChar() -goto yyrule22 +goto yyrule24 yystate13: c = lexer.getChar() -goto yyrule15 +goto yyrule17 yystate14: c = lexer.getChar() switch { default: -goto yyrule15 +goto yyrule17 case c >= '0' && c <= '9': goto yystate15 } @@ -215,7 +234,7 @@ yystate15: c = lexer.getChar() switch { default: -goto yyrule19 +goto yyrule21 case c == '.': goto yystate16 case c >= '0' && c <= '9': @@ -226,7 +245,7 @@ yystate16: c = lexer.getChar() switch { default: -goto yyrule19 +goto yyrule21 case c >= '0' && c <= '9': goto yystate16 } @@ -235,7 +254,7 @@ yystate17: c = lexer.getChar() switch { default: -goto yyrule16 +goto yyrule18 case c == '*': goto yystate18 case c == '/': @@ -265,7 +284,7 @@ yystate21: c = lexer.getChar() switch { default: -goto yyrule19 +goto yyrule21 case c == '.': goto yystate16 case c == 'd' || c == 'h' || c == 'm' || c == 's' || c == 'w' || c == 'y': @@ -276,13 +295,13 @@ goto yystate21 yystate22: c = lexer.getChar() -goto yyrule17 +goto yyrule19 yystate23: c = lexer.getChar() switch { default: -goto yyrule18 +goto yyrule20 case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate23 } @@ -291,7 +310,7 @@ yystate24: c = lexer.getChar() switch { default: -goto yyrule13 +goto yyrule15 case c == '=': goto yystate4 } @@ -300,7 +319,7 @@ yystate25: c = lexer.getChar() switch { default: -goto yyrule22 +goto yyrule24 case c == '=': goto yystate4 } @@ -309,7 +328,7 @@ yystate26: c = lexer.getChar() switch { default: -goto yyrule18 +goto yyrule20 case c == 'L': goto yystate27 case c == 'N': @@ -324,7 +343,7 @@ yystate27: c = lexer.getChar() switch { default: -goto yyrule18 +goto yyrule20 case c == 'E': goto yystate28 case c >= '0' && c <= ':' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': @@ -335,7 +354,7 @@ yystate28: c = lexer.getChar() switch { default: -goto yyrule18 +goto yyrule20 case c == 'R': goto yystate29 case c >= '0' && c <= ':' || c >= 'A' && c <= 'Q' || c >= 'S' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': @@ -346,7 +365,7 @@ yystate29: c = lexer.getChar() switch { default: -goto yyrule18 +goto yyrule20 case c == 'T': goto yystate30 case c >= '0' && c <= ':' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': @@ -366,7 +385,7 @@ yystate31: c = lexer.getChar() switch { default: -goto yyrule18 +goto yyrule20 case c == 'D': goto yystate32 case c >= '0' && c <= ':' || c >= 'A' && c <= 'C' || c >= 'E' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': @@ -377,7 +396,7 @@ yystate32: c = lexer.getChar() switch { default: -goto yyrule13 +goto yyrule15 case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate23 } @@ -386,7 +405,7 @@ yystate33: c = lexer.getChar() switch { default: -goto yyrule18 +goto yyrule20 case c == 'G': goto yystate34 case c >= '0' && c <= ':' || c >= 'A' && c <= 'F' || c >= 'H' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': @@ -397,7 +416,7 @@ yystate34: c = lexer.getChar() switch { default: -goto yyrule11 +goto yyrule13 case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate23 } @@ -406,7 +425,7 @@ yystate35: c = lexer.getChar() switch { default: -goto yyrule18 +goto yyrule20 case c == 'Y': goto yystate36 case c >= '0' && c <= ':' || c >= 'A' && c <= 'X' || c == 'Z' || c == '_' || c >= 'a' && c <= 'z': @@ -417,7 +436,7 @@ yystate36: c = lexer.getChar() switch { default: -goto yyrule10 +goto yyrule12 case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate23 } @@ -426,7 +445,7 @@ yystate37: c = lexer.getChar() switch { default: -goto yyrule18 +goto yyrule20 case c == 'O': goto yystate38 case c >= '0' && c <= ':' || c >= 'A' && c <= 'N' || c >= 'P' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': @@ -437,7 +456,7 @@ yystate38: c = lexer.getChar() switch { default: -goto yyrule18 +goto yyrule20 case c == 'U': goto yystate39 case c >= '0' && c <= ':' || c >= 'A' && c <= 'T' || c >= 'V' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': @@ -448,7 +467,7 @@ yystate39: c = lexer.getChar() switch { default: -goto yyrule18 +goto yyrule20 case c == 'N': goto yystate40 case c >= '0' && c <= ':' || c >= 'A' && c <= 'M' || c >= 'O' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': @@ -459,7 +478,7 @@ yystate40: c = lexer.getChar() switch { default: -goto yyrule18 +goto yyrule20 case c == 'T': goto yystate34 case c >= '0' && c <= ':' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': @@ -470,10 +489,10 @@ yystate41: c = lexer.getChar() switch { default: -goto yyrule18 -case c == 'O': +goto yyrule20 +case c == 'E': goto yystate42 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'N' || c >= 'P' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': +case c >= '0' && c <= ':' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate23 } @@ -481,10 +500,10 @@ yystate42: c = lexer.getChar() switch { default: -goto yyrule18 -case c == 'R': +goto yyrule20 +case c == 'S': goto yystate43 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Q' || c >= 'S' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': +case c >= '0' && c <= ':' || c >= 'A' && c <= 'R' || c >= 'T' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate23 } @@ -492,23 +511,142 @@ yystate43: c = lexer.getChar() switch { default: +goto yyrule20 +case c == 'C': +goto yystate44 +case c >= '0' && c <= ':' || c == 'A' || c == 'B' || c >= 'D' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': +goto yystate23 +} + +yystate44: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'R': +goto yystate45 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Q' || c >= 'S' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': +goto yystate23 +} + +yystate45: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'I': +goto yystate46 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'H' || c >= 'J' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': +goto yystate23 +} + +yystate46: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'P': +goto yystate47 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'O' || c >= 'Q' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': +goto yystate23 +} + +yystate47: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'T': +goto yystate48 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': +goto yystate23 +} + +yystate48: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'I': +goto yystate49 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'H' || c >= 'J' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': +goto yystate23 +} + +yystate49: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'O': +goto yystate50 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'N' || c >= 'P' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': +goto yystate23 +} + +yystate50: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'N': +goto yystate51 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'M' || c >= 'O' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': +goto yystate23 +} + +yystate51: +c = lexer.getChar() +switch { +default: +goto yyrule10 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': +goto yystate23 +} + +yystate52: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'O': +goto yystate53 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'N' || c >= 'P' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': +goto yystate23 +} + +yystate53: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'R': +goto yystate54 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Q' || c >= 'S' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': +goto yystate23 +} + +yystate54: +c = lexer.getChar() +switch { +default: goto yyrule7 case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate23 } -yystate44: +yystate55: c = lexer.getChar() switch { default: -goto yyrule18 +goto yyrule20 case c == 'F': -goto yystate45 +goto yystate56 case c >= '0' && c <= ':' || c >= 'A' && c <= 'E' || c >= 'G' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate23 } -yystate45: +yystate56: c = lexer.getChar() switch { default: @@ -517,141 +655,216 @@ case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c < goto yystate23 } -yystate46: +yystate57: c = lexer.getChar() switch { default: -goto yyrule18 +goto yyrule20 case c == 'A': -goto yystate47 +goto yystate58 case c == 'I': -goto yystate48 +goto yystate59 case c >= '0' && c <= ':' || c >= 'B' && c <= 'H' || c >= 'J' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate23 } -yystate47: +yystate58: c = lexer.getChar() switch { default: -goto yyrule18 +goto yyrule20 case c == 'X': goto yystate34 case c >= '0' && c <= ':' || c >= 'A' && c <= 'W' || c == 'Y' || c == 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate23 } -yystate48: +yystate59: c = lexer.getChar() switch { default: -goto yyrule18 +goto yyrule20 case c == 'N': goto yystate34 case c >= '0' && c <= ':' || c >= 'A' && c <= 'M' || c >= 'O' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate23 } -yystate49: +yystate60: c = lexer.getChar() switch { default: -goto yyrule18 +goto yyrule20 case c == 'R': goto yystate32 case c >= '0' && c <= ':' || c >= 'A' && c <= 'Q' || c >= 'S' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate23 } -yystate50: +yystate61: c = lexer.getChar() switch { default: -goto yyrule18 +goto yyrule20 case c == 'E': -goto yystate51 +goto yystate62 case c >= '0' && c <= ':' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate23 } -yystate51: +yystate62: c = lexer.getChar() switch { default: -goto yyrule18 +goto yyrule20 case c == 'R': -goto yystate52 +goto yystate63 case c >= '0' && c <= ':' || c >= 'A' && c <= 'Q' || c >= 'S' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate23 } -yystate52: +yystate63: c = lexer.getChar() switch { default: -goto yyrule18 +goto yyrule20 case c == 'M': -goto yystate53 +goto yystate64 case c >= '0' && c <= ':' || c >= 'A' && c <= 'L' || c >= 'N' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate23 } -yystate53: +yystate64: c = lexer.getChar() switch { default: -goto yyrule18 +goto yyrule20 case c == 'A': -goto yystate54 +goto yystate65 case c >= '0' && c <= ':' || c >= 'B' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate23 } -yystate54: +yystate65: c = lexer.getChar() switch { default: -goto yyrule18 +goto yyrule20 case c == 'N': -goto yystate55 +goto yystate66 case c >= '0' && c <= ':' || c >= 'A' && c <= 'M' || c >= 'O' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate23 } -yystate55: +yystate66: c = lexer.getChar() switch { default: -goto yyrule18 +goto yyrule20 case c == 'E': -goto yystate56 +goto yystate67 case c >= '0' && c <= ':' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate23 } -yystate56: +yystate67: c = lexer.getChar() switch { default: -goto yyrule18 +goto yyrule20 case c == 'N': -goto yystate57 +goto yystate68 case c >= '0' && c <= ':' || c >= 'A' && c <= 'M' || c >= 'O' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate23 } -yystate57: +yystate68: c = lexer.getChar() switch { default: -goto yyrule18 +goto yyrule20 case c == 'T': -goto yystate58 +goto yystate69 case c >= '0' && c <= ':' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate23 } -yystate58: +yystate69: +c = lexer.getChar() +switch { +default: +goto yyrule11 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': +goto yystate23 +} + +yystate70: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'U': +goto yystate71 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'T' || c >= 'V' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': +goto yystate23 +} + +yystate71: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'M': +goto yystate72 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'L' || c >= 'N' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': +goto yystate23 +} + +yystate72: +c = lexer.getChar() +switch { +default: +goto yyrule13 +case c == 'M': +goto yystate73 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'L' || c >= 'N' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': +goto yystate23 +} + +yystate73: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'A': +goto yystate74 +case c >= '0' && c <= ':' || c >= 'B' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': +goto yystate23 +} + +yystate74: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'R': +goto yystate75 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Q' || c >= 'S' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': +goto yystate23 +} + +yystate75: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'Y': +goto yystate76 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'X' || c == 'Z' || c == '_' || c >= 'a' && c <= 'z': +goto yystate23 +} + +yystate76: c = lexer.getChar() switch { default: @@ -660,62 +873,40 @@ case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c < goto yystate23 } -yystate59: +yystate77: c = lexer.getChar() switch { default: -goto yyrule18 -case c == 'U': -goto yystate60 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'T' || c >= 'V' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': -goto yystate23 -} - -yystate60: -c = lexer.getChar() -switch { -default: -goto yyrule18 -case c == 'M': -goto yystate34 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'L' || c >= 'N' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': -goto yystate23 -} - -yystate61: -c = lexer.getChar() -switch { -default: -goto yyrule18 +goto yyrule20 case c == 'I': -goto yystate62 +goto yystate78 case c >= '0' && c <= ':' || c >= 'A' && c <= 'H' || c >= 'J' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate23 } -yystate62: +yystate78: c = lexer.getChar() switch { default: -goto yyrule18 +goto yyrule20 case c == 'T': -goto yystate63 +goto yystate79 case c >= '0' && c <= ':' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate23 } -yystate63: +yystate79: c = lexer.getChar() switch { default: -goto yyrule18 +goto yyrule20 case c == 'H': -goto yystate64 +goto yystate80 case c >= '0' && c <= ':' || c >= 'A' && c <= 'G' || c >= 'I' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate23 } -yystate64: +yystate80: c = lexer.getChar() switch { default: @@ -724,194 +915,18 @@ case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c < goto yystate23 } -yystate65: -c = lexer.getChar() -switch { -default: -goto yyrule18 -case c == 'l': -goto yystate66 -case c == 'n': -goto yystate69 -case c == 'v': -goto yystate70 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'k' || c == 'm' || c >= 'o' && c <= 'u' || c >= 'w' && c <= 'z': -goto yystate23 -} - -yystate66: -c = lexer.getChar() -switch { -default: -goto yyrule18 -case c == 'e': -goto yystate67 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': -goto yystate23 -} - -yystate67: -c = lexer.getChar() -switch { -default: -goto yyrule18 -case c == 'r': -goto yystate68 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z': -goto yystate23 -} - -yystate68: -c = lexer.getChar() -switch { -default: -goto yyrule18 -case c == 't': -goto yystate30 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': -goto yystate23 -} - -yystate69: -c = lexer.getChar() -switch { -default: -goto yyrule18 -case c == 'd': -goto yystate32 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'c' || c >= 'e' && c <= 'z': -goto yystate23 -} - -yystate70: -c = lexer.getChar() -switch { -default: -goto yyrule18 -case c == 'g': -goto yystate71 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'f' || c >= 'h' && c <= 'z': -goto yystate23 -} - -yystate71: -c = lexer.getChar() -switch { -default: -goto yyrule12 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': -goto yystate23 -} - -yystate72: -c = lexer.getChar() -switch { -default: -goto yyrule18 -case c == 'y': -goto yystate36 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'x' || c == 'z': -goto yystate23 -} - -yystate73: -c = lexer.getChar() -switch { -default: -goto yyrule18 -case c == 'o': -goto yystate74 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'n' || c >= 'p' && c <= 'z': -goto yystate23 -} - -yystate74: -c = lexer.getChar() -switch { -default: -goto yyrule18 -case c == 'u': -goto yystate75 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 't' || c >= 'v' && c <= 'z': -goto yystate23 -} - -yystate75: -c = lexer.getChar() -switch { -default: -goto yyrule18 -case c == 'n': -goto yystate76 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': -goto yystate23 -} - -yystate76: -c = lexer.getChar() -switch { -default: -goto yyrule18 -case c == 't': -goto yystate71 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': -goto yystate23 -} - -yystate77: -c = lexer.getChar() -switch { -default: -goto yyrule18 -case c == 'o': -goto yystate78 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'n' || c >= 'p' && c <= 'z': -goto yystate23 -} - -yystate78: -c = lexer.getChar() -switch { -default: -goto yyrule18 -case c == 'r': -goto yystate43 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z': -goto yystate23 -} - -yystate79: -c = lexer.getChar() -switch { -default: -goto yyrule18 -case c == 'f': -goto yystate45 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'e' || c >= 'g' && c <= 'z': -goto yystate23 -} - -yystate80: -c = lexer.getChar() -switch { -default: -goto yyrule18 -case c == 'a': -goto yystate81 -case c == 'i': -goto yystate82 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'b' && c <= 'h' || c >= 'j' && c <= 'z': -goto yystate23 -} - yystate81: c = lexer.getChar() switch { default: -goto yyrule18 -case c == 'x': -goto yystate71 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'w' || c == 'y' || c == 'z': +goto yyrule20 +case c == 'l': +goto yystate82 +case c == 'n': +goto yystate85 +case c == 'v': +goto yystate86 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'k' || c == 'm' || c >= 'o' && c <= 'u' || c >= 'w' && c <= 'z': goto yystate23 } @@ -919,10 +934,10 @@ yystate82: c = lexer.getChar() switch { default: -goto yyrule18 -case c == 'n': -goto yystate71 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': +goto yyrule20 +case c == 'e': +goto yystate83 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': goto yystate23 } @@ -930,9 +945,9 @@ yystate83: c = lexer.getChar() switch { default: -goto yyrule18 +goto yyrule20 case c == 'r': -goto yystate32 +goto yystate84 case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z': goto yystate23 } @@ -941,10 +956,10 @@ yystate84: c = lexer.getChar() switch { default: -goto yyrule18 -case c == 'e': -goto yystate85 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': +goto yyrule20 +case c == 't': +goto yystate30 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': goto yystate23 } @@ -952,10 +967,10 @@ yystate85: c = lexer.getChar() switch { default: -goto yyrule18 -case c == 'r': -goto yystate86 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z': +goto yyrule20 +case c == 'd': +goto yystate32 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'c' || c >= 'e' && c <= 'z': goto yystate23 } @@ -963,10 +978,10 @@ yystate86: c = lexer.getChar() switch { default: -goto yyrule18 -case c == 'm': +goto yyrule20 +case c == 'g': goto yystate87 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'l' || c >= 'n' && c <= 'z': +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'f' || c >= 'h' && c <= 'z': goto yystate23 } @@ -974,10 +989,8 @@ yystate87: c = lexer.getChar() switch { default: -goto yyrule18 -case c == 'a': -goto yystate88 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'b' && c <= 'z': +goto yyrule14 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate23 } @@ -985,10 +998,10 @@ yystate88: c = lexer.getChar() switch { default: -goto yyrule18 -case c == 'n': -goto yystate89 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': +goto yyrule20 +case c == 'y': +goto yystate36 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'x' || c == 'z': goto yystate23 } @@ -996,10 +1009,10 @@ yystate89: c = lexer.getChar() switch { default: -goto yyrule18 -case c == 'e': +goto yyrule20 +case c == 'o': goto yystate90 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'n' || c >= 'p' && c <= 'z': goto yystate23 } @@ -1007,10 +1020,10 @@ yystate90: c = lexer.getChar() switch { default: -goto yyrule18 -case c == 'n': +goto yyrule20 +case c == 'u': goto yystate91 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 't' || c >= 'v' && c <= 'z': goto yystate23 } @@ -1018,10 +1031,10 @@ yystate91: c = lexer.getChar() switch { default: -goto yyrule18 -case c == 't': -goto yystate58 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': +goto yyrule20 +case c == 'n': +goto yystate92 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': goto yystate23 } @@ -1029,10 +1042,10 @@ yystate92: c = lexer.getChar() switch { default: -goto yyrule18 -case c == 'u': -goto yystate93 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 't' || c >= 'v' && c <= 'z': +goto yyrule20 +case c == 't': +goto yystate87 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': goto yystate23 } @@ -1040,10 +1053,10 @@ yystate93: c = lexer.getChar() switch { default: -goto yyrule18 -case c == 'm': -goto yystate71 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'l' || c >= 'n' && c <= 'z': +goto yyrule20 +case c == 'e': +goto yystate94 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': goto yystate23 } @@ -1051,10 +1064,10 @@ yystate94: c = lexer.getChar() switch { default: -goto yyrule18 -case c == 'i': +goto yyrule20 +case c == 's': goto yystate95 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'h' || c >= 'j' && c <= 'z': +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'r' || c >= 't' && c <= 'z': goto yystate23 } @@ -1062,10 +1075,10 @@ yystate95: c = lexer.getChar() switch { default: -goto yyrule18 -case c == 't': +goto yyrule20 +case c == 'c': goto yystate96 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c == 'a' || c == 'b' || c >= 'd' && c <= 'z': goto yystate23 } @@ -1073,41 +1086,373 @@ yystate96: c = lexer.getChar() switch { default: -goto yyrule18 -case c == 'h': -goto yystate64 -case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'g' || c >= 'i' && c <= 'z': +goto yyrule20 +case c == 'r': +goto yystate97 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z': goto yystate23 } -goto yystate97 // silence unused label error yystate97: c = lexer.getChar() -yystart97: switch { default: -goto yyabort -case c == '*': -goto yystate99 -case c >= '\x01' && c <= ')' || c >= '+' && c <= 'ÿ': +goto yyrule20 +case c == 'i': goto yystate98 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'h' || c >= 'j' && c <= 'z': +goto yystate23 } yystate98: c = lexer.getChar() -goto yyrule3 +switch { +default: +goto yyrule20 +case c == 'p': +goto yystate99 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'o' || c >= 'q' && c <= 'z': +goto yystate23 +} yystate99: c = lexer.getChar() switch { default: -goto yyrule3 -case c == '/': +goto yyrule20 +case c == 't': goto yystate100 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': +goto yystate23 } yystate100: c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'i': +goto yystate101 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'h' || c >= 'j' && c <= 'z': +goto yystate23 +} + +yystate101: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'o': +goto yystate102 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'n' || c >= 'p' && c <= 'z': +goto yystate23 +} + +yystate102: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'n': +goto yystate51 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': +goto yystate23 +} + +yystate103: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'o': +goto yystate104 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'n' || c >= 'p' && c <= 'z': +goto yystate23 +} + +yystate104: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'r': +goto yystate54 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z': +goto yystate23 +} + +yystate105: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'f': +goto yystate56 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'e' || c >= 'g' && c <= 'z': +goto yystate23 +} + +yystate106: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'a': +goto yystate107 +case c == 'i': +goto yystate108 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'b' && c <= 'h' || c >= 'j' && c <= 'z': +goto yystate23 +} + +yystate107: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'x': +goto yystate87 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'w' || c == 'y' || c == 'z': +goto yystate23 +} + +yystate108: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'n': +goto yystate87 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': +goto yystate23 +} + +yystate109: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'r': +goto yystate32 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z': +goto yystate23 +} + +yystate110: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'e': +goto yystate111 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': +goto yystate23 +} + +yystate111: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'r': +goto yystate112 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z': +goto yystate23 +} + +yystate112: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'm': +goto yystate113 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'l' || c >= 'n' && c <= 'z': +goto yystate23 +} + +yystate113: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'a': +goto yystate114 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'b' && c <= 'z': +goto yystate23 +} + +yystate114: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'n': +goto yystate115 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': +goto yystate23 +} + +yystate115: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'e': +goto yystate116 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': +goto yystate23 +} + +yystate116: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'n': +goto yystate117 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': +goto yystate23 +} + +yystate117: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 't': +goto yystate69 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': +goto yystate23 +} + +yystate118: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'u': +goto yystate119 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 't' || c >= 'v' && c <= 'z': +goto yystate23 +} + +yystate119: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'm': +goto yystate120 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'l' || c >= 'n' && c <= 'z': +goto yystate23 +} + +yystate120: +c = lexer.getChar() +switch { +default: +goto yyrule14 +case c == 'm': +goto yystate121 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'l' || c >= 'n' && c <= 'z': +goto yystate23 +} + +yystate121: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'a': +goto yystate122 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'b' && c <= 'z': +goto yystate23 +} + +yystate122: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'r': +goto yystate123 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z': +goto yystate23 +} + +yystate123: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'y': +goto yystate76 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'x' || c == 'z': +goto yystate23 +} + +yystate124: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'i': +goto yystate125 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'h' || c >= 'j' && c <= 'z': +goto yystate23 +} + +yystate125: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 't': +goto yystate126 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': +goto yystate23 +} + +yystate126: +c = lexer.getChar() +switch { +default: +goto yyrule20 +case c == 'h': +goto yystate80 +case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'g' || c >= 'i' && c <= 'z': +goto yystate23 +} + +goto yystate127 // silence unused label error +yystate127: +c = lexer.getChar() +yystart127: +switch { +default: +goto yyabort +case c == '*': +goto yystate129 +case c >= '\x01' && c <= ')' || c >= '+' && c <= 'ÿ': +goto yystate128 +} + +yystate128: +c = lexer.getChar() +goto yyrule3 + +yystate129: +c = lexer.getChar() +switch { +default: +goto yyrule3 +case c == '/': +goto yystate130 +} + +yystate130: +c = lexer.getChar() goto yyrule2 yyrule1: // "/*" @@ -1146,55 +1491,63 @@ yyrule8: // WITH|with { return WITH } -yyrule9: // PERMANENT|permanent +yyrule9: // SUMMARY|summary +{ + return SUMMARY +} +yyrule10: // DESCRIPTION|description +{ + return DESCRIPTION +} +yyrule11: // PERMANENT|permanent { return PERMANENT } -yyrule10: // BY|by +yyrule12: // BY|by { return GROUP_OP } -yyrule11: // AVG|SUM|MAX|MIN|COUNT +yyrule13: // AVG|SUM|MAX|MIN|COUNT { lval.str = lexer.token(); return AGGR_OP goto yystate0 } -yyrule12: // avg|sum|max|min|count +yyrule14: // avg|sum|max|min|count { lval.str = strings.ToUpper(lexer.token()); return AGGR_OP goto yystate0 } -yyrule13: // \<|>|AND|OR|and|or +yyrule15: // \<|>|AND|OR|and|or { lval.str = strings.ToUpper(lexer.token()); return CMP_OP goto yystate0 } -yyrule14: // ==|!=|>=|<= +yyrule16: // ==|!=|>=|<= { lval.str = lexer.token(); return CMP_OP goto yystate0 } -yyrule15: // [+\-] +yyrule17: // [+\-] { lval.str = lexer.token(); return ADDITIVE_OP goto yystate0 } -yyrule16: // [*/%] +yyrule18: // [*/%] { lval.str = lexer.token(); return MULT_OP goto yystate0 } -yyrule17: // {D}+{U} +yyrule19: // {D}+{U} { lval.str = lexer.token(); return DURATION goto yystate0 } -yyrule18: // {L}({L}|{D})* +yyrule20: // {L}({L}|{D})* { lval.str = lexer.token(); return IDENTIFIER goto yystate0 } -yyrule19: // \-?{D}+(\.{D}*)? +yyrule21: // \-?{D}+(\.{D}*)? { num, err := strconv.ParseFloat(lexer.token(), 64); if (err != nil && err.(*strconv.NumError).Err == strconv.ErrSyntax) { @@ -1203,21 +1556,21 @@ yyrule19: // \-?{D}+(\.{D}*)? lval.num = clientmodel.SampleValue(num) return NUMBER } -yyrule20: // \"(\\.|[^\\"])*\" +yyrule22: // \"(\\.|[^\\"])*\" { lval.str = lexer.token()[1:len(lexer.token()) - 1]; return STRING goto yystate0 } -yyrule21: // \'(\\.|[^\\'])*\' +yyrule23: // \'(\\.|[^\\'])*\' { lval.str = lexer.token()[1:len(lexer.token()) - 1]; return STRING goto yystate0 } -yyrule22: // [{}\[\]()=,] +yyrule24: // [{}\[\]()=,] { return int(lexer.buf[0]) } -yyrule23: // [\t\n\r ] +yyrule25: // [\t\n\r ] { /* gobble up any whitespace */ goto yystate0 diff --git a/rules/manager.go b/rules/manager.go index 3ac6a09c9..055f2aeb7 100644 --- a/rules/manager.go +++ b/rules/manager.go @@ -38,24 +38,36 @@ type RuleManager interface { AlertingRules() []*AlertingRule } +// A request for sending an alert notification to the alert manager. This needs +// to be defined in this package to prevent a circular import between +// rules<->notification. +type NotificationReq struct { + Rule *AlertingRule + ActiveAlert Alert +} + +type NotificationReqs []*NotificationReq + type ruleManager struct { // Protects the rules list. sync.Mutex rules []Rule - results chan<- *extraction.Result - done chan bool - interval time.Duration - storage *metric.TieredStorage + results chan<- *extraction.Result + notifications chan<- NotificationReqs + done chan bool + interval time.Duration + storage *metric.TieredStorage } -func NewRuleManager(results chan<- *extraction.Result, interval time.Duration, storage *metric.TieredStorage) RuleManager { +func NewRuleManager(results chan<- *extraction.Result, notifications chan<- NotificationReqs, interval time.Duration, storage *metric.TieredStorage) RuleManager { manager := &ruleManager{ - results: results, - rules: []Rule{}, - done: make(chan bool), - interval: interval, - storage: storage, + results: results, + notifications: notifications, + rules: []Rule{}, + done: make(chan bool), + interval: interval, + storage: storage, } return manager } @@ -84,6 +96,27 @@ func (m *ruleManager) Stop() { } } +func (m *ruleManager) queueAlertNotifications(rule *AlertingRule) { + activeAlerts := rule.ActiveAlerts() + if len(activeAlerts) == 0 { + return + } + + notifications := make(NotificationReqs, 0, len(activeAlerts)) + for _, aa := range activeAlerts { + if aa.State != FIRING { + // BUG: In the future, make AlertManager support pending alerts? + continue + } + + notifications = append(notifications, &NotificationReq{ + Rule: rule, + ActiveAlert: aa, + }) + } + m.notifications <- notifications +} + func (m *ruleManager) runIteration(results chan<- *extraction.Result) { now := time.Now() wg := sync.WaitGroup{} @@ -105,6 +138,10 @@ func (m *ruleManager) runIteration(results chan<- *extraction.Result) { Samples: samples, Err: err, } + + if alertingRule, ok := rule.(*AlertingRule); ok { + m.queueAlertNotifications(alertingRule) + } }(rule) } diff --git a/rules/parser.y b/rules/parser.y index c579df75a..2e265f706 100644 --- a/rules/parser.y +++ b/rules/parser.y @@ -41,7 +41,7 @@ %token NUMBER %token PERMANENT GROUP_OP %token AGGR_OP CMP_OP ADDITIVE_OP MULT_OP -%token ALERT IF FOR WITH +%token ALERT IF FOR WITH SUMMARY DESCRIPTION %type func_arg_list %type label_list grouping_opts @@ -75,9 +75,9 @@ rules_stat : qualifier IDENTIFIER rule_labels '=' rule_expr if err != nil { yylex.Error(err.Error()); return 1 } yylex.(*RulesLexer).parsedRules = append(yylex.(*RulesLexer).parsedRules, rule) } - | ALERT IDENTIFIER IF rule_expr for_duration WITH rule_labels + | ALERT IDENTIFIER IF rule_expr for_duration WITH rule_labels SUMMARY STRING DESCRIPTION STRING { - rule, err := CreateAlertingRule($2, $4, $5, $7) + rule, err := CreateAlertingRule($2, $4, $5, $7, $9, $11) if err != nil { yylex.Error(err.Error()); return 1 } yylex.(*RulesLexer).parsedRules = append(yylex.(*RulesLexer).parsedRules, rule) } diff --git a/rules/parser.y.go b/rules/parser.y.go index c8dbad4d4..d33cb7941 100644 --- a/rules/parser.y.go +++ b/rules/parser.y.go @@ -38,6 +38,8 @@ const ALERT = 57358 const IF = 57359 const FOR = 57360 const WITH = 57361 +const SUMMARY = 57362 +const DESCRIPTION = 57363 var yyToknames = []string{ "START_RULES", @@ -56,6 +58,8 @@ var yyToknames = []string{ "IF", "FOR", "WITH", + "SUMMARY", + "DESCRIPTION", " =", } var yyStatenames = []string{} @@ -83,35 +87,37 @@ const yyPrivate = 57344 var yyTokenNames []string var yyStates []string -const yyLast = 97 +const yyLast = 101 var yyAct = []int{ - 20, 38, 34, 17, 33, 43, 6, 67, 15, 66, - 19, 18, 16, 17, 15, 45, 59, 44, 60, 27, - 28, 29, 16, 17, 15, 41, 40, 18, 16, 17, - 18, 16, 17, 22, 15, 23, 21, 46, 47, 49, - 15, 8, 35, 15, 10, 51, 22, 9, 50, 53, - 52, 48, 61, 57, 18, 16, 17, 39, 8, 7, - 32, 10, 65, 42, 9, 56, 30, 15, 8, 35, - 54, 10, 62, 37, 9, 14, 7, 26, 68, 64, - 39, 13, 25, 24, 2, 3, 7, 11, 5, 4, - 1, 58, 12, 36, 55, 63, 31, + 20, 38, 34, 43, 33, 17, 6, 18, 16, 17, + 19, 15, 59, 18, 16, 17, 16, 17, 15, 27, + 28, 29, 15, 60, 23, 41, 40, 49, 15, 22, + 15, 8, 35, 67, 10, 66, 45, 9, 44, 50, + 18, 16, 17, 46, 47, 51, 18, 16, 17, 53, + 52, 7, 32, 57, 30, 15, 39, 8, 35, 48, + 10, 15, 65, 9, 8, 22, 71, 10, 21, 68, + 9, 61, 42, 14, 37, 56, 62, 7, 26, 13, + 72, 70, 54, 69, 7, 64, 39, 25, 24, 2, + 3, 11, 5, 4, 1, 58, 12, 36, 55, 63, + 31, } var yyPact = []int{ - 80, -1000, -1000, 52, 65, -1000, 17, 52, 12, 11, - -1000, -1000, 77, 76, -1000, 69, 52, 52, 52, 41, - -1000, 35, 51, 52, 25, 46, -22, -12, -18, 8, - -1000, -8, -1000, -1000, 17, -1000, 15, -1000, -1000, 31, - 14, 28, 52, -1000, -1000, 62, -1000, 74, 63, 54, - 52, -2, -1000, -1000, -1000, -1000, -6, 17, 33, 64, - 73, 25, -1000, -16, -1000, -1000, -1000, 72, -1000, + 85, -1000, -1000, 58, 63, -1000, 33, 58, 42, -2, + -1000, -1000, 82, 81, -1000, 70, 58, 58, 58, 27, + -1000, 25, 50, 58, 6, 55, -26, -10, -17, 2, + -1000, 11, -1000, -1000, 33, -1000, 19, -1000, -1000, 37, + 0, 17, 58, -1000, -1000, 51, -1000, 80, 75, 64, + 58, -6, -1000, -1000, -1000, -1000, -3, 33, 52, 68, + 79, 6, -1000, 8, -1000, 49, -1000, 77, 74, -1000, + 45, 73, -1000, } var yyPgo = []int{ - 0, 96, 95, 94, 1, 93, 0, 2, 4, 92, - 91, 90, 89, 88, 87, + 0, 100, 99, 98, 1, 97, 0, 2, 4, 96, + 95, 94, 93, 92, 91, } var yyR1 = []int{ @@ -122,20 +128,21 @@ var yyR1 = []int{ } var yyR2 = []int{ - 0, 2, 2, 0, 2, 1, 5, 7, 0, 2, + 0, 2, 2, 0, 2, 1, 5, 11, 0, 2, 0, 1, 0, 3, 2, 1, 3, 3, 3, 2, 4, 3, 4, 5, 3, 3, 3, 1, 0, 4, 1, 3, 1, 3, 1, 1, } var yyChk = []int{ - -1000, -11, 4, 5, -12, -13, -7, 24, 6, 12, - 9, -14, -9, 16, 10, 26, 14, 15, 13, -7, - -6, 24, 21, 24, 6, 6, 8, -7, -7, -7, - 25, -1, 25, -8, -7, 7, -5, 22, -4, 6, - -7, -6, 17, 27, 25, 23, 22, 23, 20, 25, - 20, -7, -8, -4, 7, -3, 11, -7, -10, 18, - 24, 19, 8, -2, 6, -6, 25, 23, 6, + -1000, -11, 4, 5, -12, -13, -7, 26, 6, 12, + 9, -14, -9, 16, 10, 28, 14, 15, 13, -7, + -6, 26, 23, 26, 6, 6, 8, -7, -7, -7, + 27, -1, 27, -8, -7, 7, -5, 24, -4, 6, + -7, -6, 17, 29, 27, 25, 24, 25, 22, 27, + 22, -7, -8, -4, 7, -3, 11, -7, -10, 18, + 26, 19, 8, -2, 6, -6, 27, 25, 20, 6, + 7, 21, 7, } var yyDef = []int{ @@ -145,7 +152,8 @@ var yyDef = []int{ 18, 0, 21, 32, 34, 35, 0, 14, 15, 0, 0, 0, 0, 22, 20, 0, 13, 0, 0, 28, 0, 8, 33, 16, 17, 23, 0, 6, 0, 0, - 0, 12, 9, 0, 30, 7, 29, 0, 31, + 0, 12, 9, 0, 30, 0, 29, 0, 0, 31, + 0, 0, 7, } var yyTok1 = []int{ @@ -153,20 +161,20 @@ var yyTok1 = []int{ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 24, 25, 3, 3, 23, 3, 3, 3, 3, 3, + 26, 27, 3, 3, 25, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 20, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 22, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 26, 3, 27, 3, 3, 3, 3, 3, 3, + 3, 28, 3, 29, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 21, 3, 22, + 3, 3, 3, 23, 3, 24, } var yyTok2 = []int{ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, - 12, 13, 14, 15, 16, 17, 18, 19, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, } var yyTok3 = []int{ 0, @@ -410,7 +418,7 @@ yydefault: case 7: //line parser.y:79 { - rule, err := CreateAlertingRule(yyS[yypt-5].str, yyS[yypt-3].ruleNode, yyS[yypt-2].str, yyS[yypt-0].labelSet) + rule, err := CreateAlertingRule(yyS[yypt-9].str, yyS[yypt-7].ruleNode, yyS[yypt-6].str, yyS[yypt-4].labelSet, yyS[yypt-2].str, yyS[yypt-0].str) if err != nil { yylex.Error(err.Error()); return 1 } yylex.(*RulesLexer).parsedRules = append(yylex.(*RulesLexer).parsedRules, rule) } diff --git a/rules/rules_test.go b/rules/rules_test.go index 502f40d56..87e276e6c 100644 --- a/rules/rules_test.go +++ b/rules/rules_test.go @@ -511,18 +511,18 @@ func TestAlertingRule(t *testing.T) { // Labels in expected output need to be alphabetically sorted. var evalOutputs = [][]string{ { - `ALERTS{alertname="HttpRequestRateLow", alertstate="pending", group="canary", instance="0", job="app-server"} => 1 @[%v]`, - `ALERTS{alertname="HttpRequestRateLow", alertstate="pending", group="canary", instance="1", job="app-server"} => 1 @[%v]`, + `ALERTS{alertname="HttpRequestRateLow", alertstate="pending", group="canary", instance="0", job="app-server", severity="critical"} => 1 @[%v]`, + `ALERTS{alertname="HttpRequestRateLow", alertstate="pending", group="canary", instance="1", job="app-server", severity="critical"} => 1 @[%v]`, }, { - `ALERTS{alertname="HttpRequestRateLow", alertstate="pending", group="canary", instance="0", job="app-server"} => 0 @[%v]`, - `ALERTS{alertname="HttpRequestRateLow", alertstate="firing", group="canary", instance="0", job="app-server"} => 1 @[%v]`, - `ALERTS{alertname="HttpRequestRateLow", alertstate="pending", group="canary", instance="1", job="app-server"} => 0 @[%v]`, - `ALERTS{alertname="HttpRequestRateLow", alertstate="firing", group="canary", instance="1", job="app-server"} => 1 @[%v]`, + `ALERTS{alertname="HttpRequestRateLow", alertstate="pending", group="canary", instance="0", job="app-server", severity="critical"} => 0 @[%v]`, + `ALERTS{alertname="HttpRequestRateLow", alertstate="firing", group="canary", instance="0", job="app-server", severity="critical"} => 1 @[%v]`, + `ALERTS{alertname="HttpRequestRateLow", alertstate="pending", group="canary", instance="1", job="app-server", severity="critical"} => 0 @[%v]`, + `ALERTS{alertname="HttpRequestRateLow", alertstate="firing", group="canary", instance="1", job="app-server", severity="critical"} => 1 @[%v]`, }, { - `ALERTS{alertname="HttpRequestRateLow", alertstate="firing", group="canary", instance="1", job="app-server"} => 0 @[%v]`, - `ALERTS{alertname="HttpRequestRateLow", alertstate="firing", group="canary", instance="0", job="app-server"} => 0 @[%v]`, + `ALERTS{alertname="HttpRequestRateLow", alertstate="firing", group="canary", instance="1", job="app-server", severity="critical"} => 0 @[%v]`, + `ALERTS{alertname="HttpRequestRateLow", alertstate="firing", group="canary", instance="0", job="app-server", severity="critical"} => 0 @[%v]`, }, { /* empty */ @@ -542,9 +542,9 @@ func TestAlertingRule(t *testing.T) { } alertName := "HttpRequestRateLow" alertLabels := clientmodel.LabelSet{ - "summary": "HTTP request rate is low", + "severity": "critical", } - rule := NewAlertingRule(alertName, alertExpr.(ast.VectorNode), time.Minute, alertLabels) + rule := NewAlertingRule(alertName, alertExpr.(ast.VectorNode), time.Minute, alertLabels, "summary", "description") for i, expected := range evalOutputs { evalTime := testStartTime.Add(testSampleInterval * time.Duration(i)) diff --git a/utility/deadline_client.go b/utility/deadline_client.go new file mode 100644 index 000000000..782c66ec5 --- /dev/null +++ b/utility/deadline_client.go @@ -0,0 +1,43 @@ +// Copyright 2013 Prometheus Team +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utility + +import ( + "net" + "net/http" + "time" +) + +// NewDeadlineClient returns a new http.Client which will time out long running +// requests. +func NewDeadlineClient(timeout time.Duration) http.Client { + return http.Client{ + Transport: &http.Transport{ + // We need to disable keepalive, becasue we set a deadline on the + // underlying connection. + DisableKeepAlives: true, + Dial: func(netw, addr string) (c net.Conn, err error) { + start := time.Now() + + c, err = net.DialTimeout(netw, addr, timeout) + + if err == nil { + c.SetDeadline(start.Add(timeout)) + } + + return + }, + }, + } +} diff --git a/web/api/query.go b/web/api/query.go index 3ce26049b..43f16ec6e 100644 --- a/web/api/query.go +++ b/web/api/query.go @@ -48,7 +48,7 @@ func (serv MetricsService) Query(expr string, asText string) string { rb := serv.ResponseBuilder() serv.setAccessControlHeaders(rb) var format ast.OutputFormat - // BUG(julius): Use Content-Type negotiation. + // BUG(julius): Use Content-Type negotiation. if asText == "" { format = ast.JSON rb.SetContentType(gorest.Application_Json)