f70317c15e596f2b0dcbbef4f4213f7f6b77472b
[icn/sdwan.git] /
1 // SPDX-License-Identifier: Apache-2.0
2 // Copyright (c) 2020 Intel Corporation
3
4 package appcontext
5
6 import (
7         "fmt"
8         "strings"
9
10         log "github.com/open-ness/EMCO/src/orchestrator/pkg/infra/logutils"
11         "github.com/open-ness/EMCO/src/orchestrator/pkg/rtcontext"
12         pkgerrors "github.com/pkg/errors"
13 )
14
15 // metaPrefix used for denoting clusterMeta level
16 const metaGrpPREFIX = "!@#metaGrp"
17
18 type AppContext struct {
19         initDone bool
20         rtcObj   rtcontext.RunTimeContext
21         rtc      rtcontext.Rtcontext
22 }
23
24 // AppContextStatus represents the current status of the appcontext
25 //      Instantiating - instantiate has been invoked and is still in progress
26 //      Instantiated - instantiate has completed
27 //      Terminating - terminate has been invoked and is still in progress
28 //      Terminated - terminate has completed
29 //      InstantiateFailed - the instantiate action has failed
30 //      TerminateFailed - the terminate action has failed
31 type AppContextStatus struct {
32         Status StatusValue
33 }
34 type StatusValue string
35 type statuses struct {
36         Instantiating     StatusValue
37         Instantiated      StatusValue
38         Terminating       StatusValue
39         Terminated        StatusValue
40         InstantiateFailed StatusValue
41         TerminateFailed   StatusValue
42 }
43
44 var AppContextStatusEnum = &statuses{
45         Instantiating:     "Instantiating",
46         Instantiated:      "Instantiated",
47         Terminating:       "Terminating",
48         Terminated:        "Terminated",
49         InstantiateFailed: "InstantiateFailed",
50         TerminateFailed:   "TerminateFailed",
51 }
52
53 // CompositeAppMeta consists of projectName, CompositeAppName,
54 // CompositeAppVersion, ReleaseName. This shall be used for
55 // instantiation of a compositeApp
56 type CompositeAppMeta struct {
57         Project               string   `json:"Project"`
58         CompositeApp          string   `json:"CompositeApp"`
59         Version               string   `json:"Version"`
60         Release               string   `json:"Release"`
61         DeploymentIntentGroup string   `json:"DeploymentIntentGroup"`
62         Namespace             string   `json:"Namespace"`
63         Level                 string   `json:"Level"`
64         ChildContextIDs       []string `json:"ChildContextIDs"`
65 }
66
67 // Init app context
68 func (ac *AppContext) InitAppContext() (interface{}, error) {
69         ac.rtcObj = rtcontext.RunTimeContext{}
70         ac.rtc = &ac.rtcObj
71         return ac.rtc.RtcInit()
72 }
73
74 // Load app context that was previously created
75 func (ac *AppContext) LoadAppContext(cid interface{}) (interface{}, error) {
76         ac.rtcObj = rtcontext.RunTimeContext{}
77         ac.rtc = &ac.rtcObj
78         return ac.rtc.RtcLoad(cid)
79 }
80
81 // CreateCompositeApp method returns composite app handle as interface.
82 func (ac *AppContext) CreateCompositeApp() (interface{}, error) {
83         h, err := ac.rtc.RtcCreate()
84         if err != nil {
85                 return nil, err
86         }
87         log.Info(":: CreateCompositeApp ::", log.Fields{"CompositeAppHandle": h})
88         return h, nil
89 }
90
91 // AddCompositeAppMeta adds the meta data associated with a composite app
92 func (ac *AppContext) AddCompositeAppMeta(meta interface{}) error {
93         err := ac.rtc.RtcAddMeta(meta)
94         if err != nil {
95                 return err
96         }
97         return nil
98 }
99
100 // Deletes the entire context
101 func (ac *AppContext) DeleteCompositeApp() error {
102         h, err := ac.rtc.RtcGet()
103         if err != nil {
104                 return err
105         }
106         err = ac.rtc.RtcDeletePrefix(h)
107         if err != nil {
108                 return err
109         }
110         return nil
111 }
112
113 //Returns the handles for a given composite app context
114 func (ac *AppContext) GetCompositeAppHandle() (interface{}, error) {
115         h, err := ac.rtc.RtcGet()
116         if err != nil {
117                 return nil, err
118         }
119         return h, nil
120 }
121
122 // GetLevelHandle returns the handle for the supplied level at the given handle.
123 // For example, to get the handle of the 'status' level at a given handle.
124 func (ac *AppContext) GetLevelHandle(handle interface{}, level string) (interface{}, error) {
125         ach := fmt.Sprintf("%v%v/", handle, level)
126         hs, err := ac.rtc.RtcGetHandles(ach)
127         if err != nil {
128                 return nil, err
129         }
130         for _, v := range hs {
131                 if v == ach {
132                         return v, nil
133                 }
134         }
135         return nil, pkgerrors.Errorf("No handle was found for level %v", level)
136 }
137
138 //Add app to the context under composite app
139 func (ac *AppContext) AddApp(handle interface{}, appname string) (interface{}, error) {
140         h, err := ac.rtc.RtcAddLevel(handle, "app", appname)
141         if err != nil {
142                 return nil, err
143         }
144         log.Info(":: Added app handle ::", log.Fields{"AppHandle": h})
145         return h, nil
146 }
147
148 //Delete app from the context and everything underneth
149 func (ac *AppContext) DeleteApp(handle interface{}) error {
150         err := ac.rtc.RtcDeletePrefix(handle)
151         if err != nil {
152                 return err
153         }
154         return nil
155 }
156
157 //Returns the handle for a given app
158 func (ac *AppContext) GetAppHandle(appname string) (interface{}, error) {
159         if appname == "" {
160                 return nil, pkgerrors.Errorf("Not a valid run time context app name")
161         }
162
163         rh, err := ac.rtc.RtcGet()
164         if err != nil {
165                 return nil, err
166         }
167
168         apph := fmt.Sprintf("%v", rh) + "app/" + appname + "/"
169         hs, err := ac.rtc.RtcGetHandles(apph)
170         if err != nil {
171                 return nil, err
172         }
173         for _, v := range hs {
174                 if v == apph {
175                         return v, nil
176                 }
177         }
178         return nil, pkgerrors.Errorf("No handle was found for the given app")
179 }
180
181 // AddCluster helps to add cluster to the context under app. It takes in the app handle and clusterName as value.
182 func (ac *AppContext) AddCluster(handle interface{}, clustername string) (interface{}, error) {
183         h, err := ac.rtc.RtcAddLevel(handle, "cluster", clustername)
184         if err != nil {
185                 return nil, err
186         }
187         log.Info(":: Added cluster handle ::", log.Fields{"ClusterHandler": h})
188         return h, nil
189 }
190
191 // AddClusterMetaGrp adds the meta info of groupNumber to which a cluster belongs.
192 // It takes in cluster handle and groupNumber as arguments
193 func (ac *AppContext) AddClusterMetaGrp(ch interface{}, gn string) error {
194         mh, err := ac.rtc.RtcAddOneLevel(ch, metaGrpPREFIX, gn)
195         if err != nil {
196                 return err
197         }
198         log.Info(":: Added cluster meta handle ::", log.Fields{"ClusterMetaHandler": mh})
199         return nil
200 }
201
202 // DeleteClusterMetaGrpHandle deletes the group number to which the cluster belongs, it takes in the cluster handle.
203 func (ac *AppContext) DeleteClusterMetaGrpHandle(ch interface{}) error {
204         err := ac.rtc.RtcDeletePrefix(ch)
205         if err != nil {
206                 return err
207         }
208         log.Info(":: Deleted cluster meta handle ::", log.Fields{"ClusterMetaHandler": ch})
209         return nil
210 }
211
212 /*
213 GetClusterMetaHandle takes in appName and ClusterName as string arguments and return the ClusterMetaHandle as string
214 */
215 func (ac *AppContext) GetClusterMetaHandle(app string, cluster string) (string, error) {
216         if app == "" {
217                 return "", pkgerrors.Errorf("Not a valid run time context app name")
218         }
219         if cluster == "" {
220                 return "", pkgerrors.Errorf("Not a valid run time context cluster name")
221         }
222
223         ch, err := ac.GetClusterHandle(app, cluster)
224         if err != nil {
225                 return "", err
226         }
227         cmh := fmt.Sprintf("%v", ch) + metaGrpPREFIX + "/"
228         return cmh, nil
229
230 }
231
232 /*
233 GetClusterGroupMap shall take in appName and return a map showing the grouping among the clusters.
234 sample output of "GroupMap" :{"1":["cluster_provider1+clusterName3","cluster_provider1+clusterName5"],"2":["cluster_provider2+clusterName4","cluster_provider2+clusterName6"]}
235 */
236 func (ac *AppContext) GetClusterGroupMap(an string) (map[string][]string, error) {
237         cl, err := ac.GetClusterNames(an)
238         if err != nil {
239                 log.Info(":: Unable to fetch clusterList for app ::", log.Fields{"AppName ": an})
240                 return nil, err
241         }
242         rh, err := ac.rtc.RtcGet()
243         if err != nil {
244                 return nil, err
245         }
246
247         var gmap = make(map[string][]string)
248         for _, cn := range cl {
249                 s := fmt.Sprintf("%v", rh) + "app/" + an + "/cluster/" + cn + "/" + metaGrpPREFIX + "/"
250                 var v string
251                 err = ac.rtc.RtcGetValue(s, &v)
252                 if err != nil {
253                         log.Info(":: No group number for cluster  ::", log.Fields{"cluster": cn, "Reason": err})
254                         continue
255                 }
256                 gn := fmt.Sprintf("%v", v)
257                 log.Info(":: GroupNumber retrieved  ::", log.Fields{"GroupNumber": gn})
258
259                 cl, found := gmap[gn]
260                 if found == false {
261                         cl = make([]string, 0)
262                 }
263                 cl = append(cl, cn)
264                 gmap[gn] = cl
265         }
266         return gmap, nil
267 }
268
269 //Delete cluster from the context and everything underneth
270 func (ac *AppContext) DeleteCluster(handle interface{}) error {
271         err := ac.rtc.RtcDeletePrefix(handle)
272         if err != nil {
273                 return err
274         }
275         return nil
276 }
277
278 //Returns the handle for a given app and cluster
279 func (ac *AppContext) GetClusterHandle(appname string, clustername string) (interface{}, error) {
280         if appname == "" {
281                 return nil, pkgerrors.Errorf("Not a valid run time context app name")
282         }
283         if clustername == "" {
284                 return nil, pkgerrors.Errorf("Not a valid run time context cluster name")
285         }
286
287         rh, err := ac.rtc.RtcGet()
288         if err != nil {
289                 return nil, err
290         }
291
292         ach := fmt.Sprintf("%v", rh) + "app/" + appname + "/cluster/" + clustername + "/"
293         hs, err := ac.rtc.RtcGetHandles(ach)
294         if err != nil {
295                 return nil, err
296         }
297         for _, v := range hs {
298                 if v == ach {
299                         return v, nil
300                 }
301         }
302         return nil, pkgerrors.Errorf("No handle was found for the given cluster")
303 }
304
305 //Returns a list of all clusters for a given app
306 func (ac *AppContext) GetClusterNames(appname string) ([]string, error) {
307         if appname == "" {
308                 return nil, pkgerrors.Errorf("Not a valid run time context app name")
309         }
310
311         rh, err := ac.rtc.RtcGet()
312         if err != nil {
313                 return nil, err
314         }
315
316         prefix := fmt.Sprintf("%v", rh) + "app/" + appname + "/cluster/"
317         hs, err := ac.rtc.RtcGetHandles(prefix)
318         if err != nil {
319                 return nil, pkgerrors.Errorf("Error getting handles for %v", prefix)
320         }
321         var cs []string
322         for _, h := range hs {
323                 hstr := fmt.Sprintf("%v", h)
324                 ks := strings.Split(hstr, prefix)
325                 for _, k := range ks {
326                         ck := strings.Split(k, "/")
327                         if len(ck) == 2 && ck[1] == "" {
328                                 cs = append(cs, ck[0])
329                         }
330                 }
331         }
332
333         if len(cs) == 0 {
334                 err = pkgerrors.New("Cluster list is empty")
335                 log.Error("Cluster list is empty",
336                         log.Fields{"clusters": cs})
337                 return cs, err
338         }
339         return cs, nil
340 }
341
342 //Add resource under app and cluster
343 func (ac *AppContext) AddResource(handle interface{}, resname string, value interface{}) (interface{}, error) {
344         h, err := ac.rtc.RtcAddResource(handle, resname, value)
345         if err != nil {
346                 return nil, err
347         }
348         log.Info(":: Added resource handle ::", log.Fields{"ResourceHandler": h})
349
350         return h, nil
351 }
352
353 //Return the handle for given app, cluster and resource name
354 func (ac *AppContext) GetResourceHandle(appname string, clustername string, resname string) (interface{}, error) {
355         if appname == "" {
356                 return nil, pkgerrors.Errorf("Not a valid run time context app name")
357         }
358         if clustername == "" {
359                 return nil, pkgerrors.Errorf("Not a valid run time context cluster name")
360         }
361
362         rh, err := ac.rtc.RtcGet()
363         if err != nil {
364                 return nil, err
365         }
366
367         acrh := fmt.Sprintf("%v", rh) + "app/" + appname + "/cluster/" + clustername + "/resource/" + resname + "/"
368         hs, err := ac.rtc.RtcGetHandles(acrh)
369         if err != nil {
370                 return nil, err
371         }
372         for _, v := range hs {
373                 if v == acrh {
374                         return v, nil
375                 }
376         }
377         return nil, pkgerrors.Errorf("No handle was found for the given resource")
378 }
379
380 //Update the resource value using the given handle
381 func (ac *AppContext) UpdateResourceValue(handle interface{}, value interface{}) error {
382         return ac.rtc.RtcUpdateValue(handle, value)
383 }
384
385 //Return the handle for given app, cluster and resource name
386 func (ac *AppContext) GetResourceStatusHandle(appname string, clustername string, resname string) (interface{}, error) {
387         if appname == "" {
388                 return nil, pkgerrors.Errorf("Not a valid run time context app name")
389         }
390         if clustername == "" {
391                 return nil, pkgerrors.Errorf("Not a valid run time context cluster name")
392         }
393         if resname == "" {
394                 return nil, pkgerrors.Errorf("Not a valid run time context resource name")
395         }
396
397         rh, err := ac.rtc.RtcGet()
398         if err != nil {
399                 return nil, err
400         }
401
402         acrh := fmt.Sprintf("%v", rh) + "app/" + appname + "/cluster/" + clustername + "/resource/" + resname + "/status/"
403         hs, err := ac.rtc.RtcGetHandles(acrh)
404         if err != nil {
405                 return nil, err
406         }
407         for _, v := range hs {
408                 if v == acrh {
409                         return v, nil
410                 }
411         }
412         return nil, pkgerrors.Errorf("No handle was found for the given resource")
413 }
414
415 //GetResourceNames ... Returns a list of all resource names for a given app
416 func (ac *AppContext) GetResourceNames(appname string, clustername string) ([]string, error) {
417         if appname == "" {
418                 return nil, pkgerrors.Errorf("Not a valid run time context app name")
419         }
420         if clustername == "" {
421                 return nil, pkgerrors.Errorf("Not a valid run time context cluster name")
422         }
423
424         rh, err := ac.rtc.RtcGet()
425         if err != nil {
426                 return nil, err
427         }
428
429         prefix := fmt.Sprintf("%v", rh) + "app/" + appname + "/cluster/" + clustername + "/resource/"
430         hs, err := ac.rtc.RtcGetHandles(prefix)
431         if err != nil {
432                 return nil, pkgerrors.Errorf("Error getting handles for %v", prefix)
433         }
434         var cs []string
435         for _, h := range hs {
436                 hstr := fmt.Sprintf("%v", h)
437                 ks := strings.Split(hstr, prefix)
438                 for _, k := range ks {
439                         ck := strings.Split(k, "/")
440                         if len(ck) == 2 && ck[1] == "" {
441                                 cs = append(cs, ck[0])
442                         }
443                 }
444         }
445         return cs, nil
446 }
447
448 //Add instruction under given handle and type
449 func (ac *AppContext) AddInstruction(handle interface{}, level string, insttype string, value interface{}) (interface{}, error) {
450         if !(insttype == "order" || insttype == "dependency") {
451                 log.Error("Not a valid app context instruction type", log.Fields{})
452                 return nil, pkgerrors.Errorf("Not a valid app context instruction type")
453         }
454         if !(level == "app" || level == "resource" || level == "subresource") {
455                 log.Error("Not a valid app context instruction level", log.Fields{})
456                 return nil, pkgerrors.Errorf("Not a valid app context instruction level")
457         }
458         h, err := ac.rtc.RtcAddInstruction(handle, level, insttype, value)
459         if err != nil {
460                 log.Error("ac.rtc.RtcAddInstruction(handle, level, insttype, value)", log.Fields{"err": err})
461                 return nil, err
462         }
463         log.Info(":: Added instruction handle ::", log.Fields{"InstructionHandler": h})
464         return h, nil
465 }
466
467 //Delete instruction under given handle
468 func (ac *AppContext) DeleteInstruction(handle interface{}) error {
469         err := ac.rtc.RtcDeletePair(handle)
470         if err != nil {
471                 return err
472         }
473         return nil
474 }
475
476 //Returns the app instruction for a given instruction type
477 func (ac *AppContext) GetAppInstruction(insttype string) (interface{}, error) {
478         if !(insttype == "order" || insttype == "dependency") {
479                 log.Error("Not a valid app context instruction type", log.Fields{})
480                 return nil, pkgerrors.Errorf("Not a valid app context instruction type")
481         }
482         rh, err := ac.rtc.RtcGet()
483         if err != nil {
484                 log.Error("ac.rtc.RtcGet()", log.Fields{"err": err})
485                 return nil, err
486         }
487         s := fmt.Sprintf("%v", rh) + "app/" + "instruction/" + insttype + "/"
488         log.Info("Getting app instruction", log.Fields{"s": s})
489         var v string
490         err = ac.rtc.RtcGetValue(s, &v)
491         if err != nil {
492                 log.Error("ac.rtc.RtcGetValue(s, &v)", log.Fields{"err": err})
493                 return nil, err
494         }
495         return v, nil
496 }
497
498 //Update the instruction usign the given handle
499 func (ac *AppContext) UpdateInstructionValue(handle interface{}, value interface{}) error {
500         return ac.rtc.RtcUpdateValue(handle, value)
501 }
502
503 //Returns the resource instruction for a given instruction type
504 func (ac *AppContext) GetResourceInstruction(appname string, clustername string, insttype string) (interface{}, error) {
505         if !(insttype == "order" || insttype == "dependency") {
506                 log.Error("Not a valid app context instruction type", log.Fields{})
507                 return nil, pkgerrors.Errorf("Not a valid app context instruction type")
508         }
509         rh, err := ac.rtc.RtcGet()
510         if err != nil {
511                 log.Error("ac.rtc.RtcGet()", log.Fields{"err": err})
512                 return nil, err
513         }
514         s := fmt.Sprintf("%v", rh) + "app/" + appname + "/cluster/" + clustername + "/resource/instruction/" + insttype + "/"
515         var v string
516         err = ac.rtc.RtcGetValue(s, &v)
517         if err != nil {
518                 log.Error("ac.rtc.RtcGetValue(s, &v)", log.Fields{"err": err})
519                 return nil, err
520         }
521         return v, nil
522 }
523
524 // AddLevelValue for holding a state object at a given level
525 // will make a handle with an appended "<level>/" to the key
526 func (ac *AppContext) AddLevelValue(handle interface{}, level string, value interface{}) (interface{}, error) {
527         h, err := ac.rtc.RtcAddOneLevel(handle, level, value)
528         if err != nil {
529                 return nil, err
530         }
531         log.Info(":: Added handle ::", log.Fields{"Handle": h})
532
533         return h, nil
534 }
535
536 // GetClusterStatusHandle returns the handle for cluster status for a given app and cluster
537 func (ac *AppContext) GetClusterStatusHandle(appname string, clustername string) (interface{}, error) {
538         if appname == "" {
539                 return nil, pkgerrors.Errorf("Not a valid run time context app name")
540         }
541         if clustername == "" {
542                 return nil, pkgerrors.Errorf("Not a valid run time context cluster name")
543         }
544
545         rh, err := ac.rtc.RtcGet()
546         if err != nil {
547                 return nil, err
548         }
549
550         acrh := fmt.Sprintf("%v", rh) + "app/" + appname + "/cluster/" + clustername + "/status/"
551         hs, err := ac.rtc.RtcGetHandles(acrh)
552         if err != nil {
553                 return nil, err
554         }
555         for _, v := range hs {
556                 if v == acrh {
557                         return v, nil
558                 }
559         }
560         return nil, pkgerrors.Errorf("No handle was found for the given resource")
561 }
562
563 //UpdateStatusValue updates the status value with the given handle
564 func (ac *AppContext) UpdateStatusValue(handle interface{}, value interface{}) error {
565         return ac.rtc.RtcUpdateValue(handle, value)
566 }
567
568 //UpdateValue updates the state value with the given handle
569 func (ac *AppContext) UpdateValue(handle interface{}, value interface{}) error {
570         return ac.rtc.RtcUpdateValue(handle, value)
571 }
572
573 //Return all the handles under the composite app
574 func (ac *AppContext) GetAllHandles(handle interface{}) ([]interface{}, error) {
575         hs, err := ac.rtc.RtcGetHandles(handle)
576         if err != nil {
577                 return nil, err
578         }
579         return hs, nil
580 }
581
582 //Returns the value for a given handle
583 func (ac *AppContext) GetValue(handle interface{}) (interface{}, error) {
584         var v interface{}
585         err := ac.rtc.RtcGetValue(handle, &v)
586         if err != nil {
587                 return nil, err
588         }
589         return v, nil
590 }
591
592 // GetCompositeAppMeta returns the meta data associated with the compositeApp
593 // Its return type is CompositeAppMeta
594 func (ac *AppContext) GetCompositeAppMeta() (CompositeAppMeta, error) {
595         mi, err := ac.rtcObj.RtcGetMeta()
596
597         if err != nil {
598                 return CompositeAppMeta{}, pkgerrors.Errorf("Failed to get compositeApp meta")
599         }
600         datamap, ok := mi.(map[string]interface{})
601         if ok == false {
602                 return CompositeAppMeta{}, pkgerrors.Errorf("Failed to cast meta interface to compositeApp meta")
603         }
604
605         p := fmt.Sprintf("%v", datamap["Project"])
606         ca := fmt.Sprintf("%v", datamap["CompositeApp"])
607         v := fmt.Sprintf("%v", datamap["Version"])
608         rn := fmt.Sprintf("%v", datamap["Release"])
609         dig := fmt.Sprintf("%v", datamap["DeploymentIntentGroup"])
610         namespace := fmt.Sprintf("%v", datamap["Namespace"])
611         level := fmt.Sprintf("%v", datamap["Level"])
612         var childInterface []interface{}
613         childCtxs := make([]string, len(childInterface))
614         if datamap["ChildContextIDs"] != nil {
615                 childInterface = datamap["ChildContextIDs"].([]interface{})
616                 for _, v := range childInterface {
617                         childCtxs = append(childCtxs, v.(string))
618                 }
619         }
620
621         return CompositeAppMeta{Project: p, CompositeApp: ca, Version: v, Release: rn, DeploymentIntentGroup: dig, Namespace: namespace, Level: level, ChildContextIDs: childCtxs}, nil
622 }