From 0c38973604b5c63a91121d3641dd6f306b580acb Mon Sep 17 00:00:00 2001 From: Mohamed Abukar Date: Thu, 17 Sep 2020 14:47:50 +0300 Subject: [PATCH] A local user interface (CLI) for alarm system Change-Id: I1d1443456d724316a07021443d13270feadd975c Signed-off-by: Mohamed Abukar --- adapter/cmd/adapter.go | 19 +++--- adapter/cmd/types.go | 4 +- cli/alarm-cli.go | 165 +++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 3 + go.sum | 8 +++ 5 files changed, 187 insertions(+), 12 deletions(-) create mode 100755 cli/alarm-cli.go diff --git a/adapter/cmd/adapter.go b/adapter/cmd/adapter.go index f68d424..a3a9050 100755 --- a/adapter/cmd/adapter.go +++ b/adapter/cmd/adapter.go @@ -42,7 +42,7 @@ func (a *AlarmAdapter) StartAlertTimer() { a.mutex.Lock() for _, m := range a.activeAlarms { app.Logger.Info("Re-raising alarm: %v", m) - a.PostAlert(a.GenerateAlertLabels(m, AlertStatusActive)) + a.PostAlert(a.GenerateAlertLabels(m.Alarm, AlertStatusActive)) } a.mutex.Unlock() } @@ -102,7 +102,7 @@ func (a *AlarmAdapter) ProcessAlarm(m *alarm.AlarmMessage) (*alert.PostAlertsOK, // New alarm -> update active alarms and post to Alert Manager if m.AlarmAction == alarm.AlarmActionRaise { - a.UpdateAlarmLists(m.Alarm) + a.UpdateAlarmLists(m) return a.PostAlert(a.GenerateAlertLabels(m.Alarm, AlertStatusActive)) } @@ -112,15 +112,14 @@ func (a *AlarmAdapter) ProcessAlarm(m *alarm.AlarmMessage) (*alert.PostAlertsOK, func (a *AlarmAdapter) IsMatchFound(newAlarm alarm.Alarm) (int, bool) { for i, m := range a.activeAlarms { if m.ManagedObjectId == newAlarm.ManagedObjectId && m.ApplicationId == newAlarm.ApplicationId && - m.SpecificProblem == newAlarm.SpecificProblem && m.IdentifyingInfo == newAlarm.IdentifyingInfo && - m.PerceivedSeverity == newAlarm.PerceivedSeverity { + m.SpecificProblem == newAlarm.SpecificProblem && m.IdentifyingInfo == newAlarm.IdentifyingInfo { return i, true } } return -1, false } -func (a *AlarmAdapter) RemoveAlarm(alarms []alarm.Alarm, i int, listName string) []alarm.Alarm { +func (a *AlarmAdapter) RemoveAlarm(alarms []alarm.AlarmMessage, i int, listName string) []alarm.AlarmMessage { a.mutex.Lock() defer a.mutex.Unlock() @@ -129,7 +128,7 @@ func (a *AlarmAdapter) RemoveAlarm(alarms []alarm.Alarm, i int, listName string) return alarms[:len(alarms)-1] } -func (a *AlarmAdapter) UpdateAlarmLists(newAlarm alarm.Alarm) { +func (a *AlarmAdapter) UpdateAlarmLists(newAlarm *alarm.AlarmMessage) { a.mutex.Lock() defer a.mutex.Unlock() @@ -143,8 +142,8 @@ func (a *AlarmAdapter) UpdateAlarmLists(newAlarm alarm.Alarm) { } // @todo: For now just keep the alarms (both active and history) in-memory. Use SDL later for persistence - a.activeAlarms = append(a.activeAlarms, newAlarm) - a.alarmHistory = append(a.alarmHistory, newAlarm) + a.activeAlarms = append(a.activeAlarms, *newAlarm) + a.alarmHistory = append(a.alarmHistory, *newAlarm) } func (a *AlarmAdapter) GenerateAlertLabels(newAlarm alarm.Alarm, status AlertStatus) (models.LabelSet, models.LabelSet) { @@ -230,8 +229,8 @@ func NewAlarmAdapter(amHost string, alertInterval int) *AlarmAdapter { amBaseUrl: viper.GetString("controls.promAlertManager.baseUrl"), amSchemes: []string{viper.GetString("controls.promAlertManager.schemes")}, alertInterval: alertInterval, - activeAlarms: make([]alarm.Alarm, 0), - alarmHistory: make([]alarm.Alarm, 0), + activeAlarms: make([]alarm.AlarmMessage, 0), + alarmHistory: make([]alarm.AlarmMessage, 0), } } diff --git a/adapter/cmd/types.go b/adapter/cmd/types.go index 14cf421..e98fbf4 100755 --- a/adapter/cmd/types.go +++ b/adapter/cmd/types.go @@ -31,8 +31,8 @@ type AlarmAdapter struct { amBaseUrl string amSchemes []string alertInterval int - activeAlarms []alarm.Alarm - alarmHistory []alarm.Alarm + activeAlarms []alarm.AlarmMessage + alarmHistory []alarm.AlarmMessage mutex sync.Mutex rmrReady bool postClear bool diff --git a/cli/alarm-cli.go b/cli/alarm-cli.go new file mode 100755 index 0000000..212f8ca --- /dev/null +++ b/cli/alarm-cli.go @@ -0,0 +1,165 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "github.com/jedib0t/go-pretty/table" + "github.com/thatisuday/commando" + "io/ioutil" + "net/http" + "os" + "time" + + "gerrit.o-ran-sc.org/r/ric-plt/alarm-go/alarm" +) + +func main() { + + // configure commando + commando. + SetExecutableName("alarm-cli"). + SetVersion("1.0.0"). + SetDescription("This CLI tool provides management interface to SEP alarm system") + + // Get active alarms + commando. + Register("active"). + SetShortDescription("Displays the SEP active alarms"). + SetDescription("This command displays more information about the SEP active alarms"). + AddFlag("host", "Alarm manager host address", commando.String, "localhost"). + AddFlag("port", "Alarm manager host address", commando.String, "8080"). + SetAction(func(args map[string]commando.ArgValue, flags map[string]commando.FlagValue) { + displayAlarms(getAlarms(flags, "active"), false) + }) + + // Get alarm history + commando. + Register("history"). + SetShortDescription("Displays the SEP alarm history"). + SetDescription("This command displays more information about the SEP alarm history"). + AddFlag("host", "Alarm manager host address", commando.String, "localhost"). + AddFlag("port", "Alarm manager host address", commando.String, "8080"). + SetAction(func(args map[string]commando.ArgValue, flags map[string]commando.FlagValue) { + displayAlarms(getAlarms(flags, "history"), true) + }) + + // Raise an alarm + commando. + Register("raise"). + SetShortDescription("Raises alarm with given parameters"). + AddFlag("moid", "Managed object Id", commando.String, nil). + AddFlag("apid", "Application Id", commando.String, nil). + AddFlag("sp", "Specific problem Id", commando.Int, nil). + AddFlag("severity", "Perceived severity", commando.String, nil). + AddFlag("iinfo", "Application identifying info", commando.String, nil). + AddFlag("aai", "Application additional info", commando.String, "-"). + AddFlag("host", "Alarm manager host address", commando.String, "localhost"). + AddFlag("port", "Alarm manager host address", commando.String, "8080"). + SetAction(func(args map[string]commando.ArgValue, flags map[string]commando.FlagValue) { + postAlarm(flags, readAlarmParams(flags, false), alarm.AlarmActionRaise) + }) + + // Clear an alarm + commando. + Register("clear"). + SetShortDescription("Raises alarm with given parameters"). + AddFlag("moid", "Managed object Id", commando.String, nil). + AddFlag("apid", "Application Id", commando.String, nil). + AddFlag("sp", "Specific problem Id", commando.Int, nil). + AddFlag("iinfo", "Application identifying info", commando.String, nil). + AddFlag("host", "Alarm manager host address", commando.String, "localhost"). + AddFlag("port", "Alarm manager host address", commando.String, "8080"). + SetAction(func(args map[string]commando.ArgValue, flags map[string]commando.FlagValue) { + postAlarm(flags, readAlarmParams(flags, true), alarm.AlarmActionClear) + }) + + // parse command-line arguments + commando.Parse(nil) +} + +func readAlarmParams(flags map[string]commando.FlagValue, clear bool) (a alarm.Alarm) { + a.ManagedObjectId, _ = flags["moid"].GetString() + a.ApplicationId, _ = flags["apid"].GetString() + a.SpecificProblem, _ = flags["sp"].GetInt() + a.IdentifyingInfo, _ = flags["iinfo"].GetString() + + if !clear { + s, _ := flags["severity"].GetString() + a.PerceivedSeverity = alarm.Severity(s) + } + + if !clear { + a.AdditionalInfo, _ = flags["aai"].GetString() + } + + return +} + +func getAlarms(flags map[string]commando.FlagValue, action alarm.AlarmAction) (alarms []alarm.AlarmMessage) { + host, _ := flags["host"].GetString() + port, _ := flags["port"].GetString() + targetUrl := fmt.Sprintf("http://%s:%s/ric/v1/alarms/%s", host, port, action) + resp, err := http.Get(targetUrl) + if err != nil || resp == nil || resp.Body == nil { + fmt.Println("Couldn't fetch active alarm list due to error: %v", err) + return alarms + } + + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + fmt.Println("ioutil.ReadAll failed: %v", err) + return alarms + } + + json.Unmarshal([]byte(body), &alarms) + fmt.Println(alarms) + return alarms +} + +func postAlarm(flags map[string]commando.FlagValue, a alarm.Alarm, action alarm.AlarmAction) { + host, _ := flags["host"].GetString() + port, _ := flags["port"].GetString() + targetUrl := fmt.Sprintf("http://%s:%s/ric/v1/alarms", host, port) + + m := alarm.AlarmMessage{Alarm: a, AlarmAction: action} + jsonData, err := json.Marshal(m) + if err != nil { + fmt.Println("json.Marshal failed: %v", err) + return + } + + resp, err := http.Post(targetUrl, "application/json", bytes.NewBuffer(jsonData)) + if err != nil || resp == nil { + fmt.Println("Couldn't fetch active alarm list due to error: %v", err) + return + } +} + +func displayAlarms(alarms []alarm.AlarmMessage, isHistory bool) { + t := table.NewWriter() + t.SetOutputMirror(os.Stdout) + if isHistory { + t.AppendHeader(table.Row{"SP", "MOID", "APPID", "IINFO", "SEVERITY", "AAI", "TIME", "ACTION"}) + } else { + t.AppendHeader(table.Row{"SP", "MOID", "APPID", "IINFO", "SEVERITY", "AAI", "TIME"}) + } + + for _, a := range alarms { + alarmTime := time.Unix(0, a.AlarmTime).Format("02/01/2006, 15:04:05") + if isHistory { + t.AppendRows([]table.Row{ + {a.SpecificProblem, a.ManagedObjectId, a.ApplicationId, a.IdentifyingInfo, a.PerceivedSeverity, a.AdditionalInfo, alarmTime, a.AlarmAction}, + }) + } else { + t.AppendRows([]table.Row{ + {a.SpecificProblem, a.ManagedObjectId, a.ApplicationId, a.IdentifyingInfo, a.PerceivedSeverity, a.AdditionalInfo, alarmTime}, + }) + } + } + + t.SetStyle(table.StyleColoredBright) + t.Render() +} diff --git a/go.mod b/go.mod index cc1fa1d..23fac57 100644 --- a/go.mod +++ b/go.mod @@ -16,9 +16,12 @@ require ( gerrit.o-ran-sc.org/r/ric-plt/xapp-frame v0.0.0-00010101000000-000000000000 github.com/go-openapi/runtime v0.19.11 github.com/go-openapi/strfmt v0.19.4 + github.com/jedib0t/go-pretty v4.3.0+incompatible // indirect + github.com/mattn/go-runewidth v0.0.9 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.1 // indirect github.com/prometheus/alertmanager v0.20.0 github.com/spf13/viper v1.6.2 github.com/stretchr/testify v1.5.1 + github.com/thatisuday/commando v1.0.4 // indirect ) diff --git a/go.sum b/go.sum index 8489760..9acf904 100644 --- a/go.sum +++ b/go.sum @@ -170,6 +170,8 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/memberlist v0.1.4/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jedib0t/go-pretty v4.3.0+incompatible h1:CGs8AVhEKg/n9YbUenWmNStRW2PHJzaeDodcfvRAbIo= +github.com/jedib0t/go-pretty v4.3.0+incompatible/go.mod h1:XemHduiw8R651AF9Pt4FwCTKeG3oo7hrHJAoznj9nag= github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= @@ -199,6 +201,8 @@ github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= @@ -283,6 +287,10 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/thatisuday/clapper v1.0.10 h1:1EkqE/nb4npp8DuTKnpvVzO/Mcac9lOPND34uUKF+bU= +github.com/thatisuday/clapper v1.0.10/go.mod h1:FQGIg8q2uzeI+3SUS82YKF4E3KexkHStbiK4qTfDknM= +github.com/thatisuday/commando v1.0.4 h1:aNdH9tvmx2EPG6rT3NTQOV/qFYPf4Ap4Spo+q+n9Ois= +github.com/thatisuday/commando v1.0.4/go.mod h1:ODGz6jwJs4QqhLJtCjRRs8xIrmLLMdatYYddP+v1b4E= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -- 2.16.6