Added an optional parameter to send config during xapp registration to
[ric-plt/appmgr.git] / pkg / restful / restful.go
1 /*
2         ==================================================================================
3         Copyright (c) 2019 AT&T Intellectual Property.
4         Copyright (c) 2019 Nokia
5
6         Licensed under the Apache License, Version 2.0 (the "License");
7         you may not use this file except in compliance with the License.
8         You may obtain a copy of the License at
9
10                 http://www.apache.org/licenses/LICENSE-2.0
11
12         Unless required by applicable law or agreed to in writing, software
13         distributed under the License is distributed on an "AS IS" BASIS,
14         WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15         See the License for the specific language governing permissions and
16         limitations under the License.
17         ==================================================================================
18 */
19
20 package restful
21
22 import (
23         "encoding/json"
24         "errors"
25         "fmt"
26         //"io/ioutil"
27         "log"
28         "net/http"
29         "os"
30         "strconv"
31         "strings"
32         "time"
33
34         "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/models"
35         "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/restapi"
36         "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/restapi/operations"
37         "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/restapi/operations/health"
38         "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/restapi/operations/xapp"
39         "github.com/go-openapi/loads"
40         "github.com/go-openapi/runtime/middleware"
41         "github.com/valyala/fastjson"
42
43         "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/appmgr"
44         "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/resthooks"
45 )
46
47 type XappData struct {
48         httpendpoint     string
49         rmrendpoint      string
50         rmrserviceep     string
51         status           string
52         xappname         string
53         xappinstname     string
54         xappversion      string
55         xappconfigpath   string
56         xappdynamiconfig bool
57         xappInstance     *models.XappInstance
58 }
59
60 var xappmap = map[string]map[string]*XappData{}
61
62 func NewRestful() *Restful {
63         r := &Restful{
64                 rh:    resthooks.NewResthook(true),
65                 ready: false,
66         }
67         r.api = r.SetupHandler()
68         return r
69 }
70
71 func (r *Restful) Run() {
72         server := restapi.NewServer(r.api)
73         defer server.Shutdown()
74         server.Port = 8080
75         server.Host = "0.0.0.0"
76
77         appmgr.Logger.Info("Xapp manager started ... serving on %s:%d\n", server.Host, server.Port)
78
79         go r.RetrieveApps()
80         if err := server.Serve(); err != nil {
81                 log.Fatal(err.Error())
82         }
83
84 }
85
86 func (r *Restful) RetrieveApps() {
87         time.Sleep(5 * time.Second)
88         var xlist models.RegisterRequest
89         applist := r.rh.GetAppsInSDL()
90         if applist != nil {
91                 appmgr.Logger.Info("List obtained from GetAppsInSDL is %s", *applist)
92                 newstring := strings.Split(*applist, " ")
93                 for i, _ := range newstring {
94                         appmgr.Logger.Debug("Checking for xapp %s", newstring[i])
95                         if newstring[i] != "" {
96                                 err := json.Unmarshal([]byte(newstring[i]), &xlist)
97                                 if err != nil {
98                                         appmgr.Logger.Error("Error while unmarshalling")
99                                         continue
100                                 }
101                         } else {
102                                 continue //SDL may have empty item,so need to skip
103                         }
104
105                         xmodel, _ := r.PrepareConfig(xlist, false)
106                         if xmodel == nil {
107                                 appmgr.Logger.Error("Xapp not found, deleting it from DB")
108                                 r.rh.UpdateAppData(xlist, true)
109                         }
110                 }
111         }
112
113 }
114
115 func (r *Restful) SetupHandler() *operations.AppManagerAPI {
116         swaggerSpec, err := loads.Embedded(restapi.SwaggerJSON, restapi.FlatSwaggerJSON)
117         if err != nil {
118                 appmgr.Logger.Error(err.Error())
119                 os.Exit(1)
120         }
121         api := operations.NewAppManagerAPI(swaggerSpec)
122
123         // URL: /ric/v1/health
124         api.HealthGetHealthAliveHandler = health.GetHealthAliveHandlerFunc(
125                 func(params health.GetHealthAliveParams) middleware.Responder {
126                         return health.NewGetHealthAliveOK()
127                 })
128
129         api.HealthGetHealthReadyHandler = health.GetHealthReadyHandlerFunc(
130                 func(params health.GetHealthReadyParams) middleware.Responder {
131                         return health.NewGetHealthReadyOK()
132                 })
133
134         // URL: /ric/v1/subscriptions
135         api.GetSubscriptionsHandler = operations.GetSubscriptionsHandlerFunc(
136                 func(params operations.GetSubscriptionsParams) middleware.Responder {
137                         return operations.NewGetSubscriptionsOK().WithPayload(r.rh.GetAllSubscriptions())
138                 })
139
140         api.GetSubscriptionByIDHandler = operations.GetSubscriptionByIDHandlerFunc(
141                 func(params operations.GetSubscriptionByIDParams) middleware.Responder {
142                         if result, found := r.rh.GetSubscriptionById(params.SubscriptionID); found {
143                                 return operations.NewGetSubscriptionByIDOK().WithPayload(&result)
144                         }
145                         return operations.NewGetSubscriptionByIDNotFound()
146                 })
147
148         api.AddSubscriptionHandler = operations.AddSubscriptionHandlerFunc(
149                 func(params operations.AddSubscriptionParams) middleware.Responder {
150                         return operations.NewAddSubscriptionCreated().WithPayload(r.rh.AddSubscription(*params.SubscriptionRequest))
151                 })
152
153         api.ModifySubscriptionHandler = operations.ModifySubscriptionHandlerFunc(
154                 func(params operations.ModifySubscriptionParams) middleware.Responder {
155                         if _, ok := r.rh.ModifySubscription(params.SubscriptionID, *params.SubscriptionRequest); ok {
156                                 return operations.NewModifySubscriptionOK()
157                         }
158                         return operations.NewModifySubscriptionBadRequest()
159                 })
160
161         api.DeleteSubscriptionHandler = operations.DeleteSubscriptionHandlerFunc(
162                 func(params operations.DeleteSubscriptionParams) middleware.Responder {
163                         if _, ok := r.rh.DeleteSubscription(params.SubscriptionID); ok {
164                                 return operations.NewDeleteSubscriptionNoContent()
165                         }
166                         return operations.NewDeleteSubscriptionBadRequest()
167                 })
168
169         // URL: /ric/v1/xapp
170         api.XappGetAllXappsHandler = xapp.GetAllXappsHandlerFunc(
171                 func(params xapp.GetAllXappsParams) middleware.Responder {
172                         if result, err := r.GetApps(); err == nil {
173                                 return xapp.NewGetAllXappsOK().WithPayload(result)
174                         }
175                         return xapp.NewGetAllXappsInternalServerError()
176                 })
177
178         // URL: /ric/v1/config
179         api.XappGetAllXappConfigHandler = xapp.GetAllXappConfigHandlerFunc(
180                 func(params xapp.GetAllXappConfigParams) middleware.Responder {
181                         return xapp.NewGetAllXappConfigOK().WithPayload(r.getAppConfig())
182                 })
183
184         api.RegisterXappHandler = operations.RegisterXappHandlerFunc(
185                 func(params operations.RegisterXappParams) middleware.Responder {
186                         appmgr.Logger.Info("appname is %s", (*params.RegisterRequest.AppName))
187                         appmgr.Logger.Info("endpoint is %s", (*params.RegisterRequest.HTTPEndpoint))
188                         appmgr.Logger.Info("rmrendpoint is %s", (*params.RegisterRequest.RmrEndpoint))
189                         if result, err := r.RegisterXapp(*params.RegisterRequest); err == nil {
190                                 go r.rh.PublishSubscription(*result, models.EventTypeDeployed)
191                                 return operations.NewRegisterXappCreated()
192                         }
193                         return operations.NewRegisterXappBadRequest()
194                 })
195
196         api.DeregisterXappHandler = operations.DeregisterXappHandlerFunc(
197                 func(params operations.DeregisterXappParams) middleware.Responder {
198                         appmgr.Logger.Info("appname is %s", (*params.DeregisterRequest.AppName))
199                         if result, err := r.DeregisterXapp(*params.DeregisterRequest); err == nil {
200                                 go r.rh.PublishSubscription(*result, models.EventTypeUndeployed)
201                                 return operations.NewDeregisterXappNoContent()
202                         }
203                         return operations.NewDeregisterXappBadRequest()
204                 })
205
206         return api
207 }
208
209 func httpGetXAppsconfig(url string) *string {
210         appmgr.Logger.Info("Invoked httprestful.httpGetXApps: " + url)
211         resp, err := http.Get(url)
212         if err != nil {
213                 return nil
214         }
215         defer resp.Body.Close()
216
217         if resp.StatusCode == http.StatusOK {
218                 var data XappConfigList
219                 appmgr.Logger.Info("http client raw response: %v", resp)
220                 if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
221                         appmgr.Logger.Error("Json decode failed: " + err.Error())
222                         return nil
223                 }
224                 //data[0] assuming only for one app
225                 str := fmt.Sprintf("%v", data[0].Config)
226                 appmgr.Logger.Info("HTTP BODY: %v", str)
227
228                 resp.Body.Close()
229                 return &str
230         } else {
231                 appmgr.Logger.Info("httprestful got an unexpected http status code: %v", resp.StatusCode)
232                 return nil
233         }
234 }
235
236 func parseConfig(config *string) *appmgr.RtmData {
237         var p fastjson.Parser
238         var msgs appmgr.RtmData
239
240         v, err := p.Parse(*config)
241         if err != nil {
242                 appmgr.Logger.Info("fastjson.Parser for failed: %v", err)
243                 return nil
244         }
245
246         if v.Exists("rmr") {
247                 for _, m := range v.GetArray("rmr", "txMessages") {
248                         msgs.TxMessages = append(msgs.TxMessages, strings.Trim(m.String(), `"`))
249                 }
250
251                 for _, m := range v.GetArray("rmr", "rxMessages") {
252                         msgs.RxMessages = append(msgs.RxMessages, strings.Trim(m.String(), `"`))
253                 }
254
255                 for _, m := range v.GetArray("rmr", "policies") {
256                         if val, err := strconv.Atoi(strings.Trim(m.String(), `"`)); err == nil {
257                                 msgs.Policies = append(msgs.Policies, int64(val))
258                         }
259                 }
260         } else {
261                 for _, p := range v.GetArray("messaging", "ports") {
262                         appmgr.Logger.Info("txMessages=%v, rxMessages=%v", p.GetArray("txMessages"), p.GetArray("rxMessages"))
263                         for _, m := range p.GetArray("txMessages") {
264                                 msgs.TxMessages = append(msgs.TxMessages, strings.Trim(m.String(), `"`))
265                         }
266
267                         for _, m := range p.GetArray("rxMessages") {
268                                 msgs.RxMessages = append(msgs.RxMessages, strings.Trim(m.String(), `"`))
269                         }
270
271                         for _, m := range p.GetArray("policies") {
272                                 if val, err := strconv.Atoi(strings.Trim(m.String(), `"`)); err == nil {
273                                         msgs.Policies = append(msgs.Policies, int64(val))
274                                 }
275                         }
276                 }
277         }
278         return &msgs
279 }
280
281 func (r *Restful) RegisterXapp(params models.RegisterRequest) (xapp *models.Xapp, err error) {
282         return r.PrepareConfig(params, true)
283 }
284
285 func (r *Restful) DeregisterXapp(params models.DeregisterRequest) (xapp *models.Xapp, err error) {
286         var registeredlist models.RegisterRequest
287         registeredlist.AppName = params.AppName
288         registeredlist.AppInstanceName = params.AppInstanceName
289         if _, found := xappmap[*params.AppName]; found {
290                 var x models.Xapp
291                 x.Instances = append(x.Instances, xappmap[*params.AppName][*params.AppInstanceName].xappInstance)
292                 registeredlist.HTTPEndpoint = &xappmap[*params.AppName][*params.AppInstanceName].httpendpoint
293                 delete(xappmap[*params.AppName], *params.AppInstanceName)
294                 if len(xappmap[*params.AppName]) == 0 {
295                         delete(xappmap, *params.AppName)
296                 }
297                 r.rh.UpdateAppData(registeredlist, true)
298                 return &x, nil
299         } else {
300                 appmgr.Logger.Error("XApp Instance %v Not Found", *params.AppName)
301                 return nil, errors.New("XApp Instance Not Found")
302         }
303 }
304
305 func (r *Restful) PrepareConfig(params models.RegisterRequest, updateflag bool) (xapp *models.Xapp, err error) {
306         maxRetries := 5
307         configPresent := false
308         var xappconfig *string
309         appmgr.Logger.Info("http endpoint is %s", *params.HTTPEndpoint)
310         for i := 1; i <= maxRetries; i++ {
311                 if params.Config != "" {
312                         appmgr.Logger.Info("Getting config during xapp register: %v", params.Config)
313                         xappconfig = &params.Config
314                         configPresent = true
315                 } else {
316                         appmgr.Logger.Info("Getting config from xapp:")
317                         xappconfig = httpGetXAppsconfig(fmt.Sprintf("http://%s%s", *params.HTTPEndpoint, params.ConfigPath))
318                 }
319
320                 if xappconfig != nil {
321                         data := parseConfig(xappconfig)
322                         if data != nil {
323                                 appmgr.Logger.Info("iRetry Count = %v", i)
324                                 var xapp models.Xapp
325
326                                 xapp.Name = params.AppName
327                                 xapp.Version = params.AppVersion
328                                 //xapp.Status = params.Status
329
330                                 r.rh.UpdateAppData(params, updateflag)
331                                 return r.FillInstanceData(params, &xapp, *data, configPresent)
332                                 break
333                         } else {
334                                 appmgr.Logger.Error("No Data from xapp")
335                         }
336                         if configPresent == true {
337                                 break
338                         }
339                         time.Sleep(2 * time.Second)
340                 }
341         }
342         return nil, errors.New("Unable to get configmap after 5 retries")
343 }
344
345 func (r *Restful) FillInstanceData(params models.RegisterRequest, xapp *models.Xapp, rtData appmgr.RtmData, configFlag bool) (xapps *models.Xapp, err error) {
346
347         endPointStr := strings.Split(*params.RmrEndpoint, ":")
348         var x models.XappInstance
349         x.Name = params.AppInstanceName
350         //x.Status = strings.ToLower(params.Status)
351         x.Status = "deployed"
352         //x.IP = endPointStr[0]
353         x.IP = fmt.Sprintf("service-ricxapp-%s-rmr.ricxapp", *params.AppInstanceName)
354         x.Port, _ = strconv.ParseInt(endPointStr[1], 10, 64)
355         x.TxMessages = rtData.TxMessages
356         x.RxMessages = rtData.RxMessages
357         x.Policies = rtData.Policies
358         xapp.Instances = append(xapp.Instances, &x)
359         rmrsrvname := fmt.Sprintf("service-ricxapp-%s-rmr.ricxapp:%s", *params.AppInstanceName, x.Port)
360
361         a := &XappData{httpendpoint: *params.HTTPEndpoint,
362                 rmrendpoint:      *params.RmrEndpoint,
363                 rmrserviceep:     rmrsrvname,
364                 status:           "deployed",
365                 xappname:         *params.AppName,
366                 xappversion:      params.AppVersion,
367                 xappinstname:     *params.AppInstanceName,
368                 xappconfigpath:   params.ConfigPath,
369                 xappdynamiconfig: configFlag,
370                 xappInstance:     &x}
371
372         if _, ok := xappmap[*params.AppName]; ok {
373                 xappmap[*params.AppName][*params.AppInstanceName] = a
374                 appmgr.Logger.Info("appname already present, %v", xappmap[*params.AppName])
375         } else {
376                 xappmap[*params.AppName] = make(map[string]*XappData)
377                 xappmap[*params.AppName][*params.AppInstanceName] = a
378                 appmgr.Logger.Info("Creating app instance, %v", xappmap[*params.AppName])
379         }
380
381         return xapp, nil
382
383 }
384
385 func (r *Restful) GetApps() (xapps models.AllDeployedXapps, err error) {
386         xapps = models.AllDeployedXapps{}
387         for _, v := range xappmap {
388                 var x models.Xapp
389                 for i, j := range v {
390                         x.Status = j.status
391                         x.Name = &j.xappname
392                         x.Version = j.xappversion
393                         appmgr.Logger.Info("Xapps details currently in map Appname = %v,rmrendpoint = %v,Status = %v", i, j.rmrendpoint, j.status)
394                         x.Instances = append(x.Instances, j.xappInstance)
395                 }
396                 xapps = append(xapps, &x)
397         }
398
399         return xapps, nil
400
401 }
402
403 func (r *Restful) getAppConfig() (configList models.AllXappConfig) {
404         for _, v := range xappmap {
405                 namespace := "ricxapp" //Namespace hardcoded, to be removed later
406                 for _, j := range v {
407                         var activeConfig interface{}
408                         if j.xappdynamiconfig {
409                                 continue
410                         }
411                         xappconfig := httpGetXAppsconfig(fmt.Sprintf("http://%s%s", j.httpendpoint, j.xappconfigpath))
412
413                         if xappconfig == nil {
414                                 appmgr.Logger.Info("config not found for %s", &j.xappname)
415                                 continue
416                         }
417                         json.Unmarshal([]byte(*xappconfig), &activeConfig)
418
419                         c := models.XAppConfig{
420                                 Metadata: &models.ConfigMetadata{XappName: &j.xappname, Namespace: &namespace},
421                                 Config:   activeConfig,
422                         }
423                         configList = append(configList, &c)
424
425                 }
426
427         }
428         return
429 }