diff --git a/cmd/icingadb-migrate/convert.go b/cmd/icingadb-migrate/convert.go index 164302d1..9cd4d537 100644 --- a/cmd/icingadb-migrate/convert.go +++ b/cmd/icingadb-migrate/convert.go @@ -8,7 +8,6 @@ import ( "github.com/icinga/icinga-go-library/types" "github.com/icinga/icinga-go-library/utils" "github.com/icinga/icingadb/pkg/common" - icingadbTypes "github.com/icinga/icingadb/pkg/icingadb/types" v1 "github.com/icinga/icingadb/pkg/icingadb/v1" "github.com/icinga/icingadb/pkg/icingadb/v1/history" "github.com/jmoiron/sqlx" @@ -621,17 +620,16 @@ func convertNotificationRows( // migrated data itself via the history ID as object name, i.e. one "virtual object" per sent notification. name := strconv.FormatUint(row.NotificationId, 10) - nt := convertNotificationType(row.NotificationReason, row.State) - - ntEnum, err := nt.Value() + notificationType, err := convertNotificationType(row.NotificationReason, row.State) if err != nil { + log.With("notification_reason", row.NotificationReason, "state", row.State).Errorf("%+v", err) continue } ts := convertTime(row.EndTime.Int64, row.EndTimeUsec) tsMilli := float64(ts.Time().UnixMilli()) - notificationHistoryId := hashAny([]interface{}{env, name, ntEnum, tsMilli}) - id := hashAny([]interface{}{env, "notification", name, ntEnum, tsMilli}) + notificationHistoryId := hashAny([]interface{}{env, name, notificationType, tsMilli}) + id := hashAny([]interface{}{env, "notification", name, notificationType, tsMilli}) typ := objectTypes[row.ObjecttypeId] hostId := calcObjectId(env, row.Name1) serviceId := calcServiceId(env, row.Name1, row.Name2) @@ -654,7 +652,7 @@ func convertNotificationRows( ServiceId: serviceId, }, NotificationId: calcObjectId(env, name), - Type: nt, + Type: history.NotificationType(notificationType), SendTime: ts, State: row.State, PreviousHardState: previousHardState, @@ -699,34 +697,34 @@ func convertNotificationRows( return } -// convertNotificationType maps IDO values[1] to Icinga DB ones[2]. +// convertNotificationType maps IDO values [^1] to their Icinga DB counterparts [^2]. // -// [1]: https://github.com/Icinga/icinga2/blob/32c7f7730db154ba0dff5856a8985d125791c/lib/db_ido/dbevents.cpp#L1507-L1524 -// [2]: https://github.com/Icinga/icingadb/blob/8f31ac143875498797725adb9bfacf3d4/pkg/types/notification_type.go#L53-L61 -func convertNotificationType(notificationReason, state uint8) icingadbTypes.NotificationType { +// [^1]: https://github.com/Icinga/icinga2/blob/32c7f7730db154ba0dff5856a8985d125791c/lib/db_ido/dbevents.cpp#L1507-L1524 +// [^2]: https://github.com/Icinga/icinga2/blob/32c7f7730db154ba0dff5856a8985d125791c73e/lib/icingadb/icingadb-utility.cpp#L157-L176 +func convertNotificationType(notificationReason, state uint8) (string, error) { switch notificationReason { case 0: // state if state == 0 { - return 64 // recovery + return "recovery", nil } else { - return 32 // problem + return "problem", nil } - case 1: // acknowledgement - return 16 - case 2: // flapping start - return 128 - case 3: // flapping end - return 256 - case 5: // downtime start - return 1 - case 6: // downtime end - return 2 - case 7: // downtime removed - return 4 - case 8: // custom - return 8 - default: // bad notification type - return 0 + case 1: + return "acknowledgement", nil + case 2: + return "flapping_start", nil + case 3: + return "flapping_end", nil + case 5: + return "downtime_start", nil + case 6: + return "downtime_end", nil + case 7: + return "downtime_removed", nil + case 8: + return "custom", nil + default: + return "", fmt.Errorf("bad notification type: %d", notificationReason) } } diff --git a/pkg/icingadb/types/notification_type.go b/pkg/icingadb/types/notification_type.go deleted file mode 100644 index 24887810..00000000 --- a/pkg/icingadb/types/notification_type.go +++ /dev/null @@ -1,62 +0,0 @@ -package types - -import ( - "database/sql/driver" - "encoding" - "github.com/pkg/errors" - "strconv" -) - -// NotificationType specifies the reason of a sent notification. -type NotificationType uint16 - -// UnmarshalText implements the encoding.TextUnmarshaler interface. -func (nt *NotificationType) UnmarshalText(text []byte) error { - s := string(text) - - i, err := strconv.ParseUint(s, 10, 16) - if err != nil { - return errors.Wrapf(err, "can't parse %q into uint16", s) - } - - n := NotificationType(i) - if _, ok := notificationTypes[n]; !ok { - return badNotificationType(s) - } - - *nt = n - return nil -} - -// Value implements the driver.Valuer interface. -func (nt NotificationType) Value() (driver.Value, error) { - if v, ok := notificationTypes[nt]; ok { - return v, nil - } else { - return nil, badNotificationType(nt) - } -} - -// badNotificationType returns an error about a syntactically, but not semantically valid NotificationType. -func badNotificationType(t interface{}) error { - return errors.Errorf("bad notification type: %#v", t) -} - -// notificationTypes maps all valid NotificationType values to their SQL representation. -var notificationTypes = map[NotificationType]string{ - 1: "downtime_start", - 2: "downtime_end", - 4: "downtime_removed", - 8: "custom", - 16: "acknowledgement", - 32: "problem", - 64: "recovery", - 128: "flapping_start", - 256: "flapping_end", -} - -// Assert interface compliance. -var ( - _ encoding.TextUnmarshaler = (*NotificationType)(nil) - _ driver.Valuer = NotificationType(0) -) diff --git a/pkg/icingadb/v1/history/notification.go b/pkg/icingadb/v1/history/notification.go index 27f9d877..1d508f23 100644 --- a/pkg/icingadb/v1/history/notification.go +++ b/pkg/icingadb/v1/history/notification.go @@ -1,23 +1,24 @@ package history import ( + "database/sql/driver" + "encoding" "github.com/icinga/icinga-go-library/database" "github.com/icinga/icinga-go-library/types" - icingadbTypes "github.com/icinga/icingadb/pkg/icingadb/types" v1 "github.com/icinga/icingadb/pkg/icingadb/v1" ) type NotificationHistory struct { HistoryTableEntity `json:",inline"` HistoryTableMeta `json:",inline"` - NotificationId types.Binary `json:"notification_id"` - Type icingadbTypes.NotificationType `json:"type"` - SendTime types.UnixMilli `json:"send_time"` - State uint8 `json:"state"` - PreviousHardState uint8 `json:"previous_hard_state"` - Author string `json:"author"` - Text types.String `json:"text"` - UsersNotified uint16 `json:"users_notified"` + NotificationId types.Binary `json:"notification_id"` + Type NotificationType `json:"type"` + SendTime types.UnixMilli `json:"send_time"` + State uint8 `json:"state"` + PreviousHardState uint8 `json:"previous_hard_state"` + Author string `json:"author"` + Text types.String `json:"text"` + UsersNotified uint16 `json:"users_notified"` } type UserNotificationHistory struct { @@ -42,10 +43,50 @@ func (*HistoryNotification) TableName() string { return "history" } +// NotificationType represents the type of notification for a notification history entry. +// +// Starting with Icinga 2 v2.15, the type is will always be written to Redis as a string. +// This merely exists to provide a compatibility with older history entries lying around in Redis, +// which may have been written as an integer. +type NotificationType string + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +func (nt *NotificationType) UnmarshalText(text []byte) error { + switch t := string(text); t { + case "1": + *nt = "downtime_start" + case "2": + *nt = "downtime_end" + case "4": + *nt = "downtime_removed" + case "8": + *nt = "custom" + case "16": + *nt = "acknowledgement" + case "32": + *nt = "problem" + case "64": + *nt = "recovery" + case "128": + *nt = "flapping_start" + case "256": + *nt = "flapping_end" + default: + *nt = NotificationType(t) + } + + return nil +} + +// Value implements the driver.Valuer interface. +func (nt NotificationType) Value() (driver.Value, error) { return string(nt), nil } + // Assert interface compliance. var ( - _ UpserterEntity = (*NotificationHistory)(nil) - _ UpserterEntity = (*UserNotificationHistory)(nil) - _ database.TableNamer = (*HistoryNotification)(nil) - _ UpserterEntity = (*HistoryNotification)(nil) + _ UpserterEntity = (*NotificationHistory)(nil) + _ UpserterEntity = (*UserNotificationHistory)(nil) + _ database.TableNamer = (*HistoryNotification)(nil) + _ UpserterEntity = (*HistoryNotification)(nil) + _ encoding.TextUnmarshaler = (*NotificationType)(nil) + _ driver.Valuer = NotificationType("") )