Skip to content

Commit

Permalink
cmd/scollector: fastly status.io monitoring and status.io lib
Browse files Browse the repository at this point in the history
  • Loading branch information
kylebrandt committed May 13, 2016
1 parent 62aa253 commit cc784ff
Show file tree
Hide file tree
Showing 8 changed files with 687 additions and 0 deletions.
92 changes: 92 additions & 0 deletions cmd/scollector/collectors/fastly.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import (
"net/http"
"net/url"
"reflect"
"regexp"
"strconv"
"time"

"bosun.org/cmd/scollector/collectors/statusio"
"bosun.org/cmd/scollector/conf"
"bosun.org/metadata"
"bosun.org/opentsdb"
Expand All @@ -34,6 +36,15 @@ func init() {
name: "c_fastly_billing",
Interval: time.Minute * 5,
})
if f.StatusBaseAddr != "" {
collectors = append(collectors, &IntervalCollector{
F: func() (opentsdb.MultiDataPoint, error) {
return c_fastly_status(f.StatusBaseAddr)
},
name: "c_fastly_status",
Interval: time.Minute * 1,
})
}
}
})
}
Expand All @@ -50,8 +61,89 @@ const (
fastlyBillingBeforeDiscountDesc = "The total incurred cost plus extras cost this month."
fastlyBillingDiscountDesc = "The calculated discount rate this month."
fastlyBillingCostDesc = "The final amount to be paid this month."

fastlyStatusPrefix = "fastly.status."
fastlyComponentStatusDesc = "The current status of the %v. 0: Operational, 1: Degraded Performance, 2: Partial Outage, 3: Major Outage." // see iota for statusio.ComponentStatus
fastlyScheduledMaintDesc = "The number of currently scheduled maintenances. Does not include maintenance that is current active"
fastlyActiveScheduledMaintDesc = "The number of currently scheduled maintenances currently in progress. Includes the 'in_progress' and 'verifying'"
fastlyActiveIncidentDesc = "The number of currently active incidents. Includes the 'investingating', 'identified', and 'monitoring' states."
)

var (
fastlyStatusPopRegex = regexp.MustCompile(`(.*)\(([A-Z]{3})\)`)
)

func c_fastly_status(baseAddr string) (opentsdb.MultiDataPoint, error) {
var md opentsdb.MultiDataPoint
c := statusio.NewClient(baseAddr)
summary, err := c.GetSummary()
if err != nil {
return md, err
}

// Process Components (Pops, Support Systems)
for _, comp := range summary.Components {
match := fastlyStatusPopRegex.FindAllStringSubmatch(comp.Name, 1)
if len(match) != 0 && len(match[0]) == 3 { // We have a pop
//name := match[0][1]
code := match[0][2]
tagSet := opentsdb.TagSet{"code": code}
Add(&md, fastlyStatusPrefix+"pop", int(comp.Status), tagSet, metadata.Gauge, metadata.StatusCode, fmt.Sprintf(fastlyComponentStatusDesc, "pop"))
continue
}
// Must be service component
tagSet := opentsdb.TagSet{"service": comp.Name}
Add(&md, fastlyStatusPrefix+"service", int(comp.Status), tagSet, metadata.Gauge, metadata.StatusCode, fmt.Sprintf(fastlyComponentStatusDesc, "service"))
}

// Scheduled Maintenance
scheduledMaintByImpact := make(map[statusio.StatusIndicator]int)
activeScheduledMaintByImpact := make(map[statusio.StatusIndicator]int)
// Make Maps
for _, si := range statusio.StatusIndicatorValues {
scheduledMaintByImpact[si] = 0
activeScheduledMaintByImpact[si] = 0
}
// Group by scheduled vs inprogress/verifying
for _, maint := range summary.ScheduledMaintenances {
switch maint.Status {
case statusio.Scheduled:
scheduledMaintByImpact[maint.Impact]++
case statusio.InProgress, statusio.Verifying:
activeScheduledMaintByImpact[maint.Impact]++
}
}
for impact, count := range scheduledMaintByImpact {
tagSet := opentsdb.TagSet{"impact": fmt.Sprint(impact)}
Add(&md, fastlyStatusPrefix+"scheduled_maint_count", count, tagSet, metadata.Gauge, metadata.Count, fastlyScheduledMaintDesc)
}
for impact, count := range activeScheduledMaintByImpact {
tagSet := opentsdb.TagSet{"impact": fmt.Sprint(impact)}
Add(&md, fastlyStatusPrefix+"in_progress_maint_count", count, tagSet, metadata.Gauge, metadata.Count, fastlyActiveScheduledMaintDesc)
}

// Incidents
// Make Map
incidentsByImpact := make(map[statusio.StatusIndicator]int)
for _, si := range statusio.StatusIndicatorValues {
incidentsByImpact[si] = 0
}
for _, incident := range summary.Incidents {
switch incident.Status {
case statusio.Investigating, statusio.Identified, statusio.Monitoring:
incidentsByImpact[incident.Impact]++
default:
continue
}
}
for impact, count := range incidentsByImpact {
tagSet := opentsdb.TagSet{"impact": fmt.Sprint(impact)}
Add(&md, fastlyStatusPrefix+"active_incident_count", count, tagSet, metadata.Gauge, metadata.Incident, fastlyActiveIncidentDesc)
}

return md, nil
}

func c_fastly_billing(c fastlyClient) (opentsdb.MultiDataPoint, error) {
var md opentsdb.MultiDataPoint
now := time.Now().UTC()
Expand Down
62 changes: 62 additions & 0 deletions cmd/scollector/collectors/statusio/componentstatus_jsonenums.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// generated by jsonenums -type=ComponentStatus; DO NOT EDIT

package statusio

import (
"encoding/json"
"fmt"
)

var (
_ComponentStatusNameToValue = map[string]ComponentStatus{
"Operational": Operational,
"DegradedPerformance": DegradedPerformance,
"PartialOutage": PartialOutage,
"MajorOutage": MajorOutage,
}

_ComponentStatusValueToName = map[ComponentStatus]string{
Operational: "Operational",
DegradedPerformance: "DegradedPerformance",
PartialOutage: "PartialOutage",
MajorOutage: "MajorOutage",
}
)

func init() {
var v ComponentStatus
if _, ok := interface{}(v).(fmt.Stringer); ok {
_ComponentStatusNameToValue = map[string]ComponentStatus{
interface{}(Operational).(fmt.Stringer).String(): Operational,
interface{}(DegradedPerformance).(fmt.Stringer).String(): DegradedPerformance,
interface{}(PartialOutage).(fmt.Stringer).String(): PartialOutage,
interface{}(MajorOutage).(fmt.Stringer).String(): MajorOutage,
}
}
}

// MarshalJSON is generated so ComponentStatus satisfies json.Marshaler.
func (r ComponentStatus) MarshalJSON() ([]byte, error) {
if s, ok := interface{}(r).(fmt.Stringer); ok {
return json.Marshal(s.String())
}
s, ok := _ComponentStatusValueToName[r]
if !ok {
return nil, fmt.Errorf("invalid ComponentStatus: %d", r)
}
return json.Marshal(s)
}

// UnmarshalJSON is generated so ComponentStatus satisfies json.Unmarshaler.
func (r *ComponentStatus) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return fmt.Errorf("ComponentStatus should be a string, got %s", data)
}
v, ok := _ComponentStatusNameToValue[s]
if !ok {
return fmt.Errorf("invalid ComponentStatus %q", s)
}
*r = v
return nil
}
65 changes: 65 additions & 0 deletions cmd/scollector/collectors/statusio/incidentstatus_jsonenums.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// generated by jsonenums -type=IncidentStatus; DO NOT EDIT

package statusio

import (
"encoding/json"
"fmt"
)

var (
_IncidentStatusNameToValue = map[string]IncidentStatus{
"Investigating": Investigating,
"Identified": Identified,
"Monitoring": Monitoring,
"Resolved": Resolved,
"PostMortem": PostMortem,
}

_IncidentStatusValueToName = map[IncidentStatus]string{
Investigating: "Investigating",
Identified: "Identified",
Monitoring: "Monitoring",
Resolved: "Resolved",
PostMortem: "PostMortem",
}
)

func init() {
var v IncidentStatus
if _, ok := interface{}(v).(fmt.Stringer); ok {
_IncidentStatusNameToValue = map[string]IncidentStatus{
interface{}(Investigating).(fmt.Stringer).String(): Investigating,
interface{}(Identified).(fmt.Stringer).String(): Identified,
interface{}(Monitoring).(fmt.Stringer).String(): Monitoring,
interface{}(Resolved).(fmt.Stringer).String(): Resolved,
interface{}(PostMortem).(fmt.Stringer).String(): PostMortem,
}
}
}

// MarshalJSON is generated so IncidentStatus satisfies json.Marshaler.
func (r IncidentStatus) MarshalJSON() ([]byte, error) {
if s, ok := interface{}(r).(fmt.Stringer); ok {
return json.Marshal(s.String())
}
s, ok := _IncidentStatusValueToName[r]
if !ok {
return nil, fmt.Errorf("invalid IncidentStatus: %d", r)
}
return json.Marshal(s)
}

// UnmarshalJSON is generated so IncidentStatus satisfies json.Unmarshaler.
func (r *IncidentStatus) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return fmt.Errorf("IncidentStatus should be a string, got %s", data)
}
v, ok := _IncidentStatusNameToValue[s]
if !ok {
return fmt.Errorf("invalid IncidentStatus %q", s)
}
*r = v
return nil
}
62 changes: 62 additions & 0 deletions cmd/scollector/collectors/statusio/maintenancestatus_jsonenums.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// generated by jsonenums -type=MaintenanceStatus; DO NOT EDIT

package statusio

import (
"encoding/json"
"fmt"
)

var (
_MaintenanceStatusNameToValue = map[string]MaintenanceStatus{
"Scheduled": Scheduled,
"InProgress": InProgress,
"Verifying": Verifying,
"Completed": Completed,
}

_MaintenanceStatusValueToName = map[MaintenanceStatus]string{
Scheduled: "Scheduled",
InProgress: "InProgress",
Verifying: "Verifying",
Completed: "Completed",
}
)

func init() {
var v MaintenanceStatus
if _, ok := interface{}(v).(fmt.Stringer); ok {
_MaintenanceStatusNameToValue = map[string]MaintenanceStatus{
interface{}(Scheduled).(fmt.Stringer).String(): Scheduled,
interface{}(InProgress).(fmt.Stringer).String(): InProgress,
interface{}(Verifying).(fmt.Stringer).String(): Verifying,
interface{}(Completed).(fmt.Stringer).String(): Completed,
}
}
}

// MarshalJSON is generated so MaintenanceStatus satisfies json.Marshaler.
func (r MaintenanceStatus) MarshalJSON() ([]byte, error) {
if s, ok := interface{}(r).(fmt.Stringer); ok {
return json.Marshal(s.String())
}
s, ok := _MaintenanceStatusValueToName[r]
if !ok {
return nil, fmt.Errorf("invalid MaintenanceStatus: %d", r)
}
return json.Marshal(s)
}

// UnmarshalJSON is generated so MaintenanceStatus satisfies json.Unmarshaler.
func (r *MaintenanceStatus) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return fmt.Errorf("MaintenanceStatus should be a string, got %s", data)
}
v, ok := _MaintenanceStatusNameToValue[s]
if !ok {
return fmt.Errorf("invalid MaintenanceStatus %q", s)
}
*r = v
return nil
}
62 changes: 62 additions & 0 deletions cmd/scollector/collectors/statusio/statusindicator_jsonenums.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// generated by jsonenums -type=StatusIndicator; DO NOT EDIT

package statusio

import (
"encoding/json"
"fmt"
)

var (
_StatusIndicatorNameToValue = map[string]StatusIndicator{
"None": None,
"Minor": Minor,
"Major": Major,
"Critical": Critical,
}

_StatusIndicatorValueToName = map[StatusIndicator]string{
None: "None",
Minor: "Minor",
Major: "Major",
Critical: "Critical",
}
)

func init() {
var v StatusIndicator
if _, ok := interface{}(v).(fmt.Stringer); ok {
_StatusIndicatorNameToValue = map[string]StatusIndicator{
interface{}(None).(fmt.Stringer).String(): None,
interface{}(Minor).(fmt.Stringer).String(): Minor,
interface{}(Major).(fmt.Stringer).String(): Major,
interface{}(Critical).(fmt.Stringer).String(): Critical,
}
}
}

// MarshalJSON is generated so StatusIndicator satisfies json.Marshaler.
func (r StatusIndicator) MarshalJSON() ([]byte, error) {
if s, ok := interface{}(r).(fmt.Stringer); ok {
return json.Marshal(s.String())
}
s, ok := _StatusIndicatorValueToName[r]
if !ok {
return nil, fmt.Errorf("invalid StatusIndicator: %d", r)
}
return json.Marshal(s)
}

// UnmarshalJSON is generated so StatusIndicator satisfies json.Unmarshaler.
func (r *StatusIndicator) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return fmt.Errorf("StatusIndicator should be a string, got %s", data)
}
v, ok := _StatusIndicatorNameToValue[s]
if !ok {
return fmt.Errorf("invalid StatusIndicator %q", s)
}
*r = v
return nil
}
Loading

0 comments on commit cc784ff

Please sign in to comment.