Naveen Sampath | 04696f7 | 2022-06-13 15:19:14 +0530 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2022-present Open Networking Foundation |
| 3 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | * you may not use this file except in compliance with the License. |
| 5 | * You may obtain a copy of the License at |
| 6 | * |
| 7 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | * |
| 9 | * Unless required by applicable law or agreed to in writing, software |
| 10 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | * See the License for the specific language governing permissions and |
| 13 | * limitations under the License. |
| 14 | */ |
| 15 | |
| 16 | // Package errorcodes provides constants that are commonly used by RWcore and adapters. |
| 17 | package errorcodes |
| 18 | |
| 19 | import ( |
| 20 | "net/http" |
vinokuma | 926cb3e | 2023-03-29 11:41:06 +0530 | [diff] [blame] | 21 | |
Naveen Sampath | 04696f7 | 2022-06-13 15:19:14 +0530 | [diff] [blame] | 22 | "google.golang.org/grpc/codes" |
| 23 | "google.golang.org/grpc/status" |
| 24 | ) |
| 25 | |
| 26 | // NBErrorCode represents the error code for the error. |
| 27 | type NBErrorCode int |
| 28 | |
| 29 | const ( |
vinokuma | 926cb3e | 2023-03-29 11:41:06 +0530 | [diff] [blame] | 30 | // VolthaErrorMessageFormat represents the format in which the Voltha accepts the errors. |
| 31 | VolthaErrorMessageFormat = "code = %d, desc = %s" |
Naveen Sampath | 04696f7 | 2022-06-13 15:19:14 +0530 | [diff] [blame] | 32 | ) |
| 33 | |
| 34 | // List of error messages returned to Voltha. |
| 35 | var ( |
| 36 | // ErrUnimplementedRPC is returned when the RPC is not implemented |
| 37 | ErrUnimplementedRPC = status.Errorf(codes.Unimplemented, VolthaErrorMessageFormat, UnsupportedOperation, "Operation not implemented") |
| 38 | // ErrOperationNotSupported is returned when the operation is not supported |
| 39 | ErrOperationNotSupported = status.Errorf(codes.Unimplemented, VolthaErrorMessageFormat, UnsupportedOperation, "Operation not supported") |
| 40 | |
| 41 | // ErrFailedRequest is returned when the component fails to send any request to other component |
| 42 | ErrFailedRequest = status.Errorf(codes.Internal, VolthaErrorMessageFormat, UnsuccessfulOperation, "Failed to send request") |
| 43 | |
| 44 | // ErrFailedToEncodeConfig is returned when the data json marshal fails |
| 45 | ErrFailedToEncodeConfig = status.Errorf(codes.Internal, VolthaErrorMessageFormat, MessageEncodeFailed, "Failed to encode data") |
| 46 | // ErrFailedToDecodeConfig is returned when the data json unmarshal fails |
| 47 | ErrFailedToDecodeConfig = status.Errorf(codes.Internal, VolthaErrorMessageFormat, MessageDecodeFailed, "Failed to decode data") |
| 48 | |
| 49 | // ErrFailedToUpdateDB is returned when update of data in KV store fails |
| 50 | ErrFailedToUpdateDB = status.Errorf(codes.Internal, VolthaErrorMessageFormat, DBOperationFailed, "Failed to update DB") |
| 51 | // ErrFailedToGetFromDB is returned when get data from KV store fails |
| 52 | ErrFailedToGetFromDB = status.Errorf(codes.Internal, VolthaErrorMessageFormat, DBOperationFailed, "Failed to fetch from DB") |
| 53 | // ErrFailedToDeleteFromDB is returned when delete data from KV store fails |
| 54 | ErrFailedToDeleteFromDB = status.Errorf(codes.Internal, VolthaErrorMessageFormat, DBOperationFailed, "Failed to delete from DB") |
| 55 | |
| 56 | // ErrDeviceNotFound is returned when the handler for the device is not present in VOLTHA |
| 57 | ErrDeviceNotFound = status.Errorf(codes.NotFound, VolthaErrorMessageFormat, ResourceNotFound, "Device not found") |
| 58 | // ErrDeviceNotReachable is returned when the connection between adapter and agent is broken |
| 59 | ErrDeviceNotReachable = status.Errorf(codes.Unavailable, VolthaErrorMessageFormat, DeviceUnreachable, "Device is not reachable") |
| 60 | // ErrWrongDevice is returned when the received request has wrong device (parent/child) |
| 61 | ErrWrongDevice = status.Errorf(codes.FailedPrecondition, VolthaErrorMessageFormat, PrerequisiteNotMet, "Wrong device in request") |
| 62 | // ErrDeviceNotEnabledAndUp is returned when the state of the device is neither enabled nor active |
| 63 | ErrDeviceNotEnabledAndUp = status.Errorf(codes.FailedPrecondition, VolthaErrorMessageFormat, ResourceInImproperState, "Device is not enabled and up") |
| 64 | // ErrDeviceDeleted is returned when the state of the device is DELETED |
| 65 | ErrDeviceDeleted = status.Errorf(codes.FailedPrecondition, VolthaErrorMessageFormat, ResourceInImproperState, "Device is deleted") |
| 66 | |
| 67 | // ErrPortNotFound is returned when the port is not present in VOLTHA |
| 68 | ErrPortNotFound = status.Errorf(codes.NotFound, VolthaErrorMessageFormat, ResourceNotFound, "Port not found") |
| 69 | // ErrPortIsInInvalidState is returned when the port is in an invalid state |
| 70 | ErrPortIsInInvalidState = status.Errorf(codes.Internal, VolthaErrorMessageFormat, ResourceInImproperState, "Port is in an invalid state") |
| 71 | |
| 72 | // ErrInvalidParamInRequest is returned when the request contains invalid configuration |
| 73 | ErrInvalidParamInRequest = status.Errorf(codes.InvalidArgument, VolthaErrorMessageFormat, InvalidArgument, "Received invalid configuration in request") |
| 74 | |
| 75 | // ErrImageNotRegistered is returned when the image is not registered |
| 76 | ErrImageNotRegistered = status.Errorf(codes.FailedPrecondition, VolthaErrorMessageFormat, PrerequisiteNotMet, "Image is not registered") |
| 77 | // ErrImageDownloadInProgress is returned when the image download is in progress |
| 78 | ErrImageDownloadInProgress = status.Errorf(codes.FailedPrecondition, VolthaErrorMessageFormat, MethodNotAllowed, "Image download is in progress") |
| 79 | ) |
| 80 | |
| 81 | // ConvertToVolthaErrorFormat converts the error to Voltha error format |
| 82 | func ConvertToVolthaErrorFormat(err error) error { |
| 83 | st, ok := status.FromError(err) |
| 84 | if !ok { |
| 85 | return err |
| 86 | } |
| 87 | return status.Errorf(st.Code(), VolthaErrorMessageFormat, GrpcToVolthaErrorCodeMap[st.Code()], st.Message()) |
| 88 | } |
| 89 | |
| 90 | const ( |
vinokuma | 926cb3e | 2023-03-29 11:41:06 +0530 | [diff] [blame] | 91 | //Success is returned when there is no error - 0 |
| 92 | Success NBErrorCode = iota |
| 93 | //InvalidURL is returned when the URL specified for the request is invalid - 1 |
| 94 | InvalidURL |
| 95 | //MissingArgument is returned when the mandatory/conditionally mandatory argument is missing - 2 |
| 96 | MissingArgument |
| 97 | //RequestTimeout is returned when the request timed out. - 3 |
| 98 | RequestTimeout |
| 99 | //ResourceAlreadyExists is returned when the resource already exists and create for the same is not allowed - 4 |
| 100 | ResourceAlreadyExists |
| 101 | //ResourceInImproperState is returned when the resource is in improper state to process the request. - 5 |
| 102 | ResourceInImproperState |
| 103 | //DeviceUnreachable is returned when the device is not reachable - 6 |
| 104 | DeviceUnreachable |
| 105 | //OperationAlreadyInProgress is returned when the requested operation is already in progress - 7 |
| 106 | OperationAlreadyInProgress |
| 107 | //InvalidConfig is returned when the configuration provided is invalid - 8 |
| 108 | InvalidConfig |
| 109 | //ResourceNotFound is returned when the resource is not found - 9 |
| 110 | ResourceNotFound |
| 111 | //MethodNotAllowed is returned when the requested method is not allowed - 10 |
| 112 | MethodNotAllowed |
| 113 | //ResourceInUse is returned when the resource is in use, the delete of the resource is not allowed when in use - 11 |
| 114 | ResourceInUse |
| 115 | //JobIDNotFound is returned when the Job ID not found - 12 |
| 116 | JobIDNotFound |
| 117 | //JobIDAlreadyInUse is returned when the Job ID already in use - 13 |
| 118 | JobIDAlreadyInUse |
| 119 | //PeerUnreachable is returned when the peer is unreachable -14 |
| 120 | PeerUnreachable |
| 121 | //InvalidPatchOperation is returned when the parameter(s) mentioned in the patch operation are invalid - 15 |
| 122 | InvalidPatchOperation |
| 123 | //OLTUnreachable is returned when the OLT is not reachable - 16 |
| 124 | OLTUnreachable |
| 125 | //PrerequisiteNotMet is returned when the required prerequisite is not met to execute the requested procedure - 17 |
| 126 | PrerequisiteNotMet |
| 127 | //MessageEncodeFailed is returned when Message encoding failed - 18 |
| 128 | MessageEncodeFailed |
| 129 | //MessageDecodeFailed is returned when Message decoding failed - 19 |
| 130 | MessageDecodeFailed |
| 131 | //ONTInternalError is returned when Internal error is reported by the ONT - 20 |
| 132 | ONTInternalError |
| 133 | //OLTInternalError is returned when Internal error is reported by the OLT - 21 |
| 134 | OLTInternalError |
| 135 | //VolthaInternalError is returned when Internal error occurred at Voltha - 22 |
| 136 | VolthaInternalError |
| 137 | //ConfigMismatch is returned when the configuration does not match - 23 |
| 138 | ConfigMismatch |
| 139 | //DBOperationFailed is returned when the database operation failed for the key - 24 |
| 140 | DBOperationFailed |
| 141 | //ResourceLimitExceeded is returned when the resource limit exceeded the allowed limit - 25 |
| 142 | ResourceLimitExceeded |
| 143 | //UndefinedEnv is returned when the required environment variable is not defined - 26 |
| 144 | UndefinedEnv |
| 145 | //InvalidArgument is returned when the argument provided is invalid - 27 |
| 146 | InvalidArgument |
| 147 | //InvalidPayload is returned when the configuration payload is invalid - 28 |
| 148 | InvalidPayload |
| 149 | //DuplicateKey is returned when the duplicate entry for the key - 29 |
| 150 | DuplicateKey |
| 151 | //DuplicateValue is returned when the duplicate entry for the value - 30 |
| 152 | DuplicateValue |
| 153 | //UnsupportedOperation is returned when the request operation is not supported - 31 |
| 154 | UnsupportedOperation |
| 155 | //UserUnauthorized is returned when the user is unauthorized to perform the requested operation - 32 |
| 156 | UserUnauthorized |
| 157 | //LiveKPISubscriptionExists is returned when the live KPI subscription exists already for the requested resource - 33 |
| 158 | LiveKPISubscriptionExists |
| 159 | //UnsuccessfulOperation is returned when the requested operation is unsuccessful - 34 |
| 160 | UnsuccessfulOperation |
| 161 | //ResourceInDisabledStateAlready is returned when the resource is in disabled state already - 35 |
| 162 | ResourceInDisabledStateAlready |
| 163 | //ResourceInEnabledStateAlready is returned when the resource is in enabled state already - 36 |
| 164 | ResourceInEnabledStateAlready |
| 165 | //ResourceNotDiscoveredYet is returned when the resource is not discovered yet - 37 |
| 166 | ResourceNotDiscoveredYet |
| 167 | //HighDiskUtilization is returned when the disk utilization is high, consider the disk cleanup. - 38 |
| 168 | HighDiskUtilization |
| 169 | //KafkaError is returned when there is a kafka error - 39 |
| 170 | KafkaError |
| 171 | //ResourceBusy is returned when the component/resource is busy. - 40 |
| 172 | ResourceBusy |
| 173 | // UnsupportedParameter is returned when un supported field is provided in request. -41 |
| 174 | UnsupportedParameter |
| 175 | //JobIDAlreadyExists is returned when the Job ID is already there in DB. -42 |
| 176 | JobIDAlreadyExists |
| 177 | //LiveKPISubscriptionNotFound is returned when the live KPI subscription not found for the requested resource. -42 |
| 178 | LiveKPISubscriptionNotFound |
| 179 | // HostUnreachable is returned when failed to establish the SFTP connection. -44 |
| 180 | HostUnreachable |
| 181 | // DHCPServerUnreachable is returned when dhcp server is unreachable. -45 |
| 182 | DHCPServerUnreachable |
| 183 | // SessionExpired is returned when user session is expired/timeout - 46 |
| 184 | SessionExpired |
| 185 | // AccessDenied is returned when user operation is forbidden - 47 |
| 186 | AccessDenied |
| 187 | // PasswordUpdateRequired is returned when password for the user is about to expire - 48 |
| 188 | PasswordUpdateRequired |
| 189 | // InvalidMessageHeader is returned when token in security request is invalid/nil - 49 |
| 190 | InvalidMessageHeader |
| 191 | // UserAccountBlocked is returned when user account gets blocked after multiple invalid attempts - 50 |
| 192 | UserAccountBlocked |
| 193 | // UserAccountExpired is returned when user account gets expired - 51 |
| 194 | UserAccountExpired |
| 195 | // UserAccountDormant is returned when user account gets dormant - 52 |
| 196 | UserAccountDormant |
| 197 | // InvalidCredentials is returned when credentials are invalid in login request - 53 |
| 198 | InvalidCredentials |
| 199 | // ConcurrentAccessFromMultipleIPs when multiple sessions gets established from same ip - 54 |
| 200 | ConcurrentAccessFromMultipleIPs |
| 201 | // KPIThresholdCrossed when KPI threshold is crossed - 55 |
| 202 | KPIThresholdCrossed |
| 203 | // ONTUnreachable is returned when the ONT is not reachable - 56 |
| 204 | ONTUnreachable |
| 205 | // ResourceUnreachable is returned when the resource is not reachable -57 |
| 206 | ResourceUnreachable |
| 207 | // ONTProcessingError is returned when onu returns processing error for omci message - 58 |
| 208 | ONTProcessingError |
| 209 | // ONTResourceBusy is returned when onu returns device busy error for omci message - 59 |
| 210 | ONTResourceBusy |
| 211 | // ONTMEInstanceExists is returned when onu returns OMCI ME instance exists error for omci message - 60 |
| 212 | ONTMEInstanceExists |
| 213 | // ONTUnknownMEInstance is returned when onu returns OMCI ME Unknown Instance error for omci message - 61 |
| 214 | ONTUnknownMEInstance |
| 215 | // JoinUnsuccessful is returned when an IGMP Join request is unsuccessful - 62 |
| 216 | JoinUnsuccessful |
| 217 | // QueryExpired is returned when there is no response to IGMP Queries from the controller - 63 |
| 218 | QueryExpired |
| 219 | // AvailableBwValidationErr is returned when requested bandwidth is not available on the pon port - 64 |
| 220 | AvailableBwValidationErr |
Naveen Sampath | 04696f7 | 2022-06-13 15:19:14 +0530 | [diff] [blame] | 221 | ) |
| 222 | |
vinokuma | 926cb3e | 2023-03-29 11:41:06 +0530 | [diff] [blame] | 223 | // NBErrorCodeMap converts error code to error description string |
Naveen Sampath | 04696f7 | 2022-06-13 15:19:14 +0530 | [diff] [blame] | 224 | var NBErrorCodeMap = map[NBErrorCode]string{ |
vinokuma | 926cb3e | 2023-03-29 11:41:06 +0530 | [diff] [blame] | 225 | Success: "Success", |
| 226 | InvalidURL: "INVALID_URL", |
| 227 | RequestTimeout: "REQUEST_TIMEOUT", |
| 228 | MissingArgument: "MISSING_ARGUMENT", |
| 229 | ResourceAlreadyExists: "RESOURCE_ALREADY_EXISTS", |
| 230 | ResourceInImproperState: "RESOURCE_IN_IMPROPER_STATE", |
| 231 | DeviceUnreachable: "DEVICE_UNREACHABLE", |
| 232 | OperationAlreadyInProgress: "OPERATION_ALREADY_IN_PROGRESS", |
| 233 | InvalidConfig: "INVALID_CONFIG", |
| 234 | ResourceNotFound: "RESOURCE_NOT_FOUND", |
| 235 | MethodNotAllowed: "METHOD_NOT_ALLOWED", |
| 236 | ResourceInUse: "RESOURCE_IN_USE", |
| 237 | JobIDNotFound: "JOB_ID_NOT_FOUND", |
| 238 | JobIDAlreadyInUse: "JOB_ID_ALREADY_IN_USE", |
| 239 | PeerUnreachable: "PEER_UNREACHABLE", |
| 240 | InvalidPatchOperation: "INVALID_PATCH_OPERATION", |
| 241 | OLTUnreachable: "OLT_UNREACHABLE", |
| 242 | PrerequisiteNotMet: "PREREQUISITE_NOT_MET", |
| 243 | MessageEncodeFailed: "MESSAGE_ENCODE_FAILED", |
| 244 | MessageDecodeFailed: "MESSAGE_DECODE_FAILED", |
| 245 | ONTInternalError: "ONT_INTERNAL_ERROR", |
| 246 | OLTInternalError: "OLT_INTERNAL_ERROR", |
| 247 | VolthaInternalError: "Voltha_INTERNAL_ERROR", |
| 248 | ConfigMismatch: "CONFIG_MISMATCH", |
| 249 | DBOperationFailed: "DB_OPERATION_FAILED", |
| 250 | ResourceLimitExceeded: "RESOURCE_LIMIT_EXCEEDED", |
| 251 | UndefinedEnv: "UNDEFINED_ENV", |
| 252 | InvalidArgument: "INVALID_ARGUMENT", |
| 253 | InvalidPayload: "INVALID_PAYLOAD", |
| 254 | DuplicateKey: "DUPLICATE_KEY", |
| 255 | DuplicateValue: "DUPLICATE_VALUE", |
| 256 | UnsupportedOperation: "UNSUPPORTED_OPERATION", |
| 257 | UserUnauthorized: "USER_UNAUTHORIZED", |
| 258 | LiveKPISubscriptionExists: "LIVE_KPI_SUBSCRIPTION_EXISTS", |
| 259 | UnsuccessfulOperation: "UNSUCCESSFUL_OPERATION", |
| 260 | ResourceInDisabledStateAlready: "RESOURCE_IN_DISABLED_STATE_ALREADY", |
| 261 | ResourceInEnabledStateAlready: "RESOURCE_IN_ENABLED_STATE_ALREADY", |
| 262 | ResourceNotDiscoveredYet: "RESOURCE_NOT_DISCOVERED_YET", |
| 263 | HighDiskUtilization: "HIGH_DISK_UTILIZATION", |
| 264 | KafkaError: "KAFKA_ERROR", |
| 265 | LiveKPISubscriptionNotFound: "LIVE_KPI_SUBSCRIPTION_NOT_FOUND", |
| 266 | ResourceBusy: "RESOURCE_BUSY", |
| 267 | UnsupportedParameter: "UNSUPPORTED_PARAMETER", |
| 268 | JobIDAlreadyExists: "JOB_ID_ALREADY_EXISTS", |
| 269 | HostUnreachable: "HOST_UNREACHABLE", |
| 270 | DHCPServerUnreachable: "DHCP_SERVER_UNREACHABLE", |
| 271 | InvalidMessageHeader: "INVALID_MESSAGE_HEADER", |
| 272 | SessionExpired: "SESSION_EXPIRED", |
| 273 | AccessDenied: "ACCESS_DENIED", |
| 274 | PasswordUpdateRequired: "PASSWORD_UPDATE_REQUIRED", |
| 275 | InvalidCredentials: "INVALID_CREDENTIALS", |
| 276 | UserAccountBlocked: "USER_ACCOUNT_BLOCKED", |
| 277 | UserAccountExpired: "USER_ACCOUNT_EXPIRED", |
| 278 | ConcurrentAccessFromMultipleIPs: "CONCURRENT_ACCESS_FROM_MULTIPLE_IPS", |
| 279 | KPIThresholdCrossed: "KPI_THRESHOLD_CROSSED", |
| 280 | ONTUnreachable: "ONT_UNREACHABLE", |
| 281 | ONTProcessingError: "ONT_PROCESSING_ERROR", |
| 282 | ONTResourceBusy: "ONT_RESOURCE_BUSY", |
| 283 | ONTMEInstanceExists: "ONT_ME_INSTANCE_ALREADY_EXISTS", |
| 284 | ONTUnknownMEInstance: "ONT_UNKNOWN_ME_INSTANCE", |
| 285 | JoinUnsuccessful: "JOIN_UNSUCCESSFUL", |
| 286 | QueryExpired: "QUERY_EXPIRED", |
Naveen Sampath | 04696f7 | 2022-06-13 15:19:14 +0530 | [diff] [blame] | 287 | } |
| 288 | |
| 289 | // GrpcToVolthaErrorCodeMap contains mapping of grpc error code coming from OpenOLT-Agent to Voltha error codes. |
| 290 | var GrpcToVolthaErrorCodeMap = map[codes.Code]NBErrorCode{ |
vinokuma | 926cb3e | 2023-03-29 11:41:06 +0530 | [diff] [blame] | 291 | codes.OK: Success, |
| 292 | codes.Canceled: UnsuccessfulOperation, |
| 293 | codes.Unknown: OLTInternalError, |
| 294 | codes.InvalidArgument: InvalidArgument, |
| 295 | codes.DeadlineExceeded: RequestTimeout, |
| 296 | codes.NotFound: ResourceNotFound, |
| 297 | codes.AlreadyExists: ResourceAlreadyExists, |
| 298 | codes.PermissionDenied: UserUnauthorized, |
| 299 | codes.ResourceExhausted: ResourceLimitExceeded, |
| 300 | codes.FailedPrecondition: PrerequisiteNotMet, |
| 301 | codes.Aborted: UnsuccessfulOperation, |
| 302 | codes.OutOfRange: InvalidArgument, |
| 303 | codes.Unimplemented: UnsupportedOperation, |
| 304 | codes.Internal: OLTInternalError, |
| 305 | codes.Unavailable: ResourceBusy, |
| 306 | codes.DataLoss: OLTInternalError, |
| 307 | codes.Unauthenticated: UserUnauthorized, |
Naveen Sampath | 04696f7 | 2022-06-13 15:19:14 +0530 | [diff] [blame] | 308 | } |
| 309 | |
| 310 | // HTTPStatusCodeToVolthaErrorCodeMap contains mapping of http status code coming from VGC to Voltha error codes. |
| 311 | var HTTPStatusCodeToVolthaErrorCodeMap = map[int]NBErrorCode{ |
vinokuma | 926cb3e | 2023-03-29 11:41:06 +0530 | [diff] [blame] | 312 | http.StatusOK: Success, |
| 313 | http.StatusCreated: Success, |
| 314 | http.StatusAccepted: Success, |
| 315 | http.StatusBadRequest: InvalidPayload, |
| 316 | http.StatusConflict: ResourceInImproperState, |
| 317 | http.StatusInternalServerError: VolthaInternalError, |
Naveen Sampath | 04696f7 | 2022-06-13 15:19:14 +0530 | [diff] [blame] | 318 | } |
| 319 | |
| 320 | // GetErrorInfo - parses the error details from err structure response from voltha |
| 321 | // Return statusCode (uint32) - Error code [0 - Success] |
vinokuma | 926cb3e | 2023-03-29 11:41:06 +0530 | [diff] [blame] | 322 | // status Msg (string) - Error Msg |
Naveen Sampath | 04696f7 | 2022-06-13 15:19:14 +0530 | [diff] [blame] | 323 | func GetErrorInfo(err error) (uint32, string) { |
vinokuma | 926cb3e | 2023-03-29 11:41:06 +0530 | [diff] [blame] | 324 | var statusCode uint32 |
| 325 | var statusMsg string |
| 326 | if status, _ := status.FromError(err); status != nil { |
| 327 | statusCode = uint32(status.Code()) |
| 328 | statusMsg = status.Message() |
| 329 | } else { |
| 330 | statusCode = 0 |
| 331 | } |
| 332 | return statusCode, statusMsg |
Naveen Sampath | 04696f7 | 2022-06-13 15:19:14 +0530 | [diff] [blame] | 333 | } |