From e9bd5cd40f36ea581271d085185d05a185a35dc9 Mon Sep 17 00:00:00 2001 From: Julian Brost Date: Tue, 24 Aug 2021 11:16:39 +0200 Subject: [PATCH] SLA reporting: additionally write relevant history events to dedicated SLA tables --- pkg/icingadb/history/sla.go | 26 +++++++++++++++++++ pkg/icingadb/history/sync.go | 10 +++++--- pkg/icingadb/v1/history/downtime.go | 40 +++++++++++++++++++++++++++++ pkg/icingadb/v1/history/state.go | 10 ++++++++ pkg/types/state_type.go | 9 +++++-- 5 files changed, 89 insertions(+), 6 deletions(-) create mode 100644 pkg/icingadb/history/sla.go diff --git a/pkg/icingadb/history/sla.go b/pkg/icingadb/history/sla.go new file mode 100644 index 00000000..79d22c72 --- /dev/null +++ b/pkg/icingadb/history/sla.go @@ -0,0 +1,26 @@ +package history + +import ( + "github.com/go-redis/redis/v8" + "github.com/icinga/icingadb/pkg/icingadb/v1/history" + "github.com/icinga/icingadb/pkg/structify" + "github.com/icinga/icingadb/pkg/types" + "reflect" +) + +var slaStateStructify = structify.MakeMapStructifier(reflect.TypeOf((*history.SlaHistoryState)(nil)).Elem(), "json") + +func stateHistoryToSlaEntity(entry redis.XMessage) ([]history.UpserterEntity, error) { + slaStateInterface, err := slaStateStructify(entry.Values) + if err != nil { + return nil, err + } + slaState := slaStateInterface.(*history.SlaHistoryState) + + if slaState.StateType != types.StateHard { + // only hard state changes are relevant for SLA history, discard all others + return nil, nil + } + + return []history.UpserterEntity{slaState}, nil +} diff --git a/pkg/icingadb/history/sync.go b/pkg/icingadb/history/sync.go index e1d6160b..d5bebd65 100644 --- a/pkg/icingadb/history/sync.go +++ b/pkg/icingadb/history/sync.go @@ -359,12 +359,14 @@ var syncPipelines = map[string][]stageFunc{ writeOneEntityStage((*v1.HistoryNotification)(nil)), // history (depends on notification_history) }, "state": { - writeOneEntityStage((*v1.StateHistory)(nil)), // state_history - writeOneEntityStage((*v1.HistoryState)(nil)), // history (depends on state_history) + writeOneEntityStage((*v1.StateHistory)(nil)), // state_history + writeOneEntityStage((*v1.HistoryState)(nil)), // history (depends on state_history) + writeMultiEntityStage(stateHistoryToSlaEntity), // sla_history_state }, "downtime": { - writeOneEntityStage((*v1.DowntimeHistory)(nil)), // downtime_history - writeOneEntityStage((*v1.HistoryDowntime)(nil)), // history (depends on downtime_history) + writeOneEntityStage((*v1.DowntimeHistory)(nil)), // downtime_history + writeOneEntityStage((*v1.HistoryDowntime)(nil)), // history (depends on downtime_history) + writeOneEntityStage((*v1.SlaHistoryDowntime)(nil)), // sla_history_downtime }, "comment": { writeOneEntityStage((*v1.CommentHistory)(nil)), // comment_history diff --git a/pkg/icingadb/v1/history/downtime.go b/pkg/icingadb/v1/history/downtime.go index 448c9ecf..99f93f6a 100644 --- a/pkg/icingadb/v1/history/downtime.go +++ b/pkg/icingadb/v1/history/downtime.go @@ -80,6 +80,30 @@ func (*HistoryDowntime) TableName() string { return "history" } +type SlaHistoryDowntime struct { + DowntimeHistoryEntity `json:",inline"` + HistoryTableMeta `json:",inline"` + SlaHistoryDowntimeUpserter `json:",inline"` + DowntimeStart types.UnixMilli `json:"start_time"` + HasBeenCancelled types.Bool `json:"has_been_cancelled" db:"-"` + CancelTime types.UnixMilli `json:"cancel_time" db:"-"` + EndTime types.UnixMilli `json:"end_time" db:"-"` +} + +// Init implements the contracts.Initer interface. +func (s *SlaHistoryDowntime) Init() { + s.DowntimeEnd.History = s +} + +type SlaHistoryDowntimeUpserter struct { + DowntimeEnd SlaDowntimeEndTime `json:"-"` +} + +// Upsert implements the contracts.Upserter interface. +func (h *SlaHistoryDowntimeUpserter) Upsert() interface{} { + return h +} + type DowntimeEventTime struct { History *HistoryDowntime `db:"-"` } @@ -109,6 +133,19 @@ func (et DowntimeEventTime) Value() (driver.Value, error) { } } +type SlaDowntimeEndTime struct { + History *SlaHistoryDowntime `db:"-"` +} + +// Value implements the driver.Valuer interface. +func (et SlaDowntimeEndTime) Value() (driver.Value, error) { + if et.History.HasBeenCancelled.Valid && et.History.HasBeenCancelled.Bool { + return et.History.CancelTime.Value() + } else { + return et.History.EndTime.Value() + } +} + // Assert interface compliance. var ( _ contracts.Entity = (*DowntimeHistoryEntity)(nil) @@ -117,5 +154,8 @@ var ( _ contracts.Initer = (*HistoryDowntime)(nil) _ contracts.TableNamer = (*HistoryDowntime)(nil) _ UpserterEntity = (*HistoryDowntime)(nil) + _ contracts.Initer = (*SlaHistoryDowntime)(nil) + _ UpserterEntity = (*SlaHistoryDowntime)(nil) _ driver.Valuer = DowntimeEventTime{} + _ driver.Valuer = SlaDowntimeEndTime{} ) diff --git a/pkg/icingadb/v1/history/state.go b/pkg/icingadb/v1/history/state.go index ad5f4703..8a913812 100644 --- a/pkg/icingadb/v1/history/state.go +++ b/pkg/icingadb/v1/history/state.go @@ -33,9 +33,19 @@ func (*HistoryState) TableName() string { return "history" } +type SlaHistoryState struct { + HistoryTableEntity `json:",inline"` + HistoryTableMeta `json:",inline"` + EventTime types.UnixMilli `json:"event_time"` + StateType types.StateType `json:"state_type" db:"-"` + HardState uint8 `json:"hard_state"` + PreviousHardState uint8 `json:"previous_hard_state"` +} + // Assert interface compliance. var ( _ UpserterEntity = (*StateHistory)(nil) _ contracts.TableNamer = (*HistoryState)(nil) _ UpserterEntity = (*HistoryState)(nil) + _ UpserterEntity = (*SlaHistoryState)(nil) ) diff --git a/pkg/types/state_type.go b/pkg/types/state_type.go index 8a24819e..f0cc69af 100644 --- a/pkg/types/state_type.go +++ b/pkg/types/state_type.go @@ -46,10 +46,15 @@ func badStateType(t interface{}) error { return errors.Errorf("bad state type: %#v", t) } +const ( + StateSoft = StateType(0) + StateHard = StateType(1) +) + // stateTypes maps all valid StateType values to their SQL representation. var stateTypes = map[StateType]string{ - 0: "soft", - 1: "hard", + StateSoft: "soft", + StateHard: "hard", } // Assert interface compliance.