blob: 9490427b71927d67f8db29e1f5b3cd8aade968d5 [file] [log] [blame]
/*
* Copyright 2020-2024 Open Networking Foundation (ONF) and the ONF Contributors
*
* 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 olterrors implements functions to manipulate OLT errors
package olterrors
import (
"context"
"encoding/json"
"fmt"
"strings"
"github.com/opencord/voltha-lib-go/v7/pkg/log"
)
const (
defaultLogAndReturnLevel = log.ErrorLevel
)
func copy(src log.Fields) log.Fields {
dst := make(log.Fields)
for k, v := range src {
dst[k] = v
}
return dst
}
func merge(one, two log.Fields) log.Fields {
dst := make(log.Fields)
for k, v := range one {
dst[k] = v
}
for k, v := range two {
dst[k] = v
}
return dst
}
// LoggableError defined functions that can be used to log an object
type LoggableError interface {
error
Log() error
LogAt(log.LogLevel) error
}
// WrappedError can be used to extract the wrapped errors of structs that include ErrAdapter
type WrappedError interface {
Unwrap() error
}
// ErrAdapter represents a basic adapter error that combines an name, field set
// and wrapped error
type ErrAdapter struct {
name string
fields log.Fields
wrapped error
}
// NewErrAdapter constructs a new error with the given values
func NewErrAdapter(name string, fields log.Fields, wrapped error) LoggableError {
return &ErrAdapter{
name: name,
fields: copy(fields),
wrapped: wrapped,
}
}
// Name returns the error name
func (e *ErrAdapter) Name() string {
return e.name
}
// Fields returns the fields associated with the error
func (e *ErrAdapter) Fields() log.Fields {
return e.fields
}
// Unwrap returns the wrapped or nested error
func (e *ErrAdapter) Unwrap() error {
return e.wrapped
}
// Error returns a string representation of the error
func (e *ErrAdapter) Error() string {
ctx := context.Background()
var buf strings.Builder
_, er := buf.WriteString(e.name)
if er != nil {
logger.Error(ctx, er)
}
if len(e.fields) > 0 {
if val, err := json.Marshal(e.fields); err == nil {
_, er = buf.WriteString(fmt.Sprintf(": [%s]", string(val)))
if er != nil {
logger.Error(ctx, er)
}
}
}
if e.wrapped != nil {
_, er = buf.WriteString(fmt.Sprintf(": %s", e.wrapped.Error()))
if er != nil {
logger.Error(ctx, er)
}
}
return buf.String()
}
// Log logs the error at the default level for log and return
func (e *ErrAdapter) Log() error {
return e.LogAt(defaultLogAndReturnLevel)
}
// LogAt logs the error at the specified level and then returns the error
func (e *ErrAdapter) LogAt(level log.LogLevel) error {
loggerFunc := logger.Debugw
switch level {
case log.InfoLevel:
loggerFunc = logger.Infow
case log.WarnLevel:
loggerFunc = logger.Warnw
case log.ErrorLevel:
loggerFunc = logger.Errorw
case log.FatalLevel:
loggerFunc = logger.Fatalw
}
local := e.fields
if e.wrapped != nil {
local = merge(e.fields, log.Fields{"wrapped": e.wrapped})
}
loggerFunc(context.Background(), e.name, local)
return e
}
// ErrInvalidValue represents an error condition with given value is not able to
// be processed
type ErrInvalidValue struct {
ErrAdapter
}
// NewErrInvalidValue constructs a new error based on the given values
func NewErrInvalidValue(fields log.Fields, wrapped error) LoggableError {
return &ErrInvalidValue{
ErrAdapter{
name: "invalid-value",
fields: copy(fields),
wrapped: wrapped,
},
}
}
// Log logs the error at the default level for log and return
func (e *ErrInvalidValue) Log() error {
_ = e.ErrAdapter.Log()
return e
}
// LogAt logs the error at the specified level and then returns the error
func (e *ErrInvalidValue) LogAt(level log.LogLevel) error {
_ = e.ErrAdapter.LogAt(level)
return e
}
// ErrNotFound represents an error condition when a value can not be located
// given a field set of criteria
type ErrNotFound struct {
ErrAdapter
}
// NewErrNotFound constructs a new error based on the given values
func NewErrNotFound(target string, fields log.Fields, wrapped error) LoggableError {
return &ErrNotFound{
ErrAdapter{
name: "not-found",
fields: merge(fields, log.Fields{"target": target}),
wrapped: wrapped,
},
}
}
// Log logs the error at the default level for log and return
func (e *ErrNotFound) Log() error {
_ = e.ErrAdapter.Log()
return e
}
// LogAt logs the error at the specified level and then returns the error
func (e *ErrNotFound) LogAt(level log.LogLevel) error {
_ = e.ErrAdapter.LogAt(level)
return e
}
// ErrPersistence representation an error condition when a persistence operation
// did not succeed
type ErrPersistence struct {
ErrAdapter
}
// NewErrPersistence constructs a new error based on the given values
func NewErrPersistence(operation, entityType string, ID uint64, fields log.Fields, wrapped error) LoggableError {
return &ErrPersistence{
ErrAdapter{
name: "unable-to-persist",
fields: merge(fields, log.Fields{
"operation": operation,
"entity-type": entityType,
"id": fmt.Sprintf("0x%x", ID)}),
wrapped: wrapped,
},
}
}
// Log logs the error at the default level for log and return
func (e *ErrPersistence) Log() error {
_ = e.ErrAdapter.Log()
return e
}
// LogAt logs the error at the specified level and then returns the error
func (e *ErrPersistence) LogAt(level log.LogLevel) error {
_ = e.ErrAdapter.LogAt(level)
return e
}
// ErrCommunication representation an error condition when an interprocess
// message communication fails
type ErrCommunication struct {
ErrAdapter
}
// NewErrCommunication constructs a new error based on the given values
func NewErrCommunication(operation string, fields log.Fields, wrapped error) LoggableError {
return &ErrCommunication{
ErrAdapter{
name: "failed-communication",
fields: merge(fields, log.Fields{
"operation": operation}),
wrapped: wrapped,
},
}
}
// Log logs the error at the default level for log and return
func (e *ErrCommunication) Log() error {
_ = e.ErrAdapter.Log()
return e
}
// LogAt logs the error at the specified level and then returns the error
func (e *ErrCommunication) LogAt(level log.LogLevel) error {
_ = e.ErrAdapter.LogAt(level)
return e
}
// ErrFlowOp represents an error condition when a flow operation to a device did
// not succeed
type ErrFlowOp struct {
ErrAdapter
}
// NewErrFlowOp constructs a new error based on the given values
func NewErrFlowOp(operation string, ID uint64, fields log.Fields, wrapped error) LoggableError {
return &ErrPersistence{
ErrAdapter{
name: "unable-to-perform-flow-operation",
fields: merge(fields, log.Fields{
"operation": operation,
"id": fmt.Sprintf("0x%x", ID)}),
wrapped: wrapped,
},
}
}
// Log logs the error at the default level for log and return
func (e *ErrFlowOp) Log() error {
_ = e.ErrAdapter.Log()
return e
}
// LogAt logs the error at the specified level and then returns the error
func (e *ErrFlowOp) LogAt(level log.LogLevel) error {
_ = e.ErrAdapter.LogAt(level)
return e
}
// ErrGroupOp represents an error condition when a group operation to a device did
// not succeed
type ErrGroupOp struct {
ErrAdapter
}
// NewErrGroupOp constructs a new error based on the given values
func NewErrGroupOp(operation string, ID uint32, fields log.Fields, wrapped error) LoggableError {
return &ErrPersistence{
ErrAdapter{
name: "unable-to-perform-group-operation",
fields: merge(fields, log.Fields{
"operation": operation,
"id": fmt.Sprintf("0x%x", ID)}),
wrapped: wrapped,
},
}
}
// Log logs the error at the default level for log and return
func (e *ErrGroupOp) Log() error {
_ = e.ErrAdapter.Log()
return e
}
// LogAt logs the error at the specified level and then returns the error
func (e *ErrGroupOp) LogAt(level log.LogLevel) error {
_ = e.ErrAdapter.LogAt(level)
return e
}
// ErrTimeout represents an error condition when the deadline for performing an
// operation has been exceeded
type ErrTimeout struct {
ErrAdapter
}
// NewErrTimeout constructs a new error based on the given values
func NewErrTimeout(operation string, fields log.Fields, wrapped error) LoggableError {
return &ErrTimeout{
ErrAdapter{
name: "operation-timed-out",
fields: merge(fields, log.Fields{"operation": operation}),
wrapped: wrapped,
},
}
}
// Log logs the error at the default level for log and return
func (e *ErrTimeout) Log() error {
_ = e.ErrAdapter.Log()
return e
}
// LogAt logs the error at the specified level and then returns the error
func (e *ErrTimeout) LogAt(level log.LogLevel) error {
_ = e.ErrAdapter.LogAt(level)
return e
}
var (
// ErrNotImplemented error returned when an unimplemented method is
// invoked
ErrNotImplemented = NewErrAdapter("not-implemented", nil, nil)
// ErrInvalidPortRange error returned when a given port is not in the
// valid range
ErrInvalidPortRange = NewErrAdapter("invalid-port-range", nil, nil)
// ErrStateTransition error returned when a state transition is fails
ErrStateTransition = NewErrAdapter("state-transition", nil, nil)
// ErrResourceManagerInstantiating error returned when an unexpected
// condition occcurs while instantiating the resource manager
ErrResourceManagerInstantiating = NewErrAdapter("resoure-manager-instantiating", nil, nil)
// ErrFlowManagerInstantiating error returned when an unexpected
// condition occcurs while instantiating the flow manager
ErrFlowManagerInstantiating = NewErrAdapter("flow-manager-instantiating", nil, nil)
// ErrGroupManagerInstantiating error returned when an unexpected
// condition occcurs while instantiating the group manager
ErrGroupManagerInstantiating = NewErrAdapter("group-manager-instantiating", nil, nil)
)