Initial alarm library implementation 68/2668/6 v0.0.1
authorMohamed Abukar <abukar.mohamed@nokia.com>
Wed, 4 Mar 2020 08:01:45 +0000 (10:01 +0200)
committerMohamed Abukar <abukar.mohamed@nokia.com>
Thu, 5 Mar 2020 09:05:14 +0000 (11:05 +0200)
Signed-off-by: Mohamed Abukar <abukar.mohamed@nokia.com>
Change-Id: I1b824d422139d2135cd89905b432824afc7be71a

README.md [new file with mode: 0755]
adapter/cmd/adapter.go [new file with mode: 0755]
alarm/alarm.go [new file with mode: 0755]
alarm/alarm_test.go [new file with mode: 0755]
alarm/go.mod [new file with mode: 0644]
alarm/go.sum [new file with mode: 0644]
alarm/utils.c [new file with mode: 0755]
alarm/utils.h [new file with mode: 0755]
assets/alarm-adapter.png [new file with mode: 0755]
ci/Dockerfile [new file with mode: 0755]

diff --git a/README.md b/README.md
new file mode 100755 (executable)
index 0000000..5e42c3d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,100 @@
+RIC Alarm Adapter and Library Interface
+=======================================
+
+This repository containts Golang implementation of Alarm Adapter and related application library interface.
+
+Architecture
+------------
+
+![Architecture](assets/alarm-adapter.png)
+
+The **Alarm Library** provides a simple interface for RIC applications (both platform application and xApps) to raise, clear and re-raise. The **Alarm Library** interacts with the **Alarm Adapter** via RMR interface.
+
+The **Alarm Adapter** is reponsible for managing alarm situations in RIC cluster and interfacing with **Northboubd** applications such as **Prometheus AlertManager** to pass the alarms.
+
+Overview for Alarm Adapter
+--------------------------
+
+### TBD
+
+Overview for Alarm Library
+--------------------------
+
+## Initialization
+
+A new alarm instance is created with InitAlarm function. MO and application identities are given as a parameter.
+
+## Alarm Context and Format
+
+The Alarm object contains following parameters:
+ * *SpecificProblem*: problem that is the cause of the alarm
+ * *PerceivedSeverity*: The severity of the alarm, see above for possible values
+ * *ManagedObjectId*: The name of the managed object that is the cause of the fault
+ * *ApplicationId*: The name of the process raised the alarm
+ * *AdditionalInfo*: Additional information given by the application
+ * *IdentifyingInfo*: Identifying additional information, which is part of alarm identity
+
+## Alarm APIs
+* *Raise*: Raises the alarm instance given as a parameter
+* *Clear*: Clears the alarm instance given as a parameter, if it the alarm active
+* *Reraise*: Attempts to re-raise the alarm instance given as a parameter
+* *ClearAll*: Clears all alarms matching moId and appId given as parameters
+
+## Example
+-------
+
+```go
+package main
+
+import (
+       alarm "gerrit.o-ran-sc.org/r/ric-plt/alarm-go/alarm"
+)
+
+func main() {
+       // Initialize the alarm component
+       alarmer, err := alarm.InitAlarm("my-pod", "my-app")
+
+       // Create a new Alarm object
+       alarm := alarmer.NewAlarm(1234, alarm.SeverityMajor, "Some App data", "eth 0 1")
+
+       // Raise an alarm (SP=1234, etc)
+       err := alarmer.Raise(alarm)
+
+       // Clear an alarm (SP=1234)
+       err := alarmer.Clear(alarm)
+
+       // Re-raise an alarm (SP=1234)
+       err := alarmer.Reraise(alarm)
+
+       // Clear all alarms raised by the application
+       err := alarmer.Reraise()
+}
+```
+
+CI
+--
+
+The Dockerfile in the `ci` directory _only_ runs, when build, the library unit tests for the repository.
+
+License
+-------
+ Copyright (c) 2020 AT&T Intellectual Property.
+ Copyright (c) 2020 Nokia.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ This source code is part of the near-RT RIC (RAN Intelligent Controller)
+ platform project (RICP).
+
+
+
diff --git a/adapter/cmd/adapter.go b/adapter/cmd/adapter.go
new file mode 100755 (executable)
index 0000000..3a1ee02
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ *  Copyright (c) 2020 AT&T Intellectual Property.
+ *  Copyright (c) 2020 Nokia.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ * This source code is part of the near-RT RIC (RAN Intelligent Controller)
+ * platform project (RICP).
+ */
+package main
+
+import (
+       "log"
+)
+
+func main() {
+       log.Println("Not implemented yet!")
+}
diff --git a/alarm/alarm.go b/alarm/alarm.go
new file mode 100755 (executable)
index 0000000..2246abe
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ *  Copyright (c) 2020 AT&T Intellectual Property.
+ *  Copyright (c) 2020 Nokia.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ * This source code is part of the near-RT RIC (RAN Intelligent Controller)
+ * platform project (RICP).
+ */
+
+package alarm
+
+import (
+       "encoding/json"
+       "errors"
+       "fmt"
+       "log"
+       "sync"
+       "time"
+       "unsafe"
+)
+
+/*
+#cgo CFLAGS: -I../
+#cgo LDFLAGS: -lrmr_nng -lnng
+
+#include "utils.h"
+*/
+import "C"
+
+// Severity for alarms
+type Severity string
+
+// Possible values for Severity
+const (
+       SeverityUnspecified Severity = "UNSPECIFIED"
+       SeverityCritical    Severity = "CRITICAL"
+       SeverityMajor       Severity = "MAJOR"
+       SeverityMinor       Severity = "MINOR"
+       SeverityWarning     Severity = "WARNING"
+       SeverityNormal      Severity = "CLEARED"
+       SeverityDefault     Severity = "DEFAULT"
+)
+
+// Alarm object - see README for more information
+type Alarm struct {
+       ManagedObjectId   string   `json:"managedObjectId"`
+       ApplicationId     string   `json:"applicationId"`
+       SpecificProblem   int      `json:"specificProblem"`
+       PerceivedSeverity Severity `json:"perceivedSeverity"`
+       AdditionalInfo    string   `json:"additionalInfo"`
+       IdentifyingInfo   string   `json:"identifyingInfo"`
+}
+
+// Alarm actions
+type AlarmAction string
+
+// Possible values for alarm actions
+const (
+       AlarmActionRaise    AlarmAction = "RAISE"
+       AlarmActionClear    AlarmAction = "CLEAR"
+       AlarmActionReraise  AlarmAction = "RERAISE"
+       AlarmActionClearAll AlarmAction = "CLEARALL"
+)
+
+type AlarmMessage struct {
+       Alarm
+       AlarmAction
+       AlarmTime int64
+}
+
+// RICAlarm is an alarm instance
+type RICAlarm struct {
+       moId   string
+       appId  string
+       rmrCtx unsafe.Pointer
+       mutex  sync.Mutex
+}
+
+// InitAlarm is the init routine which returns a new alarm instance.
+// The MO and APP identities are given as a parameters.
+// The identities are used when raising/clearing alarms, unless provided by the applications.
+func InitAlarm(mo, id string) (*RICAlarm, error) {
+       if ctx := C.rmrInit(); ctx != nil {
+               r := &RICAlarm{
+                       moId:   mo,
+                       appId:  id,
+                       rmrCtx: ctx,
+               }
+
+               return r, nil
+       }
+
+       return nil, errors.New("rmrInit failed!")
+}
+
+// Create a new Alarm instance
+func (r *RICAlarm) NewAlarm(sp int, severity Severity, ainfo, iinfo string) Alarm {
+       return Alarm{
+               ManagedObjectId:   r.moId,
+               ApplicationId:     r.appId,
+               SpecificProblem:   sp,
+               PerceivedSeverity: severity,
+               AdditionalInfo:    ainfo,
+               IdentifyingInfo:   iinfo,
+       }
+}
+
+// Create a new AlarmMessage instance
+func (r *RICAlarm) NewAlarmMessage(a Alarm, alarmAction AlarmAction) AlarmMessage {
+       alarmTime := time.Now().UnixNano() / 1000
+       return AlarmMessage{a, alarmAction, alarmTime}
+}
+
+// Raise a RIC alarm
+func (r *RICAlarm) Raise(a Alarm) error {
+       r.mutex.Lock()
+       defer r.mutex.Unlock()
+
+       m := r.NewAlarmMessage(a, AlarmActionRaise)
+       return r.SendMessage(m)
+}
+
+// Clear a RIC alarm
+func (r *RICAlarm) Clear(a Alarm) error {
+       r.mutex.Lock()
+       defer r.mutex.Unlock()
+
+       m := r.NewAlarmMessage(a, AlarmActionClear)
+       return r.SendMessage(m)
+}
+
+// Re-raise a RIC alarm
+func (r *RICAlarm) Reraise(a Alarm) error {
+       r.mutex.Lock()
+       defer r.mutex.Unlock()
+
+       m := r.NewAlarmMessage(a, AlarmActionReraise)
+       return r.SendMessage(m)
+}
+
+// Clear all alarms raised by the application
+func (r *RICAlarm) ClearAll() error {
+       r.mutex.Lock()
+       defer r.mutex.Unlock()
+
+       a := r.NewAlarm(0, SeverityDefault, "", "")
+       m := r.NewAlarmMessage(a, AlarmActionClearAll)
+
+       return r.SendMessage(m)
+}
+
+// Internal functions
+func (r *RICAlarm) AlarmString(a AlarmMessage) string {
+       s := "MOId=%s AppId=%s SP=%d severity=%s IA=%s"
+       return fmt.Sprintf(s, a.ManagedObjectId, a.ApplicationId, a.SpecificProblem, a.PerceivedSeverity, a.IdentifyingInfo)
+}
+
+func (r *RICAlarm) SendMessage(a AlarmMessage) error {
+       log.Println("Sending alarm:", r.AlarmString(a))
+
+       payload, err := json.Marshal(a)
+       if err != nil {
+               return err
+       }
+
+       datap := C.CBytes(payload)
+       defer C.free(datap)
+       meid := C.CString("ric")
+       defer C.free(unsafe.Pointer(meid))
+
+       if state := C.rmrSend(r.rmrCtx, 1234, datap, C.int(len(payload)), meid); state != C.RMR_OK {
+               return errors.New(fmt.Sprintf("rmrSend failed with error: %d", state))
+       }
+       return nil
+}
+
+func (r *RICAlarm) ReceiveMessage(cb func(AlarmMessage)) error {
+       if rbuf := C.rmrRcv(r.rmrCtx); rbuf != nil {
+               payload := C.GoBytes(unsafe.Pointer(rbuf.payload), C.int(rbuf.len))
+               a := AlarmMessage{}
+               if err := json.Unmarshal(payload, &a); err == nil {
+                       cb(a)
+               }
+       }
+       return errors.New("rmrRcv failed!")
+}
diff --git a/alarm/alarm_test.go b/alarm/alarm_test.go
new file mode 100755 (executable)
index 0000000..ea56b00
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ *  Copyright (c) 2020 AT&T Intellectual Property.
+ *  Copyright (c) 2020 Nokia.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ * This source code is part of the near-RT RIC (RAN Intelligent Controller)
+ * platform project (RICP).
+ */
+
+package alarm_test
+
+import (
+       "github.com/stretchr/testify/assert"
+       "testing"
+       "time"
+
+       "gerrit.o-ran-sc.org/r/ric-plt/alarm-go/alarm"
+)
+
+var alarmer *alarm.RICAlarm
+
+// Test cases
+func TestAlarmInitSuccess(t *testing.T) {
+       a, err := alarm.InitAlarm("my-pod", "my-app")
+       assert.Nil(t, err, "init failed")
+       assert.Equal(t, false, a == nil)
+
+       alarmer = a
+}
+
+func TestAlarmRaiseSuccess(t *testing.T) {
+       a := alarmer.NewAlarm(1234, alarm.SeverityMajor, "Some App data", "eth 0 1")
+
+       err := alarmer.Raise(a)
+       assert.Nil(t, err, "raise failed")
+}
+
+func TestAlarmClearSuccess(t *testing.T) {
+       a := alarmer.NewAlarm(1234, alarm.SeverityMajor, "Some App data", "eth 0 1")
+
+       err := alarmer.Clear(a)
+       assert.Nil(t, err, "clear failed")
+}
+
+func TestAlarmReraiseSuccess(t *testing.T) {
+       a := alarmer.NewAlarm(1234, alarm.SeverityMajor, "Some App data", "eth 0 1")
+
+       err := alarmer.Reraise(a)
+       assert.Nil(t, err, "re-raise failed")
+}
+
+func TestAlarmClearAllSuccess(t *testing.T) {
+       err := alarmer.ClearAll()
+       assert.Nil(t, err, "clearAll failed")
+}
+
+func TestAlarmSendSuccess(t *testing.T) {
+       a := alarmer.NewAlarm(1234, alarm.SeverityMajor, "Some App data", "eth 0 1")
+
+       consumer := func(m alarm.AlarmMessage) {
+               assert.Equal(t, m.ManagedObjectId, a.ManagedObjectId)
+               assert.Equal(t, m.ApplicationId, a.ApplicationId)
+               assert.Equal(t, m.SpecificProblem, a.SpecificProblem)
+               assert.Equal(t, m.PerceivedSeverity, a.PerceivedSeverity)
+               assert.Equal(t, m.AdditionalInfo, a.AdditionalInfo)
+               assert.Equal(t, m.IdentifyingInfo, a.IdentifyingInfo)
+               assert.Equal(t, m.AlarmAction, alarm.AlarmActionRaise)
+       }
+
+       go alarmer.ReceiveMessage(consumer)
+       time.Sleep(time.Duration(1 * time.Second))
+
+       m := alarmer.NewAlarmMessage(a, alarm.AlarmActionRaise)
+       err := alarmer.SendMessage(m)
+       assert.Nil(t, err, "send failed")
+}
diff --git a/alarm/go.mod b/alarm/go.mod
new file mode 100644 (file)
index 0000000..8e4bc7f
--- /dev/null
@@ -0,0 +1,5 @@
+module gerrit.o-ran-sc.org/r/ric-plt/alarm-go/alarm
+
+go 1.13
+
+require github.com/stretchr/testify v1.5.1
diff --git a/alarm/go.sum b/alarm/go.sum
new file mode 100644 (file)
index 0000000..a80206a
--- /dev/null
@@ -0,0 +1,10 @@
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/alarm/utils.c b/alarm/utils.c
new file mode 100755 (executable)
index 0000000..0827069
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+==================================================================================
+  Copyright (c) 2020 AT&T Intellectual Property.
+  Copyright (c) 2020 Nokia
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================================
+*/
+
+#include <string.h>
+#include "utils.h"
+
+void * rmrInit(void) {
+    void* mrc;  // msg router context
+
+    if( (mrc = rmr_init("tcp:4588", 1024, RMRFL_NONE)) == NULL ) {
+        fprintf(stderr, "Unable to initialize RMR\n");
+        return NULL;
+    }
+
+    // Must have a route table before we can send, so wait til RMR is ready
+    while(!rmr_ready(mrc)) {
+        fprintf(stderr, "Waiting for RMR to be ready ...\n");
+        sleep(1);
+    }
+    fprintf(stderr, "RMR is ready now ...\n");
+
+    return mrc;
+}
+
+int rmrSend(void *mrc, int mtype, void *payload, int payload_len, char *meid) {
+    int retry_count = 0;
+    rmr_mbuf_t *sbuf = rmr_alloc_msg(mrc, 1024);
+
+    sbuf->mtype = mtype;
+    sbuf->sub_id = RMR_VOID_SUBID;
+    sbuf->state = 0;
+    sbuf->len = payload_len;
+    memcpy(sbuf->payload, payload, payload_len);
+    rmr_str2meid(sbuf, meid);
+
+    do {
+        sbuf = rmr_send_msg(mrc, sbuf);
+        if (sbuf == NULL) {
+            return -1;
+        }
+
+        if (sbuf->state == RMR_OK) {
+            break;
+        }
+    } while(sbuf->state == RMR_ERR_RETRY && ++retry_count < 10);
+
+    return sbuf->state;
+}
+
+rmr_mbuf_t * rmrRcv(void *mrc) {
+    while(1) {
+        rmr_mbuf_t *rbuf = rmr_rcv_msg(mrc, NULL);
+        if (rbuf != NULL && rbuf->state == RMR_OK) {
+            return rbuf;
+        }
+    }
+
+    return NULL;
+}
+
diff --git a/alarm/utils.h b/alarm/utils.h
new file mode 100755 (executable)
index 0000000..ce67fc2
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+==================================================================================
+  Copyright (c) 2020 AT&T Intellectual Property.
+  Copyright (c) 2020 Nokia
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================================
+*/
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <time.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <rmr/rmr.h>
+
+void * rmrInit(void);
+int rmrSend(void *mrc, int mtype, void *payload, int payload_len, char *meid);
+rmr_mbuf_t * rmrRcv(void *mrc);
+
+#endif
diff --git a/assets/alarm-adapter.png b/assets/alarm-adapter.png
new file mode 100755 (executable)
index 0000000..1239bd3
Binary files /dev/null and b/assets/alarm-adapter.png differ
diff --git a/ci/Dockerfile b/ci/Dockerfile
new file mode 100755 (executable)
index 0000000..8b33fea
--- /dev/null
@@ -0,0 +1,23 @@
+# Copyright (c) 2020 AT&T Intellectual Property.
+# Copyright (c) 2020 Nokia.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# This source code is part of the near-RT RIC (RAN Intelligent Controller)
+# platform project (RICP).
+
+FROM golang:1.12
+
+RUN mkdir -p /tmp/alarm
+COPY . /tmp/alarm
+RUN cd /tmp/alarm/lib && go test . -v -coverprofile cover.out