- FM performance test tool first version for review
[ric-plt/alarm-go.git] / cli / alarm-cli.go
1 package main
2
3 import (
4         "bytes"
5         "encoding/json"
6         "fmt"
7         "io/ioutil"
8         "net/http"
9         "os"
10         "strconv"
11         "time"
12
13         "gerrit.o-ran-sc.org/r/ric-plt/alarm-go/alarm"
14         "github.com/jedib0t/go-pretty/table"
15         "github.com/thatisuday/commando"
16         "sync"
17 )
18
19 type CliAlarmDefinitions struct {
20         AlarmDefinitions []*alarm.AlarmDefinition `json:"alarmdefinitions"`
21 }
22
23 type AlarmClient struct {
24         alarmer *alarm.RICAlarm
25 }
26
27 type RicPerfAlarmObjects struct {
28         AlarmObjects []*alarm.Alarm `json:"alarmobjects"`
29 }
30
31 var CLIPerfAlarmObjects map[int]*alarm.Alarm
32
33 var wg sync.WaitGroup
34
35 var CliPerfAlarmDefinitions CliAlarmDefinitions
36
37 const (
38         Raise             string = "RAISE"
39         Clear             string = "CLEAR"
40         End               string = "END"
41         PeakTestDuration  int    = 60
42         OneSecondDuration int    = 1
43 )
44
45 func main() {
46
47         // configure commando
48         commando.
49                 SetExecutableName("alarm-cli").
50                 SetVersion("1.0.0").
51                 SetDescription("This CLI tool provides management interface to SEP alarm system")
52
53         // Get active alarms
54         commando.
55                 Register("active").
56                 SetShortDescription("Displays the SEP active alarms").
57                 SetDescription("This command displays more information about the SEP active alarms").
58                 AddFlag("host", "Alarm manager host address", commando.String, "localhost").
59                 AddFlag("port", "Alarm manager host address", commando.String, "8080").
60                 SetAction(func(args map[string]commando.ArgValue, flags map[string]commando.FlagValue) {
61                         displayAlarms(getAlarms(flags, "active"), false)
62                 })
63
64         // Get alarm history
65         commando.
66                 Register("history").
67                 SetShortDescription("Displays the SEP alarm history").
68                 SetDescription("This command displays more information about the SEP alarm history").
69                 AddFlag("host", "Alarm manager host address", commando.String, "localhost").
70                 AddFlag("port", "Alarm manager host address", commando.String, "8080").
71                 SetAction(func(args map[string]commando.ArgValue, flags map[string]commando.FlagValue) {
72                         displayAlarms(getAlarms(flags, "history"), true)
73                 })
74
75         // Raise an alarm
76         commando.
77                 Register("raise").
78                 SetShortDescription("Raises alarm with given parameters").
79                 AddFlag("moid", "Managed object Id", commando.String, nil).
80                 AddFlag("apid", "Application Id", commando.String, nil).
81                 AddFlag("sp", "Specific problem Id", commando.Int, nil).
82                 AddFlag("severity", "Perceived severity", commando.String, nil).
83                 AddFlag("iinfo", "Application identifying info", commando.String, nil).
84                 AddFlag("aai", "Application additional info", commando.String, "-").
85                 AddFlag("host", "Alarm manager host address", commando.String, "localhost").
86                 AddFlag("port", "Alarm manager host address", commando.String, "8080").
87                 AddFlag("if", "http or rmr used as interface", commando.String, "http").
88                 SetAction(func(args map[string]commando.ArgValue, flags map[string]commando.FlagValue) {
89                         postAlarm(flags, readAlarmParams(flags, false), alarm.AlarmActionRaise, nil)
90                 })
91
92         // Clear an alarm
93         commando.
94                 Register("clear").
95                 SetShortDescription("Raises alarm with given parameters").
96                 AddFlag("moid", "Managed object Id", commando.String, nil).
97                 AddFlag("apid", "Application Id", commando.String, nil).
98                 AddFlag("sp", "Specific problem Id", commando.Int, nil).
99                 AddFlag("iinfo", "Application identifying info", commando.String, nil).
100                 AddFlag("host", "Alarm manager host address", commando.String, "localhost").
101                 AddFlag("port", "Alarm manager host address", commando.String, "8080").
102                 AddFlag("if", "http or rmr used as interface", commando.String, "http").
103                 SetAction(func(args map[string]commando.ArgValue, flags map[string]commando.FlagValue) {
104                         postAlarm(flags, readAlarmParams(flags, true), alarm.AlarmActionClear, nil)
105                 })
106
107         // Configure an alarm manager
108         commando.
109                 Register("configure").
110                 SetShortDescription("Configure alarm manager with given parameters").
111                 AddFlag("mal", "max active alarms", commando.Int, nil).
112                 AddFlag("mah", "max alarm history", commando.Int, nil).
113                 AddFlag("host", "Alarm manager host address", commando.String, "localhost").
114                 AddFlag("port", "Alarm manager host address", commando.String, "8080").
115                 SetAction(func(args map[string]commando.ArgValue, flags map[string]commando.FlagValue) {
116                         postAlarmConfig(flags)
117                 })
118         // Create alarm definition
119         commando.
120                 Register("define").
121                 SetShortDescription("Define alarm with given parameters").
122                 AddFlag("aid", "alarm identifier", commando.Int, nil).
123                 AddFlag("atx", "alarm text", commando.String, nil).
124                 AddFlag("ety", "event type", commando.String, nil).
125                 AddFlag("oin", "operation instructions", commando.String, nil).
126                 AddFlag("host", "Alarm manager host address", commando.String, "localhost").
127                 AddFlag("port", "Alarm manager host address", commando.String, "8080").
128                 SetAction(func(args map[string]commando.ArgValue, flags map[string]commando.FlagValue) {
129                         postAlarmDefinition(flags)
130                 })
131                 // Delete alarm definition
132         commando.
133                 Register("undefine").
134                 SetShortDescription("Define alarm with given parameters").
135                 AddFlag("aid", "alarm identifier", commando.Int, nil).
136                 AddFlag("host", "Alarm manager host address", commando.String, "localhost").
137                 AddFlag("port", "Alarm manager host address", commando.String, "8080").
138                 SetAction(func(args map[string]commando.ArgValue, flags map[string]commando.FlagValue) {
139                         deleteAlarmDefinition(flags)
140                 })
141                 // Conduct performance test for alarm-go
142         commando.
143                 Register("perf").
144                 SetShortDescription("Conduct performance test with given parameters").
145                 AddFlag("prf", "performance profile id", commando.Int, nil).
146                 AddFlag("nal", "number of alarms", commando.Int, nil).
147                 AddFlag("aps", "alarms per sec", commando.Int, nil).
148                 AddFlag("tim", "total time of test", commando.Int, nil).
149                 AddFlag("host", "Alarm manager host address", commando.String, "localhost").
150                 AddFlag("port", "Alarm manager host address", commando.String, "8080").
151                 AddFlag("if", "http or rmr used as interface", commando.String, "http").
152                 SetAction(func(args map[string]commando.ArgValue, flags map[string]commando.FlagValue) {
153                         conductperformancetest(flags)
154                 })
155
156         // parse command-line arguments
157         commando.Parse(nil)
158 }
159
160 func readAlarmParams(flags map[string]commando.FlagValue, clear bool) (a alarm.Alarm) {
161         a.ManagedObjectId, _ = flags["moid"].GetString()
162         a.ApplicationId, _ = flags["apid"].GetString()
163         a.SpecificProblem, _ = flags["sp"].GetInt()
164         a.IdentifyingInfo, _ = flags["iinfo"].GetString()
165
166         if !clear {
167                 s, _ := flags["severity"].GetString()
168                 a.PerceivedSeverity = alarm.Severity(s)
169         }
170
171         if !clear {
172                 a.AdditionalInfo, _ = flags["aai"].GetString()
173         }
174         return
175 }
176
177 func getAlarms(flags map[string]commando.FlagValue, action alarm.AlarmAction) (alarms []alarm.AlarmMessage) {
178         host, _ := flags["host"].GetString()
179         port, _ := flags["port"].GetString()
180         targetUrl := fmt.Sprintf("http://%s:%s/ric/v1/alarms/%s", host, port, action)
181         resp, err := http.Get(targetUrl)
182         if err != nil || resp == nil || resp.Body == nil {
183                 fmt.Println("Couldn't fetch active alarm list due to error: ", err)
184                 return alarms
185         }
186
187         defer resp.Body.Close()
188
189         body, err := ioutil.ReadAll(resp.Body)
190         if err != nil {
191                 fmt.Println("ioutil.ReadAll failed: ", err)
192                 return alarms
193         }
194
195         json.Unmarshal([]byte(body), &alarms)
196         return alarms
197 }
198
199 func postAlarmWithRmrIf(a alarm.Alarm, action alarm.AlarmAction, alarmClient *AlarmClient) {
200         if alarmClient == nil {
201                 alarmClient = NewAlarmClient("my-pod", "my-app")
202         }
203         if alarmClient == nil {
204                 return
205         }
206
207         // Wait until RMR is up-and-running
208         for !alarmClient.alarmer.IsRMRReady() {
209                 time.Sleep(100 * time.Millisecond)
210         }
211
212         if action == alarm.AlarmActionRaise {
213                 alarmClient.alarmer.Raise(a)
214         }
215
216         if action == alarm.AlarmActionClear {
217                 alarmClient.alarmer.Clear(a)
218         }
219         return
220 }
221
222 func postAlarmWithHttpIf(targetUrl string, a alarm.Alarm, action alarm.AlarmAction) {
223         m := alarm.AlarmMessage{Alarm: a, AlarmAction: action}
224         jsonData, err := json.Marshal(m)
225         if err != nil {
226                 fmt.Println("json.Marshal failed: ", err)
227                 return
228         }
229
230         resp, err := http.Post(targetUrl, "application/json", bytes.NewBuffer(jsonData))
231         if err != nil || resp == nil {
232                 fmt.Println("Couldn't fetch active alarm list due to error: ", err)
233                 return
234         }
235 }
236
237 func postAlarm(flags map[string]commando.FlagValue, a alarm.Alarm, action alarm.AlarmAction, alarmClient *AlarmClient) {
238         // Check the interface to be used for raise or clear the alarm
239         rmr_or_http, _ := flags["if"].GetString()
240         if rmr_or_http == "rmr" {
241                 postAlarmWithRmrIf(a, action, alarmClient)
242         } else {
243
244                 host, _ := flags["host"].GetString()
245                 port, _ := flags["port"].GetString()
246                 targetUrl := fmt.Sprintf("http://%s:%s/ric/v1/alarms", host, port)
247                 postAlarmWithHttpIf(targetUrl, a, action)
248         }
249 }
250
251 func displayAlarms(alarms []alarm.AlarmMessage, isHistory bool) {
252         t := table.NewWriter()
253         t.SetOutputMirror(os.Stdout)
254         if isHistory {
255                 t.AppendHeader(table.Row{"SP", "MOID", "APPID", "IINFO", "SEVERITY", "AAI", "ACTION", "TIME"})
256         } else {
257                 t.AppendHeader(table.Row{"SP", "MOID", "APPID", "IINFO", "SEVERITY", "AAI", "TIME"})
258         }
259
260         for _, a := range alarms {
261                 alarmTime := time.Unix(0, a.AlarmTime).Format("02/01/2006, 15:04:05")
262                 if isHistory {
263                         t.AppendRows([]table.Row{
264                                 {a.SpecificProblem, a.ManagedObjectId, a.ApplicationId, a.IdentifyingInfo, a.PerceivedSeverity, a.AdditionalInfo, a.AlarmAction, alarmTime},
265                         })
266                 } else {
267                         t.AppendRows([]table.Row{
268                                 {a.SpecificProblem, a.ManagedObjectId, a.ApplicationId, a.IdentifyingInfo, a.PerceivedSeverity, a.AdditionalInfo, alarmTime},
269                         })
270                 }
271         }
272
273         t.SetStyle(table.StyleColoredBright)
274         t.Render()
275 }
276
277 func postAlarmConfig(flags map[string]commando.FlagValue) {
278         host, _ := flags["host"].GetString()
279         port, _ := flags["port"].GetString()
280         maxactivealarms, _ := flags["mal"].GetInt()
281         maxalarmhistory, _ := flags["mah"].GetInt()
282         targetUrl := fmt.Sprintf("http://%s:%s/ric/v1/alarms/config", host, port)
283
284         m := alarm.AlarmConfigParams{MaxActiveAlarms: maxactivealarms, MaxAlarmHistory: maxalarmhistory}
285         jsonData, err := json.Marshal(m)
286         if err != nil {
287                 fmt.Println("json.Marshal failed: ", err)
288                 return
289         }
290
291         resp, err := http.Post(targetUrl, "application/json", bytes.NewBuffer(jsonData))
292         if err != nil || resp == nil {
293                 fmt.Println("Couldn't fetch post alarm configuration due to error: ", err)
294                 return
295         }
296 }
297
298 func postAlarmDefinition(flags map[string]commando.FlagValue) {
299         host, _ := flags["host"].GetString()
300         port, _ := flags["port"].GetString()
301         alarmid, _ := flags["aid"].GetInt()
302         alarmtxt, _ := flags["atx"].GetString()
303         etype, _ := flags["ety"].GetString()
304         operation, _ := flags["oin"].GetString()
305         targetUrl := fmt.Sprintf("http://%s:%s/ric/v1/alarms/define", host, port)
306
307         var alarmdefinition alarm.AlarmDefinition
308         alarmdefinition.AlarmId = alarmid
309         alarmdefinition.AlarmText = alarmtxt
310         alarmdefinition.EventType = etype
311         alarmdefinition.OperationInstructions = operation
312
313         m := CliAlarmDefinitions{AlarmDefinitions: []*alarm.AlarmDefinition{&alarmdefinition}}
314         jsonData, err := json.Marshal(m)
315         if err != nil {
316                 fmt.Println("json.Marshal failed: ", err)
317                 return
318         }
319
320         resp, err := http.Post(targetUrl, "application/json", bytes.NewBuffer(jsonData))
321         if err != nil || resp == nil {
322                 fmt.Println("Couldn't post alarm definition due to error: ", err)
323                 return
324         }
325 }
326
327 func deleteAlarmDefinition(flags map[string]commando.FlagValue) {
328         host, _ := flags["host"].GetString()
329         port, _ := flags["port"].GetString()
330         alarmid, _ := flags["aid"].GetInt()
331         salarmid := strconv.FormatUint(uint64(alarmid), 10)
332         targetUrl := fmt.Sprintf("http://%s:%s/ric/v1/alarms/define/%s", host, port, salarmid)
333
334         client := &http.Client{}
335         req, err := http.NewRequest("DELETE", targetUrl, nil)
336         if err != nil || req == nil {
337                 fmt.Println("Couldn't make delete request due to error: ", err)
338                 return
339         }
340         resp, errr := client.Do(req)
341         if errr != nil || resp == nil {
342                 fmt.Println("Couldn't send delete request due to error: ", err)
343                 return
344         }
345 }
346
347 // NewAlarmClient returns a new AlarmClient.
348 func NewAlarmClient(moId, appId string) *AlarmClient {
349         alarmInstance, err := alarm.InitAlarm(moId, appId)
350         if err == nil {
351                 return &AlarmClient{
352                         alarmer: alarmInstance,
353                 }
354         }
355         fmt.Println("Failed to create alarmInstance", err)
356         return nil
357 }
358
359 // Conduct performance testing
360 func conductperformancetest(flags map[string]commando.FlagValue) {
361         var readerror error
362         var senderror error
363         var readobjerror error
364         host, _ := flags["host"].GetString()
365         port, _ := flags["port"].GetString()
366         targetUrl := fmt.Sprintf("http://%s:%s/ric/v1/alarms/define", host, port)
367         readerror = readPerfAlarmDefinitionFromJson()
368         if readerror == nil {
369                 senderror = sendPerfAlarmDefinitionToAlarmManager(targetUrl)
370                 if senderror == nil {
371                         fmt.Println("sent performance alarm definitions to alarm manager")
372                         CLIPerfAlarmObjects = make(map[int]*alarm.Alarm)
373                         readobjerror = readPerfAlarmObjectFromJson()
374                         if readobjerror == nil {
375                                 profile, _ := flags["prf"].GetInt()
376                                 if profile == 1 {
377                                         fmt.Println("starting peak performance test")
378                                         peakPerformanceTest(flags)
379                                 } else if profile == 2 {
380                                         fmt.Println("starting endurance test")
381                                         enduranceTest(flags)
382                                 } else {
383                                         fmt.Println("Unknown profile, received profile = ", profile)
384                                 }
385                         } else {
386                                 fmt.Println("reading performance alarm objects from json file failed ")
387                         }
388                 } else {
389                         fmt.Println("sending performance alarm definitions to alarm manager failed ")
390                 }
391
392         } else {
393                 fmt.Println("reading performance alarm definitions from json file failed ")
394         }
395
396 }
397
398 func peakPerformanceTest(flags map[string]commando.FlagValue) {
399         nalarms, _ := flags["nal"].GetInt()
400         var count int = 0
401         for aid, obj := range CLIPerfAlarmObjects {
402                 count = count + 1
403                 if count <= nalarms {
404                         fmt.Println("peakPerformanceTest: invoking worker routine ", count, aid, *obj)
405                         wg.Add(1)
406                         go raiseClearAlarmOnce(obj, flags)
407                 } else {
408                         break
409                 }
410         }
411         fmt.Println("peakPerformanceTest: Waiting for workers to finish")
412         wg.Wait()
413         fmt.Println("peakPerformanceTest: Wait completed")
414 }
415
416 func enduranceTest(flags map[string]commando.FlagValue) {
417         alarmspersec, _ := flags["aps"].GetInt()
418         var count int = 0
419         for aid, obj := range CLIPerfAlarmObjects {
420                 count = count + 1
421                 if count <= alarmspersec {
422                         fmt.Println("enduranceTest: invoking worker routine ", count, aid, *obj)
423                         wg.Add(1)
424                         go raiseClearAlarmOverPeriod(obj, flags)
425                 } else {
426                         break
427                 }
428         }
429         fmt.Println("enduranceTest: Waiting for workers to finish")
430         wg.Wait()
431         fmt.Println("enduranceTest: Wait completed")
432 }
433
434 func readPerfAlarmObjectFromJson() error {
435         filename := os.Getenv("PERF_OBJ_FILE")
436         file, err := ioutil.ReadFile(filename)
437         if err == nil {
438                 data := RicPerfAlarmObjects{}
439                 err = json.Unmarshal([]byte(file), &data)
440                 if err == nil {
441                         for _, alarmObject := range data.AlarmObjects {
442                                 ricAlarmObject := new(alarm.Alarm)
443                                 ricAlarmObject.ManagedObjectId = alarmObject.ManagedObjectId
444                                 ricAlarmObject.ApplicationId = alarmObject.ApplicationId
445                                 ricAlarmObject.SpecificProblem = alarmObject.SpecificProblem
446                                 ricAlarmObject.PerceivedSeverity = alarmObject.PerceivedSeverity
447                                 ricAlarmObject.AdditionalInfo = alarmObject.AdditionalInfo
448                                 ricAlarmObject.IdentifyingInfo = alarmObject.IdentifyingInfo
449                                 CLIPerfAlarmObjects[alarmObject.SpecificProblem] = ricAlarmObject
450                         }
451                 } else {
452                         fmt.Println("readPerfAlarmObjectFromJson: json.Unmarshal failed with error ", err)
453                         return err
454                 }
455         } else {
456                 fmt.Println("readPerfAlarmObjectFromJson: ioutil.ReadFile failed with error ", err)
457                 return err
458         }
459         return nil
460 }
461
462 func readPerfAlarmDefinitionFromJson() error {
463         filename := os.Getenv("PERF_DEF_FILE")
464         file, err := ioutil.ReadFile(filename)
465         if err == nil {
466                 data := CliAlarmDefinitions{}
467                 err = json.Unmarshal([]byte(file), &data)
468                 if err == nil {
469                         for _, alarmDefinition := range data.AlarmDefinitions {
470                                 _, exists := alarm.RICAlarmDefinitions[alarmDefinition.AlarmId]
471                                 if exists {
472                                         fmt.Println("ReadPerfAlarmDefinitionFromJson: alarm definition already exists for ", alarmDefinition.AlarmId)
473                                 } else {
474                                         fmt.Println("ReadPerfAlarmDefinitionFromJson: alarm ", alarmDefinition.AlarmId)
475                                         ricAlarmDefintion := new(alarm.AlarmDefinition)
476                                         ricAlarmDefintion.AlarmId = alarmDefinition.AlarmId
477                                         ricAlarmDefintion.AlarmText = alarmDefinition.AlarmText
478                                         ricAlarmDefintion.EventType = alarmDefinition.EventType
479                                         ricAlarmDefintion.OperationInstructions = alarmDefinition.OperationInstructions
480                                         CliPerfAlarmDefinitions.AlarmDefinitions = append(CliPerfAlarmDefinitions.AlarmDefinitions, ricAlarmDefintion)
481                                 }
482                         }
483                 } else {
484                         fmt.Println("ReadPerfAlarmDefinitionFromJson: json.Unmarshal failed with error: ", err)
485                         return err
486                 }
487         } else {
488                 fmt.Println("ReadPerfAlarmDefinitionFromJson: ioutil.ReadFile failed with error: ", err)
489                 return err
490         }
491         return nil
492 }
493
494 func sendPerfAlarmDefinitionToAlarmManager(targetUrl string) error {
495
496         jsonData, err := json.Marshal(CliPerfAlarmDefinitions)
497         if err != nil {
498                 fmt.Println("sendPerfAlarmDefinitionToAlarmManager: json.Marshal failed: ", err)
499                 return err
500         }
501
502         resp, err := http.Post(targetUrl, "application/json", bytes.NewBuffer(jsonData))
503         if err != nil || resp == nil {
504                 fmt.Println("sendPerfAlarmDefinitionToAlarmManager: Couldn't post alarm definition to targeturl due to error: ", targetUrl, err)
505                 return err
506         }
507         return nil
508 }
509
510 func wakeUpAfterTime(timeinseconds int, chn chan string, action string) {
511         time.Sleep(time.Second * time.Duration(timeinseconds))
512         chn <- action
513 }
514
515 func raiseClearAlarmOnce(alarmobject *alarm.Alarm, flags map[string]commando.FlagValue) {
516         var alarmClient *AlarmClient = nil
517         defer wg.Done()
518         chn := make(chan string, 1)
519         rmr_or_http, _ := flags["if"].GetString()
520         if rmr_or_http == "rmr" {
521                 alarmClient = NewAlarmClient("my-pod", "my-app")
522         }
523         postAlarm(flags, *alarmobject, alarm.AlarmActionRaise, alarmClient)
524         go wakeUpAfterTime(PeakTestDuration, chn, Clear)
525         select {
526         case res := <-chn:
527                 if res == Clear {
528                         postAlarm(flags, *alarmobject, alarm.AlarmActionClear, alarmClient)
529                         go wakeUpAfterTime(PeakTestDuration, chn, End)
530                 } else if res == End {
531                         return
532                 }
533         }
534 }
535
536 func raiseClearAlarmOverPeriod(alarmobject *alarm.Alarm, flags map[string]commando.FlagValue) {
537         var alarmClient *AlarmClient = nil
538         defer wg.Done()
539         timeinminutes, _ := flags["tim"].GetInt()
540         timeinseconds := timeinminutes * 60
541         chn := make(chan string, 1)
542         rmr_or_http, _ := flags["if"].GetString()
543         if rmr_or_http == "rmr" {
544                 alarmClient = NewAlarmClient("my-pod", "my-app")
545         }
546         postAlarm(flags, *alarmobject, alarm.AlarmActionRaise, alarmClient)
547         go wakeUpAfterTime(OneSecondDuration, chn, Clear)
548         go wakeUpAfterTime(timeinseconds, chn, End)
549         for {
550                 select {
551                 case res := <-chn:
552                         if res == Raise {
553                                 postAlarm(flags, *alarmobject, alarm.AlarmActionRaise, alarmClient)
554                                 go wakeUpAfterTime(OneSecondDuration, chn, Clear)
555                         } else if res == Clear {
556                                 postAlarm(flags, *alarmobject, alarm.AlarmActionClear, alarmClient)
557                                 go wakeUpAfterTime(OneSecondDuration, chn, Raise)
558                         } else if res == End {
559                                 return
560                         }
561                 }
562         }
563 }