Initial commit for Xapp Orchestration
[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         status         string
51         xappname       string
52         xappinstname   string
53         xappversion    string
54         xappconfigpath string
55         xappInstance   *models.XappInstance
56 }
57
58 var xappmap = map[string]map[string]*XappData{}
59
60 func NewRestful() *Restful {
61         r := &Restful{
62                 rh:    resthooks.NewResthook(true),
63                 ready: false,
64         }
65         r.api = r.SetupHandler()
66         return r
67 }
68
69 func (r *Restful) Run() {
70         server := restapi.NewServer(r.api)
71         defer server.Shutdown()
72         server.Port = 8080
73         server.Host = "0.0.0.0"
74
75         appmgr.Logger.Info("Xapp manager started ... serving on %s:%d\n", server.Host, server.Port)
76
77         go r.RetrieveApps()
78         if err := server.Serve(); err != nil {
79                 log.Fatal(err.Error())
80         }
81
82 }
83
84 func (r *Restful) RetrieveApps() {
85         time.Sleep(5 * time.Second)
86         var xlist models.RegisterRequest
87         applist := r.rh.GetAppsInSDL()
88         if applist != nil {
89                 appmgr.Logger.Info("List obtained from GetAppsInSDL is %s", *applist)
90                 newstring := strings.Split(*applist, " ")
91                 for i, _ := range newstring {
92                         appmgr.Logger.Debug("Checking for xapp %s", newstring[i])
93                         if newstring[i] != "" {
94                                 err := json.Unmarshal([]byte(newstring[i]), &xlist)
95                                 if err != nil {
96                                         appmgr.Logger.Error("Error while unmarshalling")
97                                         continue
98                                 }
99                         } else {
100                                 continue //SDL may have empty item,so need to skip
101                         }
102
103                         xmodel, _ := r.PrepareConfig(xlist, false)
104                         if xmodel == nil {
105                                 appmgr.Logger.Error("Xapp not found, deleting it from DB")
106                                 r.rh.UpdateAppData(xlist, true)
107                         }
108                 }
109         }
110
111 }
112
113 func (r *Restful) SetupHandler() *operations.AppManagerAPI {
114         swaggerSpec, err := loads.Embedded(restapi.SwaggerJSON, restapi.FlatSwaggerJSON)
115         if err != nil {
116                 appmgr.Logger.Error(err.Error())
117                 os.Exit(1)
118         }
119         api := operations.NewAppManagerAPI(swaggerSpec)
120
121         // URL: /ric/v1/health
122         api.HealthGetHealthAliveHandler = health.GetHealthAliveHandlerFunc(
123                 func(params health.GetHealthAliveParams) middleware.Responder {
124                         return health.NewGetHealthAliveOK()
125                 })
126
127         api.HealthGetHealthReadyHandler = health.GetHealthReadyHandlerFunc(
128                 func(params health.GetHealthReadyParams) middleware.Responder {
129                         return health.NewGetHealthReadyOK()
130                 })
131
132         // URL: /ric/v1/subscriptions
133         api.GetSubscriptionsHandler = operations.GetSubscriptionsHandlerFunc(
134                 func(params operations.GetSubscriptionsParams) middleware.Responder {
135                         return operations.NewGetSubscriptionsOK().WithPayload(r.rh.GetAllSubscriptions())
136                 })
137
138         api.GetSubscriptionByIDHandler = operations.GetSubscriptionByIDHandlerFunc(
139                 func(params operations.GetSubscriptionByIDParams) middleware.Responder {
140                         if result, found := r.rh.GetSubscriptionById(params.SubscriptionID); found {
141                                 return operations.NewGetSubscriptionByIDOK().WithPayload(&result)
142                         }
143                         return operations.NewGetSubscriptionByIDNotFound()
144                 })
145
146         api.AddSubscriptionHandler = operations.AddSubscriptionHandlerFunc(
147                 func(params operations.AddSubscriptionParams) middleware.Responder {
148                         return operations.NewAddSubscriptionCreated().WithPayload(r.rh.AddSubscription(*params.SubscriptionRequest))
149                 })
150
151         api.ModifySubscriptionHandler = operations.ModifySubscriptionHandlerFunc(
152                 func(params operations.ModifySubscriptionParams) middleware.Responder {
153                         if _, ok := r.rh.ModifySubscription(params.SubscriptionID, *params.SubscriptionRequest); ok {
154                                 return operations.NewModifySubscriptionOK()
155                         }
156                         return operations.NewModifySubscriptionBadRequest()
157                 })
158
159         api.DeleteSubscriptionHandler = operations.DeleteSubscriptionHandlerFunc(
160                 func(params operations.DeleteSubscriptionParams) middleware.Responder {
161                         if _, ok := r.rh.DeleteSubscription(params.SubscriptionID); ok {
162                                 return operations.NewDeleteSubscriptionNoContent()
163                         }
164                         return operations.NewDeleteSubscriptionBadRequest()
165                 })
166
167         // URL: /ric/v1/xapp
168         api.XappGetAllXappsHandler = xapp.GetAllXappsHandlerFunc(
169                 func(params xapp.GetAllXappsParams) middleware.Responder {
170                         if result, err := r.GetApps(); err == nil {
171                                 return xapp.NewGetAllXappsOK().WithPayload(result)
172                         }
173                         return xapp.NewGetAllXappsInternalServerError()
174                 })
175
176         api.RegisterXappHandler = operations.RegisterXappHandlerFunc(
177                 func(params operations.RegisterXappParams) middleware.Responder {
178                         appmgr.Logger.Info("appname is %s", (*params.RegisterRequest.AppName))
179                         appmgr.Logger.Info("endpoint is %s", (*params.RegisterRequest.HTTPEndpoint))
180                         appmgr.Logger.Info("rmrendpoint is %s", (*params.RegisterRequest.RmrEndpoint))
181                         if result, err := r.RegisterXapp(*params.RegisterRequest); err == nil {
182                                 go r.rh.PublishSubscription(*result, models.EventTypeDeployed)
183                                 return operations.NewRegisterXappCreated()
184                         }
185                         return operations.NewRegisterXappBadRequest()
186                 })
187
188         api.DeregisterXappHandler = operations.DeregisterXappHandlerFunc(
189                 func(params operations.DeregisterXappParams) middleware.Responder {
190                         appmgr.Logger.Info("appname is %s", (*params.DeregisterRequest.AppName))
191                         if result, err := r.DeregisterXapp(*params.DeregisterRequest); err == nil {
192                                 go r.rh.PublishSubscription(*result, models.EventTypeUndeployed)
193                                 return operations.NewDeregisterXappNoContent()
194                         }
195                         return operations.NewDeregisterXappBadRequest()
196                 })
197
198         return api
199 }
200
201 func httpGetXAppsconfig(url string) (*appmgr.RtmData, error) {
202         appmgr.Logger.Info("Invoked httprestful.httpGetXApps: " + url)
203         resp, err := http.Get(url)
204         if err != nil {
205                 return nil, err
206         }
207         defer resp.Body.Close()
208
209         if resp.StatusCode == http.StatusOK {
210                 var data XappConfigList
211                 appmgr.Logger.Info("http client raw response: %v", resp)
212                 if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
213                         appmgr.Logger.Error("Json decode failed: " + err.Error())
214                         return nil, err
215                 }
216                 //data[0] assuming only for one app
217                 str := fmt.Sprintf("%v", data[0].Config)
218                 appmgr.Logger.Info("HTTP BODY: %v", str)
219
220                 resp.Body.Close()
221
222                 var p fastjson.Parser
223                 var msgs appmgr.RtmData
224
225                 v, err := p.Parse(str)
226                 if err != nil {
227                         appmgr.Logger.Info("fastjson.Parser for failed: %v", err)
228                         return nil, err
229                 }
230
231                 if v.Exists("rmr") {
232                         for _, m := range v.GetArray("rmr", "txMessages") {
233                                 msgs.TxMessages = append(msgs.TxMessages, strings.Trim(m.String(), `"`))
234                         }
235
236                         for _, m := range v.GetArray("rmr", "rxMessages") {
237                                 msgs.RxMessages = append(msgs.RxMessages, strings.Trim(m.String(), `"`))
238                         }
239
240                         for _, m := range v.GetArray("rmr", "policies") {
241                                 if val, err := strconv.Atoi(strings.Trim(m.String(), `"`)); err == nil {
242                                         msgs.Policies = append(msgs.Policies, int64(val))
243                                 }
244                         }
245                 } else {
246                         for _, p := range v.GetArray("messaging", "ports") {
247                                 appmgr.Logger.Info("txMessages=%v, rxMessages=%v", p.GetArray("txMessages"), p.GetArray("rxMessages"))
248                                 for _, m := range p.GetArray("txMessages") {
249                                         msgs.TxMessages = append(msgs.TxMessages, strings.Trim(m.String(), `"`))
250                                 }
251
252                                 for _, m := range p.GetArray("rxMessages") {
253                                         msgs.RxMessages = append(msgs.RxMessages, strings.Trim(m.String(), `"`))
254                                 }
255
256                                 for _, m := range p.GetArray("policies") {
257                                         if val, err := strconv.Atoi(strings.Trim(m.String(), `"`)); err == nil {
258                                                 msgs.Policies = append(msgs.Policies, int64(val))
259                                         }
260                                 }
261                         }
262                 }
263                 return &msgs, nil
264         }
265         appmgr.Logger.Info("httprestful got an unexpected http status code: %v", resp.StatusCode)
266         return nil, nil
267 }
268
269 func (r *Restful) RegisterXapp(params models.RegisterRequest) (xapp *models.Xapp, err error) {
270         return r.PrepareConfig(params, true)
271 }
272
273 func (r *Restful) DeregisterXapp(params models.DeregisterRequest) (xapp *models.Xapp, err error) {
274         var registeredlist models.RegisterRequest
275         registeredlist.AppName = params.AppName
276         registeredlist.AppInstanceName = params.AppInstanceName
277         if _, found := xappmap[*params.AppName]; found {
278                 var x models.Xapp
279                 x.Instances = append(x.Instances, xappmap[*params.AppName][*params.AppInstanceName].xappInstance)
280                 registeredlist.HTTPEndpoint = &xappmap[*params.AppName][*params.AppInstanceName].httpendpoint
281                 delete(xappmap[*params.AppName], *params.AppInstanceName)
282                 if len(xappmap[*params.AppName]) == 0 {
283                         delete(xappmap, *params.AppName)
284                 }
285                 r.rh.UpdateAppData(registeredlist, true)
286                 return &x, nil
287         } else {
288                 appmgr.Logger.Error("XApp Instance %v Not Found", *params.AppName)
289                 return nil, errors.New("XApp Instance Not Found")
290         }
291 }
292
293 func (r *Restful) PrepareConfig(params models.RegisterRequest, updateflag bool) (xapp *models.Xapp, err error) {
294         maxRetries := 5
295         //tmpString := strings.Split(*params.HTTPEndpoint, "//")
296         appmgr.Logger.Info("http endpoint is %s", *params.HTTPEndpoint)
297         for i := 1; i <= maxRetries; i++ {
298                 data, err := httpGetXAppsconfig(fmt.Sprintf("http://%s%s", *params.HTTPEndpoint, params.ConfigPath))
299
300                 if data != nil && err == nil {
301                         appmgr.Logger.Info("iRetry Count = %v", i)
302                         var xapp models.Xapp
303
304                         xapp.Name = params.AppName
305                         xapp.Version = params.AppVersion
306                         //xapp.Status = params.Status
307
308                         r.rh.UpdateAppData(params, updateflag)
309                         return r.FillInstanceData(params, &xapp, *data)
310                         break
311                 } else if err == nil {
312                         appmgr.Logger.Error("Unexpected HTTP status code/JSON Parsing error")
313                 } else {
314                         appmgr.Logger.Error("Couldn't get data due to" + err.Error())
315                 }
316                 time.Sleep(2 * time.Second)
317         }
318
319         return nil, errors.New("Unable to get configmap after 5 retries")
320 }
321
322 func (r *Restful) FillInstanceData(params models.RegisterRequest, xapp *models.Xapp, rtData appmgr.RtmData) (xapps *models.Xapp, err error) {
323
324         //tmpString := strings.Split(*params.RmrEndpoint, "//")
325         endPointStr := strings.Split(*params.RmrEndpoint, ":")
326         var x models.XappInstance
327         x.Name = params.AppInstanceName
328         //x.Status = strings.ToLower(params.Status)
329         x.Status = "deployed"
330         x.IP = endPointStr[0]
331         x.Port, _ = strconv.ParseInt(endPointStr[1], 10, 64)
332         x.TxMessages = rtData.TxMessages
333         x.RxMessages = rtData.RxMessages
334         x.Policies = rtData.Policies
335         xapp.Instances = append(xapp.Instances, &x)
336
337         a := &XappData{httpendpoint: *params.HTTPEndpoint, rmrendpoint: *params.RmrEndpoint, status: "deployed", xappname: *params.AppName, xappversion: params.AppVersion, xappinstname: *params.AppInstanceName, xappconfigpath: params.ConfigPath, xappInstance: &x}
338
339         if _, ok := xappmap[*params.AppName]; ok {
340                 xappmap[*params.AppName][*params.AppInstanceName] = a
341                 appmgr.Logger.Info("appname already present, %v", xappmap[*params.AppName])
342         } else {
343                 xappmap[*params.AppName] = make(map[string]*XappData)
344                 xappmap[*params.AppName][*params.AppInstanceName] = a
345                 appmgr.Logger.Info("Creating app instance, %v", xappmap[*params.AppName])
346         }
347
348         return xapp, nil
349
350 }
351
352 func (r *Restful) GetApps() (xapps models.AllDeployedXapps, err error) {
353         xapps = models.AllDeployedXapps{}
354         for _, v := range xappmap {
355                 var x models.Xapp
356                 for i, j := range v {
357                         x.Status = j.status
358                         x.Name = &j.xappname
359                         x.Version = j.xappversion
360                         appmgr.Logger.Info("Xapps details currently in map Appname = %v,rmrendpoint = %v,Status = %v", i, j.rmrendpoint, j.status)
361                         x.Instances = append(x.Instances, j.xappInstance)
362                 }
363                 xapps = append(xapps, &x)
364         }
365
366         return xapps, nil
367
368 }