From: Mohamed Abukar Date: Wed, 4 Mar 2020 08:01:45 +0000 (+0200) Subject: Initial alarm library implementation X-Git-Tag: v0.0.1^0 X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=commitdiff_plain;h=3e0381545172d07ba76b9e84e41334a2e9cf66e4;p=ric-plt%2Falarm-go.git Initial alarm library implementation Signed-off-by: Mohamed Abukar Change-Id: I1b824d422139d2135cd89905b432824afc7be71a --- diff --git a/README.md b/README.md new file mode 100755 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 index 0000000..3a1ee02 --- /dev/null +++ b/adapter/cmd/adapter.go @@ -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 index 0000000..2246abe --- /dev/null +++ b/alarm/alarm.go @@ -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 index 0000000..ea56b00 --- /dev/null +++ b/alarm/alarm_test.go @@ -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 index 0000000..8e4bc7f --- /dev/null +++ b/alarm/go.mod @@ -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 index 0000000..a80206a --- /dev/null +++ b/alarm/go.sum @@ -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 index 0000000..0827069 --- /dev/null +++ b/alarm/utils.c @@ -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 +#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 index 0000000..ce67fc2 --- /dev/null +++ b/alarm/utils.h @@ -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 +#include +#include +#include +#include +#include + +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 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 index 0000000..8b33fea --- /dev/null +++ b/ci/Dockerfile @@ -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