[RIC-431] Add UTs | Update AddEnbRequest | Update Swagger
[ric-plt/e2mgr.git] / E2Manager / controllers / nodeb_controller.go
1 //
2 // Copyright 2019 AT&T Intellectual Property
3 // Copyright 2019 Nokia
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //      http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16
17 //  This source code is part of the near-RT RIC (RAN Intelligent Controller)
18 //  platform project (RICP).
19
20 package controllers
21
22 import (
23         "e2mgr/e2managererrors"
24         "e2mgr/logger"
25         "e2mgr/models"
26         "e2mgr/providers/httpmsghandlerprovider"
27         "encoding/json"
28         "gerrit.o-ran-sc.org/r/ric-plt/nodeb-rnib.git/entities"
29         "github.com/golang/protobuf/jsonpb"
30         "github.com/golang/protobuf/proto"
31         "github.com/gorilla/mux"
32         "io"
33         "io/ioutil"
34         "net/http"
35         "net/http/httputil"
36         "strings"
37 )
38
39 const (
40         ParamRanName = "ranName"
41         LimitRequest = 2000
42 )
43 const ApplicationJson = "application/json"
44 const ContentType = "Content-Type"
45
46 type INodebController interface {
47         Shutdown(writer http.ResponseWriter, r *http.Request)
48         X2Reset(writer http.ResponseWriter, r *http.Request)
49         GetNodeb(writer http.ResponseWriter, r *http.Request)
50         UpdateGnb(writer http.ResponseWriter, r *http.Request)
51         GetNodebIdList(writer http.ResponseWriter, r *http.Request)
52         SetGeneralConfiguration(writer http.ResponseWriter, r *http.Request)
53         AddEnb(writer http.ResponseWriter, r *http.Request)
54 }
55
56 type NodebController struct {
57         logger          *logger.Logger
58         handlerProvider *httpmsghandlerprovider.IncomingRequestHandlerProvider
59 }
60
61 func NewNodebController(logger *logger.Logger, handlerProvider *httpmsghandlerprovider.IncomingRequestHandlerProvider) *NodebController {
62         return &NodebController{
63                 logger:          logger,
64                 handlerProvider: handlerProvider,
65         }
66 }
67
68 func (c *NodebController) GetNodebIdList(writer http.ResponseWriter, r *http.Request) {
69         c.logger.Infof("[Client -> E2 Manager] #NodebController.GetNodebIdList - request: %v", c.prettifyRequest(r))
70
71         c.handleRequest(writer, &r.Header, httpmsghandlerprovider.GetNodebIdListRequest, nil, false, http.StatusOK)
72 }
73
74 func (c *NodebController) GetNodeb(writer http.ResponseWriter, r *http.Request) {
75         c.logger.Infof("[Client -> E2 Manager] #NodebController.GetNodeb - request: %v", c.prettifyRequest(r))
76         vars := mux.Vars(r)
77         ranName := vars["ranName"]
78         request := models.GetNodebRequest{RanName: ranName}
79         c.handleRequest(writer, &r.Header, httpmsghandlerprovider.GetNodebRequest, request, false, http.StatusOK)
80 }
81
82 func (c *NodebController) UpdateGnb(writer http.ResponseWriter, r *http.Request) {
83         c.logger.Infof("[Client -> E2 Manager] #NodebController.UpdateGnb - request: %v", c.prettifyRequest(r))
84         vars := mux.Vars(r)
85         ranName := vars[ParamRanName]
86
87         request := models.UpdateGnbRequest{}
88
89         gnb := entities.Gnb{}
90
91         if !c.extractRequestBodyToProto(r, &gnb, writer) {
92                 return
93         }
94
95         request.Gnb = &gnb
96         request.RanName = ranName
97         c.handleRequest(writer, &r.Header, httpmsghandlerprovider.UpdateGnbRequest, request, true, http.StatusOK)
98 }
99
100 func (c *NodebController) AddEnb(writer http.ResponseWriter, r *http.Request) {
101         c.logger.Infof("[Client -> E2 Manager] #NodebController.AddEnb - request: %v", c.prettifyRequest(r))
102
103         defer r.Body.Close()
104         body, err := ioutil.ReadAll(io.LimitReader(r.Body, LimitRequest))
105
106         if err != nil {
107                 c.logger.Errorf("[Client -> E2 Manager] #NodebController.AddEnb - unable to read request body - error: %s", err)
108                 c.handleErrorResponse(e2managererrors.NewInvalidJsonError(), writer)
109                 return
110         }
111
112         addEnbRequest := models.AddEnbRequest{}
113         err = json.Unmarshal(body, &addEnbRequest)
114
115         if err != nil {
116                 c.logger.Errorf("[Client -> E2 Manager] #NodebController.AddEnb - unable to unmarshal json - error: %s", err)
117                 c.handleErrorResponse(e2managererrors.NewInvalidJsonError(), writer)
118                 return
119         }
120
121         c.handleRequest(writer, &r.Header, httpmsghandlerprovider.AddEnbRequest, &addEnbRequest, true, http.StatusCreated)
122 }
123
124 func (c *NodebController) SetGeneralConfiguration(writer http.ResponseWriter, r *http.Request) {
125         c.logger.Infof("[Client -> E2 Manager] #NodebController.SetGeneralConfiguration - request: %v", c.prettifyRequest(r))
126
127         request := models.GeneralConfigurationRequest{}
128
129         if !c.extractJsonBodyDisallowUnknownFields(r, &request, writer) {
130                 return
131         }
132         c.handleRequest(writer, &r.Header, httpmsghandlerprovider.SetGeneralConfigurationRequest, request, false, http.StatusOK)
133 }
134
135 func (c *NodebController) Shutdown(writer http.ResponseWriter, r *http.Request) {
136         c.logger.Infof("[Client -> E2 Manager] #NodebController.Shutdown - request: %v", c.prettifyRequest(r))
137         c.handleRequest(writer, &r.Header, httpmsghandlerprovider.ShutdownRequest, nil, false, http.StatusNoContent)
138 }
139
140 func (c *NodebController) X2Reset(writer http.ResponseWriter, r *http.Request) {
141         c.logger.Infof("[Client -> E2 Manager] #NodebController.X2Reset - request: %v", c.prettifyRequest(r))
142         request := models.ResetRequest{}
143         vars := mux.Vars(r)
144         ranName := vars[ParamRanName]
145
146         if err := c.extractJsonBody(r, &request); err != nil {
147                 c.handleErrorResponse(err, writer)
148                 return
149         }
150         request.RanName = ranName
151         c.handleRequest(writer, &r.Header, httpmsghandlerprovider.ResetRequest, request, false, http.StatusNoContent)
152 }
153
154 func (c *NodebController) extractRequestBodyToProto(r *http.Request, pb proto.Message, writer http.ResponseWriter) bool {
155         defer r.Body.Close()
156
157         err := jsonpb.Unmarshal(r.Body, pb)
158
159         if err != nil {
160                 c.logger.Errorf("[Client -> E2 Manager] #NodebController.extractJsonBody - unable to extract json body - error: %s", err)
161                 c.handleErrorResponse(e2managererrors.NewInvalidJsonError(), writer)
162                 return false
163         }
164
165         return true
166 }
167
168 func (c *NodebController) extractJsonBodyDisallowUnknownFields(r *http.Request, request models.Request, writer http.ResponseWriter) bool {
169         defer r.Body.Close()
170
171         decoder := json.NewDecoder(r.Body)
172         decoder.DisallowUnknownFields()
173
174         if err := decoder.Decode(&request); err != nil {
175                 c.logger.Errorf("[Client -> E2 Manager] #NodebController.extractJsonBody - unable to extract json body - error: %s", err)
176                 c.handleErrorResponse(e2managererrors.NewInvalidJsonError(), writer)
177                 return false
178         }
179
180         return true
181 }
182
183 func (c *NodebController) extractJsonBody(r *http.Request, request models.Request) error {
184         defer r.Body.Close()
185         body, err := ioutil.ReadAll(io.LimitReader(r.Body, LimitRequest))
186
187         if err != nil {
188                 c.logger.Errorf("[Client -> E2 Manager] #NodebController.extractJsonBody - unable to extract json body - error: %s", err)
189                 return e2managererrors.NewInvalidJsonError()
190         }
191
192         err = json.Unmarshal(body, &request)
193         if err != nil {
194                 c.logger.Errorf("[Client -> E2 Manager] #NodebController.extractJsonBody - unable to extract json body - error: %s", err)
195                 return e2managererrors.NewInvalidJsonError()
196         }
197
198         return nil
199 }
200
201 func (c *NodebController) handleRequest(writer http.ResponseWriter, header *http.Header, requestName httpmsghandlerprovider.IncomingRequest, request models.Request, validateRequestHeaders bool, successStatusCode int) {
202
203         if validateRequestHeaders {
204
205                 err := c.validateRequestHeader(header)
206                 if err != nil {
207                         c.handleErrorResponse(err, writer)
208                         return
209                 }
210         }
211
212         handler, err := c.handlerProvider.GetHandler(requestName)
213
214         if err != nil {
215                 c.handleErrorResponse(err, writer)
216                 return
217         }
218
219         response, err := handler.Handle(request)
220
221         if err != nil {
222                 c.handleErrorResponse(err, writer)
223                 return
224         }
225
226         if successStatusCode == http.StatusNoContent {
227                 writer.WriteHeader(successStatusCode)
228                 c.logger.Infof("[E2 Manager -> Client] #NodebController.handleRequest - status response: %v", http.StatusNoContent)
229                 return
230         }
231
232         result, err := response.Marshal()
233
234         if err != nil {
235                 c.handleErrorResponse(err, writer)
236                 return
237         }
238
239         c.logger.Infof("[E2 Manager -> Client] #NodebController.handleRequest - response: %s", result)
240         writer.Header().Set(ContentType, ApplicationJson)
241         writer.WriteHeader(successStatusCode)
242         writer.Write(result)
243 }
244
245 func (c *NodebController) validateRequestHeader(header *http.Header) error {
246
247         if header.Get(ContentType) != ApplicationJson {
248                 c.logger.Errorf("#NodebController.validateRequestHeader - validation failure, incorrect content type")
249
250                 return e2managererrors.NewHeaderValidationError()
251         }
252         return nil
253 }
254
255 func (c *NodebController) handleErrorResponse(err error, writer http.ResponseWriter) {
256
257         var errorResponseDetails models.ErrorResponse
258         var httpError int
259
260         if err != nil {
261                 switch err.(type) {
262                 case *e2managererrors.RnibDbError:
263                         e2Error, _ := err.(*e2managererrors.RnibDbError)
264                         errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message}
265                         httpError = http.StatusInternalServerError
266                 case *e2managererrors.CommandAlreadyInProgressError:
267                         e2Error, _ := err.(*e2managererrors.CommandAlreadyInProgressError)
268                         errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message}
269                         httpError = http.StatusMethodNotAllowed
270                 case *e2managererrors.HeaderValidationError:
271                         e2Error, _ := err.(*e2managererrors.HeaderValidationError)
272                         errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message}
273                         httpError = http.StatusUnsupportedMediaType
274                 case *e2managererrors.WrongStateError:
275                         e2Error, _ := err.(*e2managererrors.WrongStateError)
276                         errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message}
277                         httpError = http.StatusBadRequest
278                 case *e2managererrors.RequestValidationError:
279                         e2Error, _ := err.(*e2managererrors.RequestValidationError)
280                         errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message}
281                         httpError = http.StatusBadRequest
282                 case *e2managererrors.InvalidJsonError:
283                         e2Error, _ := err.(*e2managererrors.InvalidJsonError)
284                         errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message}
285                         httpError = http.StatusBadRequest
286                 case *e2managererrors.RmrError:
287                         e2Error, _ := err.(*e2managererrors.RmrError)
288                         errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message}
289                         httpError = http.StatusInternalServerError
290                 case *e2managererrors.ResourceNotFoundError:
291                         e2Error, _ := err.(*e2managererrors.ResourceNotFoundError)
292                         errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message}
293                         httpError = http.StatusNotFound
294                 case *e2managererrors.E2TInstanceAbsenceError:
295                         e2Error, _ := err.(*e2managererrors.E2TInstanceAbsenceError)
296                         errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message}
297                         httpError = http.StatusServiceUnavailable
298                 case *e2managererrors.RoutingManagerError:
299                         e2Error, _ := err.(*e2managererrors.RoutingManagerError)
300                         errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message}
301                         httpError = http.StatusServiceUnavailable
302                 case *e2managererrors.NodebExistsError:
303                         e2Error, _ := err.(*e2managererrors.NodebExistsError)
304                         errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message}
305                         httpError = http.StatusBadRequest
306                 default:
307                         e2Error := e2managererrors.NewInternalError()
308                         errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message}
309                         httpError = http.StatusInternalServerError
310                 }
311         }
312         errorResponse, _ := json.Marshal(errorResponseDetails)
313
314         c.logger.Errorf("[E2 Manager -> Client] #NodebController.handleErrorResponse - http status: %d, error response: %+v", httpError, errorResponseDetails)
315
316         writer.Header().Set(ContentType, ApplicationJson)
317         writer.WriteHeader(httpError)
318         _, err = writer.Write(errorResponse)
319 }
320
321 func (c *NodebController) prettifyRequest(request *http.Request) string {
322         dump, _ := httputil.DumpRequest(request, true)
323         requestPrettyPrint := strings.Replace(string(dump), "\r\n", " ", -1)
324         return strings.Replace(requestPrettyPrint, "\n", "", -1)
325 }