[RIC-247] RAN Health Check Request API - Validation
[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         UpdateEnb(writer http.ResponseWriter, r *http.Request)
52         GetNodebIdList(writer http.ResponseWriter, r *http.Request)
53         SetGeneralConfiguration(writer http.ResponseWriter, r *http.Request)
54         AddEnb(writer http.ResponseWriter, r *http.Request)
55         DeleteEnb(writer http.ResponseWriter, r *http.Request)
56         HealthCheckRequest(writer http.ResponseWriter, r *http.Request)
57 }
58
59 type NodebController struct {
60         logger          *logger.Logger
61         handlerProvider *httpmsghandlerprovider.IncomingRequestHandlerProvider
62 }
63
64 func NewNodebController(logger *logger.Logger, handlerProvider *httpmsghandlerprovider.IncomingRequestHandlerProvider) *NodebController {
65         return &NodebController{
66                 logger:          logger,
67                 handlerProvider: handlerProvider,
68         }
69 }
70
71 func (c *NodebController) GetNodebIdList(writer http.ResponseWriter, r *http.Request) {
72         c.logger.Infof("[Client -> E2 Manager] #NodebController.GetNodebIdList - request: %v", c.prettifyRequest(r))
73
74         c.handleRequest(writer, &r.Header, httpmsghandlerprovider.GetNodebIdListRequest, nil, false, http.StatusOK)
75 }
76
77 func (c *NodebController) GetNodeb(writer http.ResponseWriter, r *http.Request) {
78         c.logger.Infof("[Client -> E2 Manager] #NodebController.GetNodeb - request: %v", c.prettifyRequest(r))
79         vars := mux.Vars(r)
80         ranName := vars["ranName"]
81         request := models.GetNodebRequest{RanName: ranName}
82         c.handleRequest(writer, &r.Header, httpmsghandlerprovider.GetNodebRequest, request, false, http.StatusOK)
83 }
84
85 func (c *NodebController) UpdateGnb(writer http.ResponseWriter, r *http.Request) {
86         c.logger.Infof("[Client -> E2 Manager] #NodebController.UpdateGnb - request: %v", c.prettifyRequest(r))
87         vars := mux.Vars(r)
88         ranName := vars[ParamRanName]
89
90         request := models.UpdateGnbRequest{}
91
92         gnb := entities.Gnb{}
93
94         if !c.extractRequestBodyToProto(r, &gnb, writer) {
95                 return
96         }
97
98         request.Gnb = &gnb
99         request.RanName = ranName
100         c.handleRequest(writer, &r.Header, httpmsghandlerprovider.UpdateGnbRequest, &request, true, http.StatusOK)
101 }
102
103 func (c *NodebController) UpdateEnb(writer http.ResponseWriter, r *http.Request) {
104         c.logger.Infof("[Client -> E2 Manager] #NodebController.UpdateEnb - request: %v", c.prettifyRequest(r))
105
106         defer r.Body.Close()
107         body, err := ioutil.ReadAll(r.Body)
108
109         if err != nil {
110                 c.logger.Errorf("[Client -> E2 Manager] #NodebController.UpdateEnb - unable to read request body - error: %s", err)
111                 c.handleErrorResponse(e2managererrors.NewInvalidJsonError(), writer)
112                 return
113         }
114
115         updateEnbRequest := models.UpdateEnbRequest{}
116         err = json.Unmarshal(body, &updateEnbRequest)
117
118         if err != nil {
119                 c.logger.Errorf("[Client -> E2 Manager] #NodebController.UpdateEnb - unable to unmarshal json - error: %s", err)
120                 c.handleErrorResponse(e2managererrors.NewInvalidJsonError(), writer)
121                 return
122         }
123
124         vars := mux.Vars(r)
125         ranName := vars[ParamRanName]
126
127         updateEnbRequest.RanName = ranName
128
129         c.handleRequest(writer, &r.Header, httpmsghandlerprovider.UpdateEnbRequest, &updateEnbRequest, true, http.StatusOK)
130 }
131
132 func (c *NodebController) AddEnb(writer http.ResponseWriter, r *http.Request) {
133         c.logger.Infof("[Client -> E2 Manager] #NodebController.AddEnb - request: %v", c.prettifyRequest(r))
134
135         defer r.Body.Close()
136         body, err := ioutil.ReadAll(r.Body)
137
138         if err != nil {
139                 c.logger.Errorf("[Client -> E2 Manager] #NodebController.AddEnb - unable to read request body - error: %s", err)
140                 c.handleErrorResponse(e2managererrors.NewInvalidJsonError(), writer)
141                 return
142         }
143
144         addEnbRequest := models.AddEnbRequest{}
145         err = json.Unmarshal(body, &addEnbRequest)
146
147         if err != nil {
148                 c.logger.Errorf("[Client -> E2 Manager] #NodebController.AddEnb - unable to unmarshal json - error: %s", err)
149                 c.handleErrorResponse(e2managererrors.NewInvalidJsonError(), writer)
150                 return
151         }
152
153         c.handleRequest(writer, &r.Header, httpmsghandlerprovider.AddEnbRequest, &addEnbRequest, true, http.StatusCreated)
154 }
155
156 func (c *NodebController) DeleteEnb(writer http.ResponseWriter, r *http.Request) {
157         c.logger.Infof("[Client -> E2 Manager] #NodebController.DeleteEnb - request: %v", c.prettifyRequest(r))
158         vars := mux.Vars(r)
159         ranName := vars["ranName"]
160         request := &models.DeleteEnbRequest{RanName: ranName}
161         c.handleRequest(writer, &r.Header, httpmsghandlerprovider.DeleteEnbRequest, request, true, http.StatusNoContent)
162 }
163
164 func (c *NodebController) SetGeneralConfiguration(writer http.ResponseWriter, r *http.Request) {
165         c.logger.Infof("[Client -> E2 Manager] #NodebController.SetGeneralConfiguration - request: %v", c.prettifyRequest(r))
166
167         request := models.GeneralConfigurationRequest{}
168
169         if !c.extractJsonBodyDisallowUnknownFields(r, &request, writer) {
170                 return
171         }
172         c.handleRequest(writer, &r.Header, httpmsghandlerprovider.SetGeneralConfigurationRequest, request, false, http.StatusOK)
173 }
174
175 func (c *NodebController) Shutdown(writer http.ResponseWriter, r *http.Request) {
176         c.logger.Infof("[Client -> E2 Manager] #NodebController.Shutdown - request: %v", c.prettifyRequest(r))
177         c.handleRequest(writer, &r.Header, httpmsghandlerprovider.ShutdownRequest, nil, false, http.StatusNoContent)
178 }
179
180 func (c *NodebController) X2Reset(writer http.ResponseWriter, r *http.Request) {
181         c.logger.Infof("[Client -> E2 Manager] #NodebController.X2Reset - request: %v", c.prettifyRequest(r))
182         request := models.ResetRequest{}
183         vars := mux.Vars(r)
184         ranName := vars[ParamRanName]
185
186         if err := c.extractJsonBody(r, &request); err != nil {
187                 c.handleErrorResponse(err, writer)
188                 return
189         }
190         request.RanName = ranName
191         c.handleRequest(writer, &r.Header, httpmsghandlerprovider.ResetRequest, request, false, http.StatusNoContent)
192 }
193
194 func (c *NodebController) HealthCheckRequest(writer http.ResponseWriter, r *http.Request) {
195         c.logger.Infof("[Client -> E2 Manager] #NodebController.HealthCheckRequest - request: %v", c.prettifyRequest(r))
196
197         request := models.HealthCheckRequest{}
198
199         if err := c.extractJsonBody(r, &request); err != nil {
200                 c.handleErrorResponse(err, writer)
201                 return
202         }
203
204         c.handleRequest(writer, &r.Header, httpmsghandlerprovider.HealthCheckRequest, request, true, http.StatusNoContent)
205 }
206
207 func (c *NodebController) extractRequestBodyToProto(r *http.Request, pb proto.Message, writer http.ResponseWriter) bool {
208         defer r.Body.Close()
209
210         err := jsonpb.Unmarshal(r.Body, pb)
211
212         if err != nil {
213                 c.logger.Errorf("[Client -> E2 Manager] #NodebController.extractJsonBody - unable to extract json body - error: %s", err)
214                 c.handleErrorResponse(e2managererrors.NewInvalidJsonError(), writer)
215                 return false
216         }
217
218         return true
219 }
220
221 func (c *NodebController) extractJsonBodyDisallowUnknownFields(r *http.Request, request models.Request, writer http.ResponseWriter) bool {
222         defer r.Body.Close()
223
224         decoder := json.NewDecoder(r.Body)
225         decoder.DisallowUnknownFields()
226
227         if err := decoder.Decode(&request); err != nil {
228                 c.logger.Errorf("[Client -> E2 Manager] #NodebController.extractJsonBody - unable to extract json body - error: %s", err)
229                 c.handleErrorResponse(e2managererrors.NewInvalidJsonError(), writer)
230                 return false
231         }
232
233         return true
234 }
235
236 func (c *NodebController) extractJsonBody(r *http.Request, request models.Request) error {
237         defer r.Body.Close()
238         body, err := ioutil.ReadAll(io.LimitReader(r.Body, LimitRequest))
239
240         if err != nil {
241                 c.logger.Errorf("[Client -> E2 Manager] #NodebController.extractJsonBody - unable to extract json body - error: %s", err)
242                 return e2managererrors.NewInvalidJsonError()
243         }
244
245         err = json.Unmarshal(body, &request)
246         if err != nil {
247                 c.logger.Errorf("[Client -> E2 Manager] #NodebController.extractJsonBody - unable to extract json body - error: %s", err)
248                 return e2managererrors.NewInvalidJsonError()
249         }
250
251         return nil
252 }
253
254 func (c *NodebController) handleRequest(writer http.ResponseWriter, header *http.Header, requestName httpmsghandlerprovider.IncomingRequest, request models.Request, validateRequestHeaders bool, successStatusCode int) {
255
256         if validateRequestHeaders {
257
258                 err := c.validateRequestHeader(header)
259                 if err != nil {
260                         c.handleErrorResponse(err, writer)
261                         return
262                 }
263         }
264
265         handler, err := c.handlerProvider.GetHandler(requestName)
266
267         if err != nil {
268                 c.handleErrorResponse(err, writer)
269                 return
270         }
271
272         response, err := handler.Handle(request)
273
274         if err != nil {
275                 c.handleErrorResponse(err, writer)
276                 return
277         }
278
279         if successStatusCode == http.StatusNoContent {
280                 writer.WriteHeader(successStatusCode)
281                 c.logger.Infof("[E2 Manager -> Client] #NodebController.handleRequest - status response: %v", http.StatusNoContent)
282                 return
283         }
284
285         result, err := response.Marshal()
286
287         if err != nil {
288                 c.handleErrorResponse(err, writer)
289                 return
290         }
291
292         c.logger.Infof("[E2 Manager -> Client] #NodebController.handleRequest - response: %s", result)
293         writer.Header().Set(ContentType, ApplicationJson)
294         writer.WriteHeader(successStatusCode)
295         writer.Write(result)
296 }
297
298 func (c *NodebController) validateRequestHeader(header *http.Header) error {
299
300         if header.Get(ContentType) != ApplicationJson {
301                 c.logger.Errorf("#NodebController.validateRequestHeader - validation failure, incorrect content type")
302
303                 return e2managererrors.NewHeaderValidationError()
304         }
305         return nil
306 }
307
308 func (c *NodebController) handleErrorResponse(err error, writer http.ResponseWriter) {
309
310         var errorResponseDetails models.ErrorResponse
311         var httpError int
312
313         if err != nil {
314                 switch err.(type) {
315                 case *e2managererrors.RnibDbError:
316                         e2Error, _ := err.(*e2managererrors.RnibDbError)
317                         errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message}
318                         httpError = http.StatusInternalServerError
319                 case *e2managererrors.CommandAlreadyInProgressError:
320                         e2Error, _ := err.(*e2managererrors.CommandAlreadyInProgressError)
321                         errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message}
322                         httpError = http.StatusMethodNotAllowed
323                 case *e2managererrors.HeaderValidationError:
324                         e2Error, _ := err.(*e2managererrors.HeaderValidationError)
325                         errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message}
326                         httpError = http.StatusUnsupportedMediaType
327                 case *e2managererrors.WrongStateError:
328                         e2Error, _ := err.(*e2managererrors.WrongStateError)
329                         errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message}
330                         httpError = http.StatusBadRequest
331                 case *e2managererrors.RequestValidationError:
332                         e2Error, _ := err.(*e2managererrors.RequestValidationError)
333                         errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message}
334                         httpError = http.StatusBadRequest
335                 case *e2managererrors.InvalidJsonError:
336                         e2Error, _ := err.(*e2managererrors.InvalidJsonError)
337                         errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message}
338                         httpError = http.StatusBadRequest
339                 case *e2managererrors.RmrError:
340                         e2Error, _ := err.(*e2managererrors.RmrError)
341                         errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message}
342                         httpError = http.StatusInternalServerError
343                 case *e2managererrors.ResourceNotFoundError:
344                         e2Error, _ := err.(*e2managererrors.ResourceNotFoundError)
345                         errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message}
346                         httpError = http.StatusNotFound
347                 case *e2managererrors.E2TInstanceAbsenceError:
348                         e2Error, _ := err.(*e2managererrors.E2TInstanceAbsenceError)
349                         errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message}
350                         httpError = http.StatusServiceUnavailable
351                 case *e2managererrors.RoutingManagerError:
352                         e2Error, _ := err.(*e2managererrors.RoutingManagerError)
353                         errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message}
354                         httpError = http.StatusServiceUnavailable
355                 case *e2managererrors.NodebExistsError:
356                         e2Error, _ := err.(*e2managererrors.NodebExistsError)
357                         errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message}
358                         httpError = http.StatusBadRequest
359                 case *e2managererrors.NoConnectedRanError:
360                         e2Error, _ := err.(*e2managererrors.NoConnectedRanError)
361                         errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message}
362                         httpError = http.StatusNotFound
363                 default:
364                         e2Error := e2managererrors.NewInternalError()
365                         errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message}
366                         httpError = http.StatusInternalServerError
367                 }
368         }
369         errorResponse, _ := json.Marshal(errorResponseDetails)
370
371         c.logger.Errorf("[E2 Manager -> Client] #NodebController.handleErrorResponse - http status: %d, error response: %+v", httpError, errorResponseDetails)
372
373         writer.Header().Set(ContentType, ApplicationJson)
374         writer.WriteHeader(httpError)
375         _, err = writer.Write(errorResponse)
376 }
377
378 func (c *NodebController) prettifyRequest(request *http.Request) string {
379         dump, _ := httputil.DumpRequest(request, true)
380         requestPrettyPrint := strings.Replace(string(dump), "\r\n", " ", -1)
381         return strings.Replace(requestPrettyPrint, "\n", "", -1)
382 }