// // Copyright 2019 AT&T Intellectual Property // Copyright 2019 Nokia // // 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 controllers import ( "e2mgr/logger" "e2mgr/models" "e2mgr/providers" "e2mgr/rNibWriter" "e2mgr/services" "e2mgr/sessions" "e2mgr/utils" "encoding/json" "errors" "gerrit.o-ran-sc.org/r/ric-plt/nodeb-rnib.git/common" "gerrit.o-ran-sc.org/r/ric-plt/nodeb-rnib.git/reader" "github.com/go-ozzo/ozzo-validation" "github.com/go-ozzo/ozzo-validation/is" "github.com/golang/protobuf/jsonpb" "github.com/julienschmidt/httprouter" "net/http" "net/http/httputil" "strings" "sync" "time" ) const ( parseErrorCode int = 401 validationErrorCode int = 402 notFoundErrorCode int = 404 internalErrorCode int = 501 requiredInputErrorMessage = "Mandatory fields are missing" validationFailedMessage = "Validation failed" parseErrorMessage = "Parse failure" notFoundErrorMessage = "Resource not found" internalErrorMessage = "Internal Server Error. Please try again later" sendMessageErrorMessage = "Failed to send message. For more information please check logs" ) var E2Sessions = make(sessions.E2Sessions) var messageChannel chan *models.E2RequestMessage var errorChannel chan error type NodebController struct { rmrService *services.RmrService Logger *logger.Logger rnibReaderProvider func() reader.RNibReader rnibWriterProvider func() rNibWriter.RNibWriter } func NewNodebController(logger *logger.Logger, rmrService *services.RmrService, rnibReaderProvider func() reader.RNibReader, rnibWriterProvider func() rNibWriter.RNibWriter) *NodebController { messageChannel = make(chan *models.E2RequestMessage) errorChannel = make(chan error) return &NodebController{ rmrService: rmrService, Logger: logger, rnibReaderProvider: rnibReaderProvider, rnibWriterProvider: rnibWriterProvider, } } func prettifyRequest(request *http.Request) string { dump, _ := httputil.DumpRequest(request, true) requestPrettyPrint := strings.Replace(string(dump), "\r\n", " ", -1) return strings.Replace(requestPrettyPrint, "\n", "", -1) } func (rc NodebController) HandleRequest(writer http.ResponseWriter, request *http.Request, params httprouter.Params) { startTime := time.Now() rc.Logger.Infof("[Client -> E2 Manager] #nodeb_controller.HandleRequest - request: %v", prettifyRequest(request)) messageTypeParam := params.ByName("messageType") requestHandlerProvider := providers.NewRequestHandlerProvider(rc.rnibWriterProvider) handler, err := requestHandlerProvider.GetHandler(rc.Logger, messageTypeParam) if err != nil { handleErrorResponse(rc.Logger, http.StatusNotFound, notFoundErrorCode, notFoundErrorMessage, writer, startTime) return } requestDetails, err := parseJson(rc.Logger, request) if err != nil { handleErrorResponse(rc.Logger, http.StatusBadRequest, parseErrorCode, parseErrorMessage, writer, startTime) return } rc.Logger.Infof("#nodeb_controller.HandleRequest - request: %+v", requestDetails) if err := validateRequestDetails(rc.Logger, requestDetails); err != nil { handleErrorResponse(rc.Logger, http.StatusBadRequest, validationErrorCode, requiredInputErrorMessage, writer, startTime) return } err = handler.PreHandle(rc.Logger, &requestDetails) if err != nil { handleErrorResponse(rc.Logger, http.StatusInternalServerError, internalErrorCode, err.Error(), writer, startTime) return } rc.Logger.Infof("[E2 Manager -> Client] #nodeb_controller.HandleRequest - http status: 200") writer.WriteHeader(http.StatusOK) var wg sync.WaitGroup go handler.CreateMessage(rc.Logger, &requestDetails, messageChannel, E2Sessions, startTime, wg) go rc.rmrService.SendMessage(handler.GetMessageType(), messageChannel, errorChannel, wg) wg.Wait() err = <-errorChannel if err != nil { handleErrorResponse(rc.Logger, http.StatusInternalServerError, internalErrorCode, sendMessageErrorMessage, writer, startTime) return } printHandlingRequestElapsedTimeInMs(rc.Logger, startTime) } func (rc NodebController) GetNodebIdList (writer http.ResponseWriter, request *http.Request, params httprouter.Params) { startTime := time.Now() rnibReaderService := services.NewRnibReaderService(rc.rnibReaderProvider) nodebIdList, rnibError := rnibReaderService.GetNodebIdList() if rnibError != nil { rc.Logger.Errorf("%v", rnibError); httpStatusCode, errorCode, errorMessage := rnibErrorToHttpError(rnibError) handleErrorResponse(rc.Logger,httpStatusCode,errorCode,errorMessage,writer,startTime ) return; } pmList:= utils.ConvertNodebIdListToProtoMessageList(*nodebIdList) result, err := utils.MarshalProtoMessageListToJsonArray(pmList) if err != nil { rc.Logger.Errorf("%v", err); handleErrorResponse(rc.Logger,http.StatusInternalServerError,internalErrorCode,internalErrorMessage,writer,startTime ) return; } writer.Header().Set("Content-Type", "application/json") rc.Logger.Infof("[E2 Manager -> Client] #nodeb_controller.GetNodebIdList - response: %s", result) writer.Write([]byte(result)) } func (rc NodebController) GetNodeb(writer http.ResponseWriter, request *http.Request, params httprouter.Params) { startTime := time.Now() ranName := params.ByName("ranName") // WAS: respondingNode, rnibError := reader.GetRNibReader().GetNodeb(ranName) rnibReaderService := services.NewRnibReaderService(rc.rnibReaderProvider); respondingNode, rnibError := rnibReaderService.GetNodeb(ranName) if rnibError != nil { rc.Logger.Errorf("%v", rnibError) httpStatusCode, errorCode, errorMessage := rnibErrorToHttpError(rnibError) handleErrorResponse(rc.Logger, httpStatusCode, errorCode, errorMessage, writer, startTime) return } m := jsonpb.Marshaler{} result, err := m.MarshalToString(respondingNode) if err != nil { rc.Logger.Errorf("%v", err) handleErrorResponse(rc.Logger, http.StatusInternalServerError, internalErrorCode, internalErrorMessage, writer, startTime) return } writer.Header().Set("Content-Type", "application/json") rc.Logger.Infof("[E2 Manager -> Client] #nodeb_controller.GetNodeb - response: %s", result) writer.Write([]byte(result)) } func (rc NodebController) HandleHealthCheckRequest(writer http.ResponseWriter, request *http.Request, params httprouter.Params) { //fmt.Println("[X-APP -> Client] #HandleHealthCheckRequest - http status: 200") writer.WriteHeader(http.StatusOK) } func parseJson(logger *logger.Logger, request *http.Request) (models.RequestDetails, error) { var requestDetails models.RequestDetails if err := json.NewDecoder(request.Body).Decode(&requestDetails); err != nil { logger.Errorf("#nodeb_controller.parseJson - cannot deserialize incoming request. request: %v, error: %v", request, err) return requestDetails, err } return requestDetails, nil } func validateRequestDetails(logger *logger.Logger, requestDetails models.RequestDetails) error { if requestDetails.RanPort == 0 { logger.Errorf("#nodeb_controller.validateRequestDetails - validation failure: port cannot be zero") return errors.New("port: cannot be blank") } err := validation.ValidateStruct(&requestDetails, validation.Field(&requestDetails.RanIp, validation.Required, is.IP), validation.Field(&requestDetails.RanName, validation.Required), ) if err != nil { logger.Errorf("#nodeb_controller.validateRequestDetails - validation failure, error: %v", err) } return err } func handleErrorResponse(logger *logger.Logger, httpStatus int, errorCode int, errorMessage string, writer http.ResponseWriter, startTime time.Time) { errorResponseDetails := models.ErrorResponse{errorCode, errorMessage} errorResponse, _ := json.Marshal(errorResponseDetails) printHandlingRequestElapsedTimeInMs(logger, startTime) logger.Infof("[E2 Manager -> Client] #nodeb_controller.handleErrorResponse - http status: %d, error response: %+v", httpStatus, errorResponseDetails) writer.Header().Set("Content-Type", "application/json") writer.WriteHeader(httpStatus) _, err := writer.Write(errorResponse) if err != nil { logger.Errorf("#nodeb_controller.handleErrorResponse - Cannot send response. writer:%v", writer) } } func printHandlingRequestElapsedTimeInMs(logger *logger.Logger, startTime time.Time) { logger.Infof("Summary: #nodeb_controller.printElapsedTimeInMs - Elapsed time for handling request from client to E2 termination: %f ms", float64(time.Since(startTime))/float64(time.Millisecond)) } func rnibErrorToHttpError(rnibError common.IRNibError) (int, int, string) { switch rnibError.GetCode() { case common.RESOURCE_NOT_FOUND: return http.StatusNotFound, notFoundErrorCode, notFoundErrorMessage case common.INTERNAL_ERROR: return http.StatusInternalServerError, internalErrorCode, internalErrorMessage case common.VALIDATION_ERROR: return http.StatusBadRequest, validationErrorCode, validationFailedMessage default: return http.StatusInternalServerError, internalErrorCode, internalErrorMessage } }