First Commit of Voltha-Go-Controller from Radisys
Change-Id: I8e2e908e7ab09a4fe3d86849da18b6d69dcf4ab0
diff --git a/internal/pkg/errorcodes/errorcodes.go b/internal/pkg/errorcodes/errorcodes.go
new file mode 100644
index 0000000..4bb0490
--- /dev/null
+++ b/internal/pkg/errorcodes/errorcodes.go
@@ -0,0 +1,333 @@
+/*
+* Copyright 2022-present Open Networking Foundation
+* 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 errorcodes provides constants that are commonly used by RWcore and adapters.
+package errorcodes
+
+import (
+ "net/http"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+)
+
+// NBErrorCode represents the error code for the error.
+type NBErrorCode int
+
+const (
+ // VolthaErrorMessageFormat represents the format in which the Voltha accepts the errors.
+ VolthaErrorMessageFormat = "code = %d, desc = %s"
+)
+
+// List of error messages returned to Voltha.
+var (
+ // ErrUnimplementedRPC is returned when the RPC is not implemented
+ ErrUnimplementedRPC = status.Errorf(codes.Unimplemented, VolthaErrorMessageFormat, UnsupportedOperation, "Operation not implemented")
+ // ErrOperationNotSupported is returned when the operation is not supported
+ ErrOperationNotSupported = status.Errorf(codes.Unimplemented, VolthaErrorMessageFormat, UnsupportedOperation, "Operation not supported")
+
+ // ErrFailedRequest is returned when the component fails to send any request to other component
+ ErrFailedRequest = status.Errorf(codes.Internal, VolthaErrorMessageFormat, UnsuccessfulOperation, "Failed to send request")
+
+ // ErrFailedToEncodeConfig is returned when the data json marshal fails
+ ErrFailedToEncodeConfig = status.Errorf(codes.Internal, VolthaErrorMessageFormat, MessageEncodeFailed, "Failed to encode data")
+ // ErrFailedToDecodeConfig is returned when the data json unmarshal fails
+ ErrFailedToDecodeConfig = status.Errorf(codes.Internal, VolthaErrorMessageFormat, MessageDecodeFailed, "Failed to decode data")
+
+ // ErrFailedToUpdateDB is returned when update of data in KV store fails
+ ErrFailedToUpdateDB = status.Errorf(codes.Internal, VolthaErrorMessageFormat, DBOperationFailed, "Failed to update DB")
+ // ErrFailedToGetFromDB is returned when get data from KV store fails
+ ErrFailedToGetFromDB = status.Errorf(codes.Internal, VolthaErrorMessageFormat, DBOperationFailed, "Failed to fetch from DB")
+ // ErrFailedToDeleteFromDB is returned when delete data from KV store fails
+ ErrFailedToDeleteFromDB = status.Errorf(codes.Internal, VolthaErrorMessageFormat, DBOperationFailed, "Failed to delete from DB")
+
+ // ErrDeviceNotFound is returned when the handler for the device is not present in VOLTHA
+ ErrDeviceNotFound = status.Errorf(codes.NotFound, VolthaErrorMessageFormat, ResourceNotFound, "Device not found")
+ // ErrDeviceNotReachable is returned when the connection between adapter and agent is broken
+ ErrDeviceNotReachable = status.Errorf(codes.Unavailable, VolthaErrorMessageFormat, DeviceUnreachable, "Device is not reachable")
+ // ErrWrongDevice is returned when the received request has wrong device (parent/child)
+ ErrWrongDevice = status.Errorf(codes.FailedPrecondition, VolthaErrorMessageFormat, PrerequisiteNotMet, "Wrong device in request")
+ // ErrDeviceNotEnabledAndUp is returned when the state of the device is neither enabled nor active
+ ErrDeviceNotEnabledAndUp = status.Errorf(codes.FailedPrecondition, VolthaErrorMessageFormat, ResourceInImproperState, "Device is not enabled and up")
+ // ErrDeviceDeleted is returned when the state of the device is DELETED
+ ErrDeviceDeleted = status.Errorf(codes.FailedPrecondition, VolthaErrorMessageFormat, ResourceInImproperState, "Device is deleted")
+
+ // ErrPortNotFound is returned when the port is not present in VOLTHA
+ ErrPortNotFound = status.Errorf(codes.NotFound, VolthaErrorMessageFormat, ResourceNotFound, "Port not found")
+ // ErrPortIsInInvalidState is returned when the port is in an invalid state
+ ErrPortIsInInvalidState = status.Errorf(codes.Internal, VolthaErrorMessageFormat, ResourceInImproperState, "Port is in an invalid state")
+
+ // ErrInvalidParamInRequest is returned when the request contains invalid configuration
+ ErrInvalidParamInRequest = status.Errorf(codes.InvalidArgument, VolthaErrorMessageFormat, InvalidArgument, "Received invalid configuration in request")
+
+ // ErrImageNotRegistered is returned when the image is not registered
+ ErrImageNotRegistered = status.Errorf(codes.FailedPrecondition, VolthaErrorMessageFormat, PrerequisiteNotMet, "Image is not registered")
+ // ErrImageDownloadInProgress is returned when the image download is in progress
+ ErrImageDownloadInProgress = status.Errorf(codes.FailedPrecondition, VolthaErrorMessageFormat, MethodNotAllowed, "Image download is in progress")
+)
+
+// ConvertToVolthaErrorFormat converts the error to Voltha error format
+func ConvertToVolthaErrorFormat(err error) error {
+ st, ok := status.FromError(err)
+ if !ok {
+ return err
+ }
+ return status.Errorf(st.Code(), VolthaErrorMessageFormat, GrpcToVolthaErrorCodeMap[st.Code()], st.Message())
+}
+
+const (
+ //Success is returned when there is no error - 0
+ Success NBErrorCode = iota
+ //InvalidURL is returned when the URL specified for the request is invalid - 1
+ InvalidURL
+ //MissingArgument is returned when the mandatory/conditionally mandatory argument is missing - 2
+ MissingArgument
+ //RequestTimeout is returned when the request timed out. - 3
+ RequestTimeout
+ //ResourceAlreadyExists is returned when the resource already exists and create for the same is not allowed - 4
+ ResourceAlreadyExists
+ //ResourceInImproperState is returned when the resource is in improper state to process the request. - 5
+ ResourceInImproperState
+ //DeviceUnreachable is returned when the device is not reachable - 6
+ DeviceUnreachable
+ //OperationAlreadyInProgress is returned when the requested operation is already in progress - 7
+ OperationAlreadyInProgress
+ //InvalidConfig is returned when the configuration provided is invalid - 8
+ InvalidConfig
+ //ResourceNotFound is returned when the resource is not found - 9
+ ResourceNotFound
+ //MethodNotAllowed is returned when the requested method is not allowed - 10
+ MethodNotAllowed
+ //ResourceInUse is returned when the resource is in use, the delete of the resource is not allowed when in use - 11
+ ResourceInUse
+ //JobIDNotFound is returned when the Job ID not found - 12
+ JobIDNotFound
+ //JobIDAlreadyInUse is returned when the Job ID already in use - 13
+ JobIDAlreadyInUse
+ //PeerUnreachable is returned when the peer is unreachable -14
+ PeerUnreachable
+ //InvalidPatchOperation is returned when the parameter(s) mentioned in the patch operation are invalid - 15
+ InvalidPatchOperation
+ //OLTUnreachable is returned when the OLT is not reachable - 16
+ OLTUnreachable
+ //PrerequisiteNotMet is returned when the required prerequisite is not met to execute the requested procedure - 17
+ PrerequisiteNotMet
+ //MessageEncodeFailed is returned when Message encoding failed - 18
+ MessageEncodeFailed
+ //MessageDecodeFailed is returned when Message decoding failed - 19
+ MessageDecodeFailed
+ //ONTInternalError is returned when Internal error is reported by the ONT - 20
+ ONTInternalError
+ //OLTInternalError is returned when Internal error is reported by the OLT - 21
+ OLTInternalError
+ //VolthaInternalError is returned when Internal error occurred at Voltha - 22
+ VolthaInternalError
+ //ConfigMismatch is returned when the configuration does not match - 23
+ ConfigMismatch
+ //DBOperationFailed is returned when the database operation failed for the key - 24
+ DBOperationFailed
+ //ResourceLimitExceeded is returned when the resource limit exceeded the allowed limit - 25
+ ResourceLimitExceeded
+ //UndefinedEnv is returned when the required environment variable is not defined - 26
+ UndefinedEnv
+ //InvalidArgument is returned when the argument provided is invalid - 27
+ InvalidArgument
+ //InvalidPayload is returned when the configuration payload is invalid - 28
+ InvalidPayload
+ //DuplicateKey is returned when the duplicate entry for the key - 29
+ DuplicateKey
+ //DuplicateValue is returned when the duplicate entry for the value - 30
+ DuplicateValue
+ //UnsupportedOperation is returned when the request operation is not supported - 31
+ UnsupportedOperation
+ //UserUnauthorized is returned when the user is unauthorized to perform the requested operation - 32
+ UserUnauthorized
+ //LiveKPISubscriptionExists is returned when the live KPI subscription exists already for the requested resource - 33
+ LiveKPISubscriptionExists
+ //UnsuccessfulOperation is returned when the requested operation is unsuccessful - 34
+ UnsuccessfulOperation
+ //ResourceInDisabledStateAlready is returned when the resource is in disabled state already - 35
+ ResourceInDisabledStateAlready
+ //ResourceInEnabledStateAlready is returned when the resource is in enabled state already - 36
+ ResourceInEnabledStateAlready
+ //ResourceNotDiscoveredYet is returned when the resource is not discovered yet - 37
+ ResourceNotDiscoveredYet
+ //HighDiskUtilization is returned when the disk utilization is high, consider the disk cleanup. - 38
+ HighDiskUtilization
+ //KafkaError is returned when there is a kafka error - 39
+ KafkaError
+ //ResourceBusy is returned when the component/resource is busy. - 40
+ ResourceBusy
+ // UnsupportedParameter is returned when un supported field is provided in request. -41
+ UnsupportedParameter
+ //JobIDAlreadyExists is returned when the Job ID is already there in DB. -42
+ JobIDAlreadyExists
+ //LiveKPISubscriptionNotFound is returned when the live KPI subscription not found for the requested resource. -42
+ LiveKPISubscriptionNotFound
+ // HostUnreachable is returned when failed to establish the SFTP connection. -44
+ HostUnreachable
+ // DHCPServerUnreachable is returned when dhcp server is unreachable. -45
+ DHCPServerUnreachable
+ // SessionExpired is returned when user session is expired/timeout - 46
+ SessionExpired
+ // AccessDenied is returned when user operation is forbidden - 47
+ AccessDenied
+ // PasswordUpdateRequired is returned when password for the user is about to expire - 48
+ PasswordUpdateRequired
+ // InvalidMessageHeader is returned when token in security request is invalid/nil - 49
+ InvalidMessageHeader
+ // UserAccountBlocked is returned when user account gets blocked after multiple invalid attempts - 50
+ UserAccountBlocked
+ // UserAccountExpired is returned when user account gets expired - 51
+ UserAccountExpired
+ // UserAccountDormant is returned when user account gets dormant - 52
+ UserAccountDormant
+ // InvalidCredentials is returned when credentials are invalid in login request - 53
+ InvalidCredentials
+ // ConcurrentAccessFromMultipleIPs when multiple sessions gets established from same ip - 54
+ ConcurrentAccessFromMultipleIPs
+ // KPIThresholdCrossed when KPI threshold is crossed - 55
+ KPIThresholdCrossed
+ // ONTUnreachable is returned when the ONT is not reachable - 56
+ ONTUnreachable
+ // ResourceUnreachable is returned when the resource is not reachable -57
+ ResourceUnreachable
+ // ONTProcessingError is returned when onu returns processing error for omci message - 58
+ ONTProcessingError
+ // ONTResourceBusy is returned when onu returns device busy error for omci message - 59
+ ONTResourceBusy
+ // ONTMEInstanceExists is returned when onu returns OMCI ME instance exists error for omci message - 60
+ ONTMEInstanceExists
+ // ONTUnknownMEInstance is returned when onu returns OMCI ME Unknown Instance error for omci message - 61
+ ONTUnknownMEInstance
+ // JoinUnsuccessful is returned when an IGMP Join request is unsuccessful - 62
+ JoinUnsuccessful
+ // QueryExpired is returned when there is no response to IGMP Queries from the controller - 63
+ QueryExpired
+ // AvailableBwValidationErr is returned when requested bandwidth is not available on the pon port - 64
+ AvailableBwValidationErr
+)
+
+//NBErrorCodeMap converts error code to error description string
+var NBErrorCodeMap = map[NBErrorCode]string{
+ Success: "Success",
+ InvalidURL: "INVALID_URL",
+ RequestTimeout: "REQUEST_TIMEOUT",
+ MissingArgument: "MISSING_ARGUMENT",
+ ResourceAlreadyExists: "RESOURCE_ALREADY_EXISTS",
+ ResourceInImproperState: "RESOURCE_IN_IMPROPER_STATE",
+ DeviceUnreachable: "DEVICE_UNREACHABLE",
+ OperationAlreadyInProgress: "OPERATION_ALREADY_IN_PROGRESS",
+ InvalidConfig: "INVALID_CONFIG",
+ ResourceNotFound: "RESOURCE_NOT_FOUND",
+ MethodNotAllowed: "METHOD_NOT_ALLOWED",
+ ResourceInUse: "RESOURCE_IN_USE",
+ JobIDNotFound: "JOB_ID_NOT_FOUND",
+ JobIDAlreadyInUse: "JOB_ID_ALREADY_IN_USE",
+ PeerUnreachable: "PEER_UNREACHABLE",
+ InvalidPatchOperation: "INVALID_PATCH_OPERATION",
+ OLTUnreachable: "OLT_UNREACHABLE",
+ PrerequisiteNotMet: "PREREQUISITE_NOT_MET",
+ MessageEncodeFailed: "MESSAGE_ENCODE_FAILED",
+ MessageDecodeFailed: "MESSAGE_DECODE_FAILED",
+ ONTInternalError: "ONT_INTERNAL_ERROR",
+ OLTInternalError: "OLT_INTERNAL_ERROR",
+ VolthaInternalError: "Voltha_INTERNAL_ERROR",
+ ConfigMismatch: "CONFIG_MISMATCH",
+ DBOperationFailed: "DB_OPERATION_FAILED",
+ ResourceLimitExceeded: "RESOURCE_LIMIT_EXCEEDED",
+ UndefinedEnv: "UNDEFINED_ENV",
+ InvalidArgument: "INVALID_ARGUMENT",
+ InvalidPayload: "INVALID_PAYLOAD",
+ DuplicateKey: "DUPLICATE_KEY",
+ DuplicateValue: "DUPLICATE_VALUE",
+ UnsupportedOperation: "UNSUPPORTED_OPERATION",
+ UserUnauthorized: "USER_UNAUTHORIZED",
+ LiveKPISubscriptionExists: "LIVE_KPI_SUBSCRIPTION_EXISTS",
+ UnsuccessfulOperation: "UNSUCCESSFUL_OPERATION",
+ ResourceInDisabledStateAlready: "RESOURCE_IN_DISABLED_STATE_ALREADY",
+ ResourceInEnabledStateAlready: "RESOURCE_IN_ENABLED_STATE_ALREADY",
+ ResourceNotDiscoveredYet: "RESOURCE_NOT_DISCOVERED_YET",
+ HighDiskUtilization: "HIGH_DISK_UTILIZATION",
+ KafkaError: "KAFKA_ERROR",
+ LiveKPISubscriptionNotFound: "LIVE_KPI_SUBSCRIPTION_NOT_FOUND",
+ ResourceBusy: "RESOURCE_BUSY",
+ UnsupportedParameter: "UNSUPPORTED_PARAMETER",
+ JobIDAlreadyExists: "JOB_ID_ALREADY_EXISTS",
+ HostUnreachable: "HOST_UNREACHABLE",
+ DHCPServerUnreachable: "DHCP_SERVER_UNREACHABLE",
+ InvalidMessageHeader: "INVALID_MESSAGE_HEADER",
+ SessionExpired: "SESSION_EXPIRED",
+ AccessDenied: "ACCESS_DENIED",
+ PasswordUpdateRequired: "PASSWORD_UPDATE_REQUIRED",
+ InvalidCredentials: "INVALID_CREDENTIALS",
+ UserAccountBlocked: "USER_ACCOUNT_BLOCKED",
+ UserAccountExpired: "USER_ACCOUNT_EXPIRED",
+ ConcurrentAccessFromMultipleIPs: "CONCURRENT_ACCESS_FROM_MULTIPLE_IPS",
+ KPIThresholdCrossed: "KPI_THRESHOLD_CROSSED",
+ ONTUnreachable: "ONT_UNREACHABLE",
+ ONTProcessingError: "ONT_PROCESSING_ERROR",
+ ONTResourceBusy: "ONT_RESOURCE_BUSY",
+ ONTMEInstanceExists: "ONT_ME_INSTANCE_ALREADY_EXISTS",
+ ONTUnknownMEInstance: "ONT_UNKNOWN_ME_INSTANCE",
+ JoinUnsuccessful: "JOIN_UNSUCCESSFUL",
+ QueryExpired: "QUERY_EXPIRED",
+}
+
+// GrpcToVolthaErrorCodeMap contains mapping of grpc error code coming from OpenOLT-Agent to Voltha error codes.
+var GrpcToVolthaErrorCodeMap = map[codes.Code]NBErrorCode{
+ codes.OK: Success,
+ codes.Canceled: UnsuccessfulOperation,
+ codes.Unknown: OLTInternalError,
+ codes.InvalidArgument: InvalidArgument,
+ codes.DeadlineExceeded: RequestTimeout,
+ codes.NotFound: ResourceNotFound,
+ codes.AlreadyExists: ResourceAlreadyExists,
+ codes.PermissionDenied: UserUnauthorized,
+ codes.ResourceExhausted: ResourceLimitExceeded,
+ codes.FailedPrecondition: PrerequisiteNotMet,
+ codes.Aborted: UnsuccessfulOperation,
+ codes.OutOfRange: InvalidArgument,
+ codes.Unimplemented: UnsupportedOperation,
+ codes.Internal: OLTInternalError,
+ codes.Unavailable: ResourceBusy,
+ codes.DataLoss: OLTInternalError,
+ codes.Unauthenticated: UserUnauthorized,
+}
+
+// HTTPStatusCodeToVolthaErrorCodeMap contains mapping of http status code coming from VGC to Voltha error codes.
+var HTTPStatusCodeToVolthaErrorCodeMap = map[int]NBErrorCode{
+ http.StatusOK: Success,
+ http.StatusCreated: Success,
+ http.StatusAccepted: Success,
+ http.StatusBadRequest: InvalidPayload,
+ http.StatusConflict: ResourceInImproperState,
+ http.StatusInternalServerError: VolthaInternalError,
+}
+
+// GetErrorInfo - parses the error details from err structure response from voltha
+// Return statusCode (uint32) - Error code [0 - Success]
+// status Msg (string) - Error Msg
+func GetErrorInfo(err error) (uint32, string) {
+ var statusCode uint32
+ var statusMsg string
+ if status, _ := status.FromError(err); status != nil {
+ statusCode = uint32(status.Code())
+ statusMsg = status.Message()
+ } else {
+ statusCode = 0
+ }
+ return statusCode, statusMsg
+}
+
diff --git a/internal/pkg/errorcodes/service/errors.go b/internal/pkg/errorcodes/service/errors.go
new file mode 100644
index 0000000..a25278d
--- /dev/null
+++ b/internal/pkg/errorcodes/service/errors.go
@@ -0,0 +1,122 @@
+/*
+* Copyright 2022-present Open Networking Foundation
+* 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 service provides constants.
+package service
+
+const (
+ errorCodeStartRange = 1000
+)
+
+const (
+ // VolthaErrorMessageFormat represents the format in which the Voltha accepts the errors.
+ VolthaErrorMessageFormat = "code = %d, desc = %s"
+)
+
+// ErrorCode is Enum of error type
+type ErrorCode int
+
+//ErrorAction is Enum for error action
+type ErrorAction int
+
+const (
+ //ErrOk is returned when request is successful
+ ErrOk ErrorCode = 0
+ //ErrInProgress is returned when operation is in progress
+ ErrInProgress ErrorCode = iota + errorCodeStartRange
+ //ErrInvalidParm is returned when parameter is wrong
+ ErrInvalidParm
+ //ErrResourceUnavailable is returned when no free resources are available
+ ErrResourceUnavailable
+ //ErrAlreadyExists is returned when entry already exists
+ ErrAlreadyExists
+ //ErrNotExists is returned when entry does not exists
+ ErrNotExists
+ //ErrInvalidOperation is returned when invalid operation is performed
+ ErrInvalidOperation
+ //ErrDeviceNotConnected is returned when there is no connection with the target system
+ ErrDeviceNotConnected
+ //ErrTimeout is returned when operation times out
+ ErrTimeout
+ //ErrResourceBusy is returned when resource is busy
+ ErrResourceBusy
+ //ErrInternal is returned when Errors happened internally
+ ErrInternal
+ //ErrIo is returned when there is I/O error
+ ErrIo
+ //ErrMandatoryParmIsMissing is returned when mandatory parameter is missing
+ ErrMandatoryParmIsMissing
+ //ErrBadState is returned when object is in bad state
+ ErrBadState
+ //ErrOnuInternal is returned when ONT internal failure occurs
+ ErrOnuInternal
+ //ErrElanNotCreated is returned when ELAN is not created
+ ErrElanNotCreated
+ //ErrOltInternal is returned when OLT internal failure occurs
+ ErrOltInternal
+)
+
+//ErrorCodeMap converts error code to error description string
+var ErrorCodeMap = map[ErrorCode]string{
+ ErrOk: "Success",
+ ErrInProgress: "Operation is in progress",
+ ErrInvalidParm: "Invalid parameter",
+ ErrResourceUnavailable: "No free resource available",
+ ErrAlreadyExists: "Entry already exists",
+ ErrNotExists: "Entry does not exists",
+ ErrInvalidOperation: "Invalid Operation",
+ ErrDeviceNotConnected: "No connection with the target system",
+ ErrTimeout: "Operation timed out",
+ ErrResourceBusy: "Resource Busy",
+ ErrInternal: "Internal Error",
+ ErrIo: "I/O Error",
+ ErrMandatoryParmIsMissing: "Mandatory parameter is missing",
+ ErrBadState: "Object is in bad state",
+ ErrOnuInternal: "ONT internal error",
+ ErrElanNotCreated: "ELAN not created",
+ ErrOltInternal: "OLT internal error",
+}
+
+const (
+ //Retry is returned if subservice reactivation is required
+ Retry ErrorAction = iota
+ //Quiet is returned if no action has to be taken
+ Quiet
+ //Deactivate is returned if subservice has to be deactivated
+ Deactivate
+ //Invalid is returned when invalid error is received from vgc
+ Invalid
+)
+
+//RetryErrorCodeMap consists of errors that requires service activation retry
+var RetryErrorCodeMap = map[ErrorCode]ErrorAction{
+ ErrOk: Quiet,
+ ErrInProgress: Deactivate,
+ ErrInvalidParm: Deactivate,
+ ErrResourceUnavailable: Deactivate,
+ ErrAlreadyExists: Quiet,
+ ErrNotExists: Quiet,
+ ErrInvalidOperation: Deactivate,
+ ErrDeviceNotConnected: Quiet,
+ ErrTimeout: Retry,
+ ErrResourceBusy: Retry,
+ ErrInternal: Deactivate,
+ ErrIo: Retry,
+ ErrMandatoryParmIsMissing: Deactivate,
+ ErrBadState: Deactivate,
+ ErrOnuInternal: Retry,
+ ErrElanNotCreated: Retry,
+ ErrOltInternal: Deactivate,
+}