Initial version 39/239/2 v0.0.1 v0.0.2
authorMohamed Abukar <abukar.mohamed@nokia.com>
Sun, 2 Jun 2019 08:45:52 +0000 (11:45 +0300)
committerMohamed Abukar <abukar.mohamed@nokia.com>
Sun, 2 Jun 2019 12:52:58 +0000 (15:52 +0300)
Change-Id: Ia95ac0cc8321d78489de7b728449f94e5553f27a
Signed-off-by: Mohamed Abukar <abukar.mohamed@nokia.com>
30 files changed:
.gitignore [new file with mode: 0644]
Makefile [new file with mode: 0755]
README.md [new file with mode: 0755]
assets/xappframe-arch.png [new file with mode: 0755]
config/config-file.yaml [new file with mode: 0755]
config/uta_rtg.rt [new file with mode: 0755]
examples/example-xapp.go [new file with mode: 0755]
examples/go.mod [new file with mode: 0755]
examples/go.sum [new file with mode: 0644]
go.mod [new file with mode: 0644]
go.sum [new file with mode: 0644]
pkg/api/rest_api.json [new file with mode: 0644]
pkg/xapp/asn.go [new file with mode: 0755]
pkg/xapp/config.go [new file with mode: 0755]
pkg/xapp/db.go [new file with mode: 0755]
pkg/xapp/logger.go [new file with mode: 0755]
pkg/xapp/metrics.go [new file with mode: 0755]
pkg/xapp/mtypes.go [new file with mode: 0755]
pkg/xapp/restapi.go [new file with mode: 0755]
pkg/xapp/restapi_test.go [new file with mode: 0644]
pkg/xapp/rmr.go [new file with mode: 0755]
pkg/xapp/xapp.go [new file with mode: 0755]
pkg/xapp/xapp_test.go [new file with mode: 0755]
test/manifest/config-file-rx.yaml [new file with mode: 0755]
test/manifest/config-file-tx.yaml [new file with mode: 0755]
test/manifest/gnb-sim.yaml [new file with mode: 0755]
test/manifest/uta_rtg_ric.rt [new file with mode: 0755]
test/xapp/forwarder.go [new file with mode: 0755]
test/xapp/generator.go [new file with mode: 0755]
test/xapp/gnbsim.go [new file with mode: 0755]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..2b6464e
--- /dev/null
@@ -0,0 +1,6 @@
+cache
+build
+helm_chart
+scripts
+Dockerfile
+
diff --git a/Makefile b/Makefile
new file mode 100755 (executable)
index 0000000..4edffec
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,58 @@
+#   Copyright (c) 2019 AT&T Intellectual Property.
+#   Copyright (c) 2019 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.
+
+ROOT_DIR:=.
+BUILD_DIR:=$(ROOT_DIR)/build
+
+COVEROUT := $(BUILD_DIR)/cover.out
+COVERHTML := $(BUILD_DIR)/cover.html
+
+GOOS=$(shell go env GOOS)
+GOCMD=go
+GOBUILD=$(GOCMD) build -a -installsuffix cgo
+GOTEST=$(GOCMD) test -v -coverprofile $(COVEROUT)
+
+GOFILES := $(shell find $(ROOT_DIR) -name '*.go' -not -name '*_test.go') go.mod go.sum
+GOFILES_NO_VENDOR := $(shell find $(ROOT_DIR) -path ./vendor -prune -o -name "*.go" -not -name '*_test.go' -print)
+
+APP:=$(BUILD_DIR)/xapp-sim
+APPTST:=$(APP)_test
+
+.PHONY: FORCE
+.DEFAULT: build
+
+default: build
+
+$(APP): $(GOFILES)
+       GO111MODULE=on GO_ENABLED=0 GOOS=linux $(GOBUILD) -o $@ ./test/xapp
+
+$(APPTST): $(GOFILES)
+       GO111MODULE=on GO_ENABLED=0 GOOS=linux $(GOTEST) -c -o $@ ./pkg/xapp 
+       RMR_SEED_RT=config/uta_rtg.rt $@ -f config/config-file.yaml -test.coverprofile $(COVEROUT)
+       go tool cover -html=$(COVEROUT) -o $(COVERHTML)
+
+build: $(APP)
+
+test: $(APPTST)
+
+fmt: $(GOFILES_NO_VENDOR)
+       gofmt -w -s $^
+       @(RESULT="$$(gofmt -l $^)"; test -z "$${RESULT}" || (echo -e "gofmt failed:\n$${RESULT}" && false) )
+
+clean:
+       @echo "  >  Cleaning build cache"
+       @-rm -rf $(APP) $(APPTST) 2> /dev/null
+       go clean 2> /dev/null
diff --git a/README.md b/README.md
new file mode 100755 (executable)
index 0000000..71e03b4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,86 @@
+# XAPP-FRAME
+
+## Introduction
+**xapp-frame** is a simple framework for rapid development of RIC xapps, and supports various services essential for RIC xapps such as RESTful APIs, RMR (RIC Message Routing), database backend services and watching and populating config-map changes in K8S environment.
+
+## Architecture
+
+![Architecture](assets/xappframe-arch.png)
+
+## Features and Components
+
+* RESTful support
+* Health check/probes (readiness and liveliness)
+* Reading and watching config-map
+* RMR messaging
+* SDL
+* Loggind and tracing
+* Encoding and decoding of commonly used RIC ASN.1 messages
+* And more to come
+
+## Quick Start
+
+#### Below is a simple example xapp. For more information, see the sample code in the xapp/examples folder:
+```go
+package main
+
+import "gitlabe1.ext.net.nokia.com/ric_dev/nokia-xapps/xapp/pkg/xapp"
+
+type MessageCounsumer struct {
+}
+
+func (m MessageCounsumer) Consume(mtype, len int, payload []byte) (err error) {
+        xapp.Logger.Debug("Message received - type=%d len=%d", mtype, len)
+
+        xapp.Sdl.Store("myKey", payload)
+        xapp.Rmr.Send(10005, len, payload)
+        return nil
+}
+
+func main() {
+    xapp.Run(MessageCounsumer{})
+}
+```
+#### Installing and running the example xapp
+
+    git clone git@gitlabe1.ext.net.nokia.com:ric_dev/nokia-xapps/xapp.git
+
+#### Build and run
+    unset GOPATH
+    cd xapp/examples
+    go build example-xapp.go
+    ./example-xapp
+
+Congratulations! You've just built your first **xapp** application.
+
+## API
+#### API List
+ * TBD
+
+#### API Usage and Examples
+* Setting logging level and writing to log
+    ```
+    xapp.Logger.SetLevel(4)
+    xapp.Logger.Info("Status inquiry ...")
+    ```
+* Storing key-value data to SDL
+    ```
+    xapp.Sdl.Store("myKey", payload)
+    ```
+* Sending RMR messages
+    ```
+    mid := Rmr.GetRicMessageId("RIC_SUB_RESP")
+    xapp.Rmr.Send(mid, 1234, len, payload)
+    ```
+* Injecting REST API resources (URL)
+    ```
+    xapp.Resource.InjectRoute("/ric/v1/health/stat", statisticsHandler, "GET")
+    Resource.InjectQueryRoute("/ric/v1/user", handler, "GET", "foo", "bar", "id", "mykey")
+    ```
+
+## Documentation
+
+## Community
+
+## License
+This project is licensed under the Apache License 2.0 - see the [LICENSE.md](LICENSE.md) file for details
\ No newline at end of file
diff --git a/assets/xappframe-arch.png b/assets/xappframe-arch.png
new file mode 100755 (executable)
index 0000000..2f3e4bd
Binary files /dev/null and b/assets/xappframe-arch.png differ
diff --git a/config/config-file.yaml b/config/config-file.yaml
new file mode 100755 (executable)
index 0000000..b4bbde3
--- /dev/null
@@ -0,0 +1,38 @@
+#   Copyright (c) 2019 AT&T Intellectual Property.
+#   Copyright (c) 2019 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.
+
+"local":
+  "host": ":8080"
+"logger":
+  "level": 4
+"rmr":
+  "protPort": "tcp:4560"
+  "maxSize": 2072
+  "numWorkers": 1
+"db":
+  "host": "localhost"
+  "port": 6379
+  "namespaces": ["sdl", "rnib"]
+"test":
+  "mode": "forwarder"
+  "mtype": 10004
+  "subId": 1111
+  "size": 100
+  "rate": 10
+  "amount": 10
+  "rounds": 1
+  "store": 0
+  "waitForAck": 0
+
diff --git a/config/uta_rtg.rt b/config/uta_rtg.rt
new file mode 100755 (executable)
index 0000000..2b08655
--- /dev/null
@@ -0,0 +1,4 @@
+newrt|start
+rte|10004|localhost:4560
+rte|10005|localhost:4591
+newrt|end
diff --git a/examples/example-xapp.go b/examples/example-xapp.go
new file mode 100755 (executable)
index 0000000..03764a1
--- /dev/null
@@ -0,0 +1,18 @@
+package main
+
+import "gitlabe1.ext.net.nokia.com/ric_dev/nokia-xapps/xapp/pkg/xapp"
+
+type MessageCounter struct {
+}
+
+func (m MessageCounter) Consume(mtype, len int, payload []byte) (err error) {
+       xapp.Logger.Debug("Message received - type=%d len=%d", mtype, len)
+
+       xapp.Sdl.Store("myKey", payload)
+       xapp.Rmr.Send(10005, len, payload)
+       return nil
+}
+
+func main() {
+       xapp.Run(MessageCounter{})
+}
diff --git a/examples/go.mod b/examples/go.mod
new file mode 100755 (executable)
index 0000000..c39c00b
--- /dev/null
@@ -0,0 +1,6 @@
+go 1.12
+
+module gitlabe1.ext.net.nokia.com/example-xapp
+
+require gitlabe1.ext.net.nokia.com/ric_dev/nokia-xapps/xapp v0.0.0
+replace gitlabe1.ext.net.nokia.com/ric_dev/nokia-xapps/xapp => gitlabe1.ext.net.nokia.com/ric_dev/nokia-xapps/xapp.git v0.0.0-20190520084019-6d175b3c7483
diff --git a/examples/go.sum b/examples/go.sum
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/go.mod b/go.mod
new file mode 100644 (file)
index 0000000..ce000a7
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,15 @@
+module gerrit.o-ran-sc.org/r/ric-plt/xapp-frame
+
+go 1.12
+
+require (
+       gerrit.o-ran-sc.org/r/ric-plt/sdlgo v0.1.1
+       github.com/BurntSushi/toml v0.3.1 // indirect
+       github.com/fsnotify/fsnotify v1.4.7
+       github.com/gorilla/mux v1.7.1
+       github.com/prometheus/client_golang v0.9.3
+       github.com/spf13/viper v1.3.2
+       gitlabe1.ext.net.nokia.com/ric_dev/ue-nib latest
+)
+
+replace gerrit.o-ran-sc.org/r/ric-plt/sdlgo => gerrit.o-ran-sc.org/r/ric-plt/sdlgo.git v0.1.1
diff --git a/go.sum b/go.sum
new file mode 100644 (file)
index 0000000..7ad1b40
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,119 @@
+gerrit.o-ran-sc.org/r/ric-plt/sdlgo.git v0.1.1 h1:D2fU0/YXdqSNYsmptSBbkDfG76uBFKjnhQiq5cD4WT4=
+gerrit.o-ran-sc.org/r/ric-plt/sdlgo.git v0.1.1/go.mod h1:2Y8gw2jqj9urI8VFqFQn7BX0J3A852+YrXVV9V8gOt4=
+github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
+github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
+github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-redis/redis v6.15.2+incompatible h1:9SpNVG76gr6InJGxoZ6IuuxaCOQwDAhzyXg+Bs+0Sb4=
+github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/gorilla/mux v1.7.1 h1:Dw4jY2nghMMRsh1ol8dv1axHkDwMQK2DHerMNJsIpJU=
+github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+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/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+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/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
+github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
+github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+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/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
+github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
+github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY=
+github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M=
+github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
+github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+gitlabe1.ext.net.nokia.com/ric_dev/ue-nib v0.0.0-20190531121637-5379da45e235 h1:oTxU1A7TVgLmAkJGBTKYPxyBsq5KWm/gPjKuznIIpMQ=
+gitlabe1.ext.net.nokia.com/ric_dev/ue-nib v0.0.0-20190531121637-5379da45e235/go.mod h1:rhiDbAhxaCJouoZfj0+vSoYUWM2t9i1EdR0MpewgTYo=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a h1:1n5lsVfiQW3yfsRGu98756EH1YthsFqr/5mxHduZW2A=
+golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+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/pkg/api/rest_api.json b/pkg/api/rest_api.json
new file mode 100644 (file)
index 0000000..a778c1d
--- /dev/null
@@ -0,0 +1,43 @@
+{
+    "swagger" : "2.0",
+    "info" : {
+      "description" : "REST API specification xAPP Framework",
+      "version"     : "0.0.1",
+      "title"       : "RIC xAPP Framework",
+      "license" : {
+        "name" : "",
+        "url"  : "http://www.nokia.com"
+      }
+    },
+    "host"     : "xapp-service",
+    "basePath" : "/ric/v1",
+    "schemes"  : [
+      "https",
+      "http"
+    ],
+    "paths" : {
+      "/health/alive" : {
+        "get" : {
+          "summary"     : "Health check of xAPP - Liveness probe",
+          "operationId" : "getHealthAlive",
+          "responses"   : {
+            "200" : {
+              "description" : "Status of xApp is ok"
+            }
+          }
+        }
+      },
+      "/health/ready" : {
+        "get" : {
+          "summary"     : "Health check of xAPP - Readiness probe",
+          "operationId" : "getHealthReady",
+          "responses"   : {
+            "200" : {
+              "description" : "Status of xApp is ok"
+            }
+          }
+        }
+      }
+    }
+}
+  
\ No newline at end of file
diff --git a/pkg/xapp/asn.go b/pkg/xapp/asn.go
new file mode 100755 (executable)
index 0000000..9c50491
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+==================================================================================
+  Copyright (c) 2019 AT&T Intellectual Property.
+  Copyright (c) 2019 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.
+==================================================================================
+*/
+
+package xapp
+
+type ASN struct {
+}
+
+func (*ASN) Encode(mtype int, len int, pdu []byte) (b []byte) {
+       Logger.Info("Encode: mtype=%d len=%d ... not implemented yet!", mtype, len)
+
+       return
+}
+
+func (*ASN) Decode(mtype int, len int, pdu []byte) (b []byte) {
+       Logger.Info("Decode: mtype=%d len=%d ... not implemented yet", mtype, len)
+
+       return
+}
diff --git a/pkg/xapp/config.go b/pkg/xapp/config.go
new file mode 100755 (executable)
index 0000000..c63e32c
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+==================================================================================
+  Copyright (c) 2019 AT&T Intellectual Property.
+  Copyright (c) 2019 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.
+==================================================================================
+*/
+
+package xapp
+
+import (
+       "flag"
+       "github.com/fsnotify/fsnotify"
+       "github.com/spf13/viper"
+       "os"
+)
+
+type Configurator struct {
+}
+
+type ConfigChangeCB func(filename string)
+
+var ConfigChangeListeners []ConfigChangeCB
+
+func parseCmd() string {
+       var fileName *string
+       fileName = flag.String("f", os.Getenv("CFG_FILE"), "Specify the configuration file.")
+       flag.Parse()
+
+       return *fileName
+}
+
+func LoadConfig() (l Log) {
+       l = Log{}
+       viper.SetConfigFile(parseCmd())
+
+       if err := viper.ReadInConfig(); err != nil {
+               l.Error("Reading config file failed: %v", err.Error())
+       }
+       l.Info("Using config file: %s", viper.ConfigFileUsed())
+
+       viper.WatchConfig()
+       viper.OnConfigChange(func(e fsnotify.Event) {
+               l.Info("config file %s changed ", e.Name)
+
+               Logger.SetLevel(viper.GetInt("logger.level"))
+               if len(ConfigChangeListeners) > 0 {
+                       for _, f := range ConfigChangeListeners {
+                               go f(e.Name)
+                       }
+               }
+       })
+
+       return
+}
+
+func AddConfigChangeListener(f ConfigChangeCB) {
+       if ConfigChangeListeners == nil {
+               ConfigChangeListeners = make([]ConfigChangeCB, 0)
+       }
+       ConfigChangeListeners = append(ConfigChangeListeners, f)
+}
+
+func (*Configurator) GetString(key string) string {
+       return viper.GetString(key)
+}
+
+func (*Configurator) GetInt(key string) int {
+       return viper.GetInt(key)
+}
diff --git a/pkg/xapp/db.go b/pkg/xapp/db.go
new file mode 100755 (executable)
index 0000000..0ab0b1f
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+==================================================================================
+  Copyright (c) 2019 AT&T Intellectual Property.
+  Copyright (c) 2019 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.
+==================================================================================
+*/
+
+package xapp
+
+import (
+       sdl "gerrit.o-ran-sc.org/r/ric-plt/sdlgo"
+       "gitlabe1.ext.net.nokia.com/ric_dev/ue-nib/api"
+       "gitlabe1.ext.net.nokia.com/ric_dev/ue-nib/pkg/uenibreader"
+       "gitlabe1.ext.net.nokia.com/ric_dev/ue-nib/pkg/uenibwriter"
+       "sync"
+       "time"
+)
+
+// To be removed later
+type SDLStatistics struct{}
+
+var SDLCounterOpts = []CounterOpts{
+       {Name: "Stored", Help: "The total number of stored SDL transactions"},
+       {Name: "StoreError", Help: "The total number of SDL store errors"},
+}
+
+type SDLClient struct {
+       db   *sdl.SdlInstance
+       stat map[string]Counter
+       mux  sync.Mutex
+}
+
+type UENIBClient struct {
+       reader *uenibreader.Reader
+       writer *uenibwriter.Writer
+}
+
+type RNIBClient struct {
+       db *sdl.SdlInstance
+}
+
+// NewSDLClient returns a new SDLClient.
+func NewSDLClient(ns string) *SDLClient {
+       return &SDLClient{
+               db: sdl.NewSdlInstance(ns, sdl.NewDatabase()),
+       }
+}
+
+func (s *SDLClient) TestConnection() {
+       // Test DB connection, and wait until ready!
+       for {
+               if _, err := s.db.GetAll(); err == nil {
+                       break
+               }
+               Logger.Warn("Database connection not ready, waiting ...")
+               time.Sleep(time.Duration(5 * time.Second))
+       }
+       Logger.Info("Connection to database established!")
+
+       s.RegisterMetrics()
+}
+
+func (s *SDLClient) Store(key string, value interface{}) (err error) {
+       err = s.db.Set(key, value)
+       if err != nil {
+               s.UpdateStatCounter("StoreError")
+       } else {
+               s.UpdateStatCounter("Stored")
+       }
+       return
+}
+
+func (s *SDLClient) Read(key string) (value map[string]interface{}, err error) {
+       value, err = s.db.Get([]string{key})
+       return
+}
+
+func (s *SDLClient) Subscribe(cb func(string, ...string), channel string) error {
+       return s.db.SubscribeChannel(cb, channel)
+}
+
+func (s *SDLClient) MSubscribe(cb func(string, ...string), channels ...string) error {
+       return s.db.SubscribeChannel(cb, channels...)
+}
+
+func (s *SDLClient) StoreAndPublish(channel string, event string, pairs ...interface{}) error {
+       return s.db.SetAndPublish([]string{channel, event}, pairs...)
+}
+
+func (s *SDLClient) MStoreAndPublish(channelsAndEvents []string, pairs ...interface{}) error {
+       return s.db.SetAndPublish(channelsAndEvents, pairs...)
+}
+
+func (s *SDLClient) Clear() {
+       s.db.RemoveAll()
+}
+
+func (s *SDLClient) RegisterMetrics() {
+       s.stat = Metric.RegisterCounterGroup(SDLCounterOpts, "SDL")
+}
+
+func (s *SDLClient) UpdateStatCounter(name string) {
+       s.mux.Lock()
+       s.stat[name].Inc()
+       s.mux.Unlock()
+}
+
+func (c *SDLClient) GetStat() (t SDLStatistics) {
+       return
+}
+
+func NewUENIBClient() *UENIBClient {
+       return &UENIBClient{
+               reader: uenibreader.NewReader(),
+               writer: uenibwriter.NewWriter(),
+       }
+}
+
+func (u *UENIBClient) StoreUeMeasurement(gNbId string, gNbUeX2ApId string, data *api.MeasResults) error {
+       return u.writer.UpdateUeMeasurement(gNbId, gNbUeX2ApId, data)
+}
+
+func (u *UENIBClient) ReadUeMeasurement(gNbId string, gNbUeX2ApId string) (*api.MeasResults, error) {
+       return u.reader.GetUeMeasurement(gNbId, gNbUeX2ApId)
+}
+
+// To be removed ...
+func NewRNIBClient(ns string) *RNIBClient {
+       return &RNIBClient{
+               db: sdl.NewSdlInstance(ns, sdl.NewDatabase()),
+       }
+}
+
+func (r *RNIBClient) GetgNBList() (values map[string]interface{}, err error) {
+       keys, err := r.db.GetAll()
+       if err == nil {
+               values = make(map[string]interface{})
+               for _, key := range keys {
+                       v, err := r.db.Get([]string{key})
+                       if err == nil {
+                               values[key] = v[key]
+                       }
+               }
+       }
+       return
+}
+
+func (r *RNIBClient) GetNRCellList(key string) (value map[string]interface{}, err error) {
+       return r.db.Get([]string{key})
+}
+
+func (r *RNIBClient) GetUE(key1, key2 string) (value map[string]interface{}, err error) {
+       return r.db.Get([]string{key1 + key2})
+}
+
+func (r *RNIBClient) Store(key string, value interface{}) (err error) {
+       return r.db.Set(key, value)
+}
+
+func (r *RNIBClient) Clear() {
+       r.db.RemoveAll()
+}
diff --git a/pkg/xapp/logger.go b/pkg/xapp/logger.go
new file mode 100755 (executable)
index 0000000..a73cb36
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+==================================================================================
+  Copyright (c) 2019 AT&T Intellectual Property.
+  Copyright (c) 2019 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.
+==================================================================================
+*/
+
+package xapp
+
+/*
+#cgo CFLAGS: -I/usr/local/include
+#cgo LDFLAGS: -lmdclog
+#
+#include <mdclog/mdclog.h>
+void xAppMgr_mdclog_write(mdclog_severity_t severity, const char *msg) {
+     mdclog_write(severity, "%s", msg);
+}
+*/
+import "C"
+
+import (
+       "fmt"
+       "log"
+       "time"
+)
+
+type Log struct {
+}
+
+const (
+       LogLvlErr   = C.MDCLOG_ERR
+       LogLvlWarn  = C.MDCLOG_WARN
+       LogLvlInfo  = C.MDCLOG_INFO
+       LogLvlDebug = C.MDCLOG_DEBUG
+)
+
+func WriteLog(lvl C.mdclog_severity_t, msg string) {
+       t := time.Now().Format("2019-01-02 15:04:05")
+       text := fmt.Sprintf("%s:: %s ", t, msg)
+
+       C.xAppMgr_mdclog_write(lvl, C.CString(text))
+}
+
+func (Log) SetLevel(level int) {
+       l := C.mdclog_severity_t(level)
+       C.mdclog_level_set(l)
+}
+
+func (Log) SetMdc(key string, value string) {
+       C.mdclog_mdc_add(C.CString(key), C.CString(value))
+}
+
+func (Log) Fatal(pattern string, args ...interface{}) {
+       WriteLog(LogLvlErr, fmt.Sprintf(pattern, args...))
+       log.Panic("Fatal error occured, exiting ...")
+}
+
+func (Log) Error(pattern string, args ...interface{}) {
+       WriteLog(LogLvlErr, fmt.Sprintf(pattern, args...))
+}
+
+func (Log) Warn(pattern string, args ...interface{}) {
+       WriteLog(LogLvlWarn, fmt.Sprintf(pattern, args...))
+}
+
+func (Log) Info(pattern string, args ...interface{}) {
+       WriteLog(LogLvlInfo, fmt.Sprintf(pattern, args...))
+}
+
+func (Log) Debug(pattern string, args ...interface{}) {
+       WriteLog(LogLvlDebug, fmt.Sprintf(pattern, args...))
+}
diff --git a/pkg/xapp/metrics.go b/pkg/xapp/metrics.go
new file mode 100755 (executable)
index 0000000..e31fdb7
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+==================================================================================
+  Copyright (c) 2019 AT&T Intellectual Property.
+  Copyright (c) 2019 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.
+==================================================================================
+*/
+
+package xapp
+
+import (
+       "github.com/gorilla/mux"
+       "github.com/prometheus/client_golang/prometheus"
+       "github.com/prometheus/client_golang/prometheus/promauto"
+       "github.com/prometheus/client_golang/prometheus/promhttp"
+)
+
+type Metrics struct {
+       Namespace string
+}
+
+// Alias
+type CounterOpts prometheus.CounterOpts
+type Counter prometheus.Counter
+type Gauge prometheus.Gauge
+
+func NewMetrics(url, namespace string, r *mux.Router) *Metrics {
+       if url == "" {
+               url = "/ric/v1/metrics"
+       }
+       if namespace == "" {
+               namespace = "ricxapp"
+       }
+
+       Logger.Info("Serving metrics on: url=%s namespace=%s", url, namespace)
+
+       // Expose 'metrics' endpoint with standard golang metrics used by prometheus
+       r.Handle(url, promhttp.Handler())
+
+       return &Metrics{Namespace: namespace}
+}
+
+func (m *Metrics) RegisterCounter(opts CounterOpts) Counter {
+       Logger.Info("Register new counter with opts: %v", opts)
+
+       return promauto.NewCounter(prometheus.CounterOpts(opts))
+}
+
+func (m *Metrics) RegisterCounterGroup(opts []CounterOpts, subsytem string) (c map[string]Counter) {
+       c = make(map[string]Counter)
+       for _, opt := range opts {
+               opt.Namespace = m.Namespace
+               opt.Subsystem = subsytem
+               c[opt.Name] = m.RegisterCounter(opt)
+       }
+
+       return
+}
+
+func (m *Metrics) RegisterGauge(opts CounterOpts) Gauge {
+       Logger.Info("Register new gauge with opts: %v", opts)
+
+       return promauto.NewGauge(prometheus.GaugeOpts(opts))
+}
+
+func (m *Metrics) RegisterGaugeGroup(opts []CounterOpts, subsytem string) (c map[string]Gauge) {
+       c = make(map[string]Gauge)
+       for _, opt := range opts {
+               opt.Namespace = m.Namespace
+               opt.Subsystem = subsytem
+               c[opt.Name] = m.RegisterGauge(opt)
+       }
+
+       return
+}
diff --git a/pkg/xapp/mtypes.go b/pkg/xapp/mtypes.go
new file mode 100755 (executable)
index 0000000..9a84447
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+==================================================================================
+  Copyright (c) 2019 AT&T Intellectual Property.
+  Copyright (c) 2019 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.
+==================================================================================
+*/
+
+package xapp
+
+/*
+#include <rmr/RIC_message_types.h>
+*/
+import "C"
+
+var RICMessageTypes = map[string]int{
+       "RIC_SUB_REQ":                  C.RIC_SUB_REQ,
+       "RIC_SUB_RESP":                 C.RIC_SUB_RESP,
+       "RIC_SUB_FAILURE":              C.RIC_SUB_FAILURE,
+       "RIC_SUB_DEL_REQ":              C.RIC_SUB_DEL_REQ,
+       "RIC_SUB_DEL_RESP":             C.RIC_SUB_DEL_RESP,
+       "RIC_SUB_DEL_FAILURE":          C.RIC_SUB_DEL_FAILURE,
+       "RIC_SERVICE_UPDATE":           C.RIC_SERVICE_UPDATE,
+       "RIC_SERVICE_UPDATE_ACK":       C.RIC_SERVICE_UPDATE_ACK,
+       "RIC_SERVICE_UPDATE_FAILURE":   C.RIC_SERVICE_UPDATE_FAILURE,
+       "RIC_CONTROL_REQ":              C.RIC_CONTROL_REQ,
+       "RIC_CONTROL_ACK":              C.RIC_CONTROL_ACK,
+       "RIC_CONTROL_FAILURE":          C.RIC_CONTROL_FAILURE,
+       "RIC_INDICATION":               C.RIC_INDICATION,
+       "RIC_SERVICE_QUERY":            C.RIC_SERVICE_QUERY,
+       "RIC_X2_SETUP_REQ":             C.RIC_X2_SETUP_REQ,
+       "RIC_X2_SETUP_RESP":            C.RIC_X2_SETUP_RESP,
+       "RIC_X2_SETUP_FAILURE":         C.RIC_X2_SETUP_FAILURE,
+       "RIC_X2_RESET":                 C.RIC_X2_RESET,
+       "RIC_X2_RESET_RESP":            C.RIC_X2_RESET_RESP,
+       "RIC_ENDC_X2_SETUP_REQ":        C.RIC_ENDC_X2_SETUP_REQ,
+       "RIC_ENDC_X2_SETUP_RESP":       C.RIC_ENDC_X2_SETUP_RESP,
+       "RIC_ENDC_X2_SETUP_FAILURE":    C.RIC_ENDC_X2_SETUP_FAILURE,
+       "RIC_ENDC_CONF_UPDATE":         C.RIC_ENDC_CONF_UPDATE,
+       "RIC_ENDC_CONF_UPDATE_ACK":     C.RIC_ENDC_CONF_UPDATE_ACK,
+       "RIC_ENDC_CONF_UPDATE_FAILURE": C.RIC_ENDC_CONF_UPDATE_FAILURE,
+       "RIC_RES_STATUS_REQ":           C.RIC_RES_STATUS_REQ,
+       "RIC_RES_STATUS_RESP":          C.RIC_RES_STATUS_RESP,
+       "RIC_RES_STATUS_FAILURE":       C.RIC_RES_STATUS_FAILURE,
+       "RIC_ENB_CONF_UPDATE":          C.RIC_ENB_CONF_UPDATE,
+       "RIC_ENB_CONF_UPDATE_ACK":      C.RIC_ENB_CONF_UPDATE_ACK,
+       "RIC_ENB_CONF_UPDATE_FAILURE":  C.RIC_ENB_CONF_UPDATE_FAILURE,
+       "RIC_ENB_LOAD_INFORMATION":     C.RIC_ENB_LOAD_INFORMATION,
+       "RIC_GNB_STATUS_INDICATION":    C.RIC_GNB_STATUS_INDICATION,
+       "RIC_RESOURCE_STATUS_UPDATE":   C.RIC_RESOURCE_STATUS_UPDATE,
+       "RIC_ERROR_INDICATION":         C.RIC_ERROR_INDICATION,
+       "DC_ADM_INT_CONTROL":           C.DC_ADM_INT_CONTROL,
+       "DC_ADM_INT_CONTROL_ACK":       C.DC_ADM_INT_CONTROL_ACK,
+}
diff --git a/pkg/xapp/restapi.go b/pkg/xapp/restapi.go
new file mode 100755 (executable)
index 0000000..291a874
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+==================================================================================
+  Copyright (c) 2019 AT&T Intellectual Property.
+  Copyright (c) 2019 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.
+==================================================================================
+*/
+
+package xapp
+
+import (
+       "encoding/json"
+       "github.com/gorilla/mux"
+       "net/http"
+)
+
+const (
+       ReadyURL = "/ric/v1/health/ready"
+       AliveURL = "/ric/v1/health/alive"
+)
+
+type StatusCb func() bool
+
+type Router struct {
+       router *mux.Router
+       cbMap  []StatusCb
+}
+
+func NewRouter() *Router {
+       r := &Router{
+               router: mux.NewRouter().StrictSlash(true),
+               cbMap:  make([]StatusCb, 0),
+       }
+
+       // Inject default routes for health probes
+       r.InjectRoute(ReadyURL, readyHandler, "GET")
+       r.InjectRoute(AliveURL, aliveHandler, "GET")
+
+       return r
+}
+
+func (r *Router) serviceChecker(inner http.HandlerFunc) http.HandlerFunc {
+       return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+               Logger.Info("restapi: method=%s url=%s", req.Method, req.URL.RequestURI())
+               if req.URL.RequestURI() == AliveURL || (Rmr.IsReady() && r.CheckStatus()) {
+                       inner.ServeHTTP(w, req)
+               } else {
+                       respondWithJSON(w, http.StatusServiceUnavailable, nil)
+               }
+       })
+}
+
+func (r *Router) InjectRoute(url string, handler http.HandlerFunc, method string) *mux.Route {
+       return r.router.HandleFunc(url, r.serviceChecker(handler)).Methods(method)
+}
+
+func (r *Router) InjectQueryRoute(url string, h http.HandlerFunc, m string, q ...string) *mux.Route {
+       return r.router.HandleFunc(url, r.serviceChecker(h)).Methods(m).Queries(q...)
+}
+
+func (r *Router) InjectStatusCb(f StatusCb) {
+       r.cbMap = append(r.cbMap, f)
+}
+
+func (r *Router) CheckStatus() (status bool) {
+       if len(r.cbMap) == 0 {
+               return true
+       }
+
+       for _, f := range r.cbMap {
+               status = f()
+       }
+       return
+}
+
+func readyHandler(w http.ResponseWriter, r *http.Request) {
+       respondWithJSON(w, http.StatusOK, nil)
+}
+
+func aliveHandler(w http.ResponseWriter, r *http.Request) {
+       respondWithJSON(w, http.StatusOK, nil)
+}
+
+func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
+       w.Header().Set("Content-Type", "application/json")
+       w.WriteHeader(code)
+       if payload != nil {
+               response, _ := json.Marshal(payload)
+               w.Write(response)
+       }
+}
diff --git a/pkg/xapp/restapi_test.go b/pkg/xapp/restapi_test.go
new file mode 100644 (file)
index 0000000..76d5c5f
--- /dev/null
@@ -0,0 +1,41 @@
+package xapp
+
+import (
+       "net/http"
+       "net/http/httptest"
+       "testing"
+)
+
+func TestGetHealthReadyCheck(t *testing.T) {
+       req, err := http.NewRequest("GET", "/ric/v1/health/ready", nil)
+       if err != nil {
+               t.Fatal(err)
+       }
+       rr := httptest.NewRecorder()
+       handler := http.HandlerFunc(readyHandler)
+       handler.ServeHTTP(rr, req)
+       if status := rr.Code; status != http.StatusOK {
+               t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK)
+       }
+       //expected := `{"ready": true}`
+       //if rr.Body.String() != expected {
+       //  t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected)
+       //}
+}
+
+func TestGetHealthAliveCheck(t *testing.T) {
+       req, err := http.NewRequest("GET", "/ric/v1/health/alive", nil)
+       if err != nil {
+               t.Fatal(err)
+       }
+       rr := httptest.NewRecorder()
+       handler := http.HandlerFunc(aliveHandler)
+       handler.ServeHTTP(rr, req)
+       if status := rr.Code; status != http.StatusOK {
+               t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK)
+       }
+       //expected := `{"alive": true}`
+       //if rr.Body.String() != expected {
+       //  t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected)
+       //}
+}
diff --git a/pkg/xapp/rmr.go b/pkg/xapp/rmr.go
new file mode 100755 (executable)
index 0000000..9b1edd7
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+==================================================================================
+  Copyright (c) 2019 AT&T Intellectual Property.
+  Copyright (c) 2019 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.
+==================================================================================
+*/
+
+package xapp
+
+/*
+#include <time.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <rmr/rmr.h>
+#include <rmr/RIC_message_types.h>
+
+void write_bytes_array(unsigned char *dst, void *data, int len) {
+    memcpy((void *)dst, (void *)data, len);
+}
+
+#cgo CFLAGS: -I../
+#cgo LDFLAGS: -lrmr_nng -lnng
+*/
+import "C"
+
+import (
+       "github.com/spf13/viper"
+       "strconv"
+       "sync"
+       "time"
+       "unsafe"
+)
+
+var RMRCounterOpts = []CounterOpts{
+       {Name: "Transmitted", Help: "The total number of transmited RMR messages"},
+       {Name: "Received", Help: "The total number of received RMR messages"},
+       {Name: "TransmitError", Help: "The total number of RMR transmission errors"},
+       {Name: "ReceiveError", Help: "The total number of RMR receive errors"},
+}
+
+// To be removed ...
+type RMRStatistics struct{}
+
+type RMRClient struct {
+       context   unsafe.Pointer
+       ready     int
+       wg        sync.WaitGroup
+       mux       sync.Mutex
+       stat      map[string]Counter
+       consumers []MessageConsumer
+}
+
+type MessageConsumer interface {
+       Consume(mtype int, sid int, len int, payload []byte) error
+}
+
+func NewRMRClient() *RMRClient {
+       r := &RMRClient{}
+       r.consumers = make([]MessageConsumer, 0)
+
+       p := C.CString(viper.GetString("rmr.protPort"))
+       m := C.int(viper.GetInt("rmr.maxSize"))
+       defer C.free(unsafe.Pointer(p))
+
+       r.context = C.rmr_init(p, m, C.int(0))
+       if r.context == nil {
+               Logger.Fatal("rmrClient: Initializing RMR context failed, bailing out!")
+       }
+
+       return r
+}
+
+func (m *RMRClient) Start(c MessageConsumer) {
+       m.RegisterMetrics()
+
+       for {
+               Logger.Info("rmrClient: Waiting for RMR to be ready ...")
+
+               if m.ready = int(C.rmr_ready(m.context)); m.ready == 1 {
+                       break
+               }
+               time.Sleep(10 * time.Second)
+       }
+       m.wg.Add(viper.GetInt("rmr.numWorkers"))
+
+       if c != nil {
+               m.consumers = append(m.consumers, c)
+       }
+
+       for w := 0; w < viper.GetInt("rmr.numWorkers"); w++ {
+               go m.Worker("worker-"+strconv.Itoa(w), 0)
+       }
+
+       m.Wait()
+}
+
+func (m *RMRClient) Worker(taskName string, msgSize int) {
+       p := viper.GetString("rmr.protPort")
+       Logger.Info("rmrClient: '%s': receiving messages on [%s]", taskName, p)
+
+       defer m.wg.Done()
+       for {
+               rxBuffer := C.rmr_rcv_msg(m.context, nil)
+               if rxBuffer == nil {
+                       m.UpdateStatCounter("ReceiveError")
+                       continue
+               }
+               m.UpdateStatCounter("Received")
+
+               go m.parseMessage(rxBuffer)
+       }
+}
+
+func (m *RMRClient) parseMessage(rxBuffer *C.rmr_mbuf_t) {
+       if len(m.consumers) == 0 {
+               Logger.Info("rmrClient: No message handlers defined, message discarded!")
+               return
+       }
+
+       for _, c := range m.consumers {
+               cptr := unsafe.Pointer(rxBuffer.payload)
+               payload := C.GoBytes(cptr, C.int(rxBuffer.len))
+
+               err := c.Consume(int(rxBuffer.mtype), int(rxBuffer.sub_id), int(rxBuffer.len), payload)
+               if err != nil {
+                       Logger.Warn("rmrClient: Consumer returned error: %v", err)
+               }
+       }
+}
+
+func (m *RMRClient) Allocate() *C.rmr_mbuf_t {
+       buf := C.rmr_alloc_msg(m.context, 0)
+       if buf == nil {
+               Logger.Fatal("rmrClient: Allocating message buffer failed!")
+       }
+
+       return buf
+}
+
+func (m *RMRClient) Send(mtype int, sid int, len int, payload []byte) bool {
+       buf := m.Allocate()
+
+       buf.mtype = C.int(mtype)
+       buf.sub_id = C.int(sid)
+       buf.len = C.int(len)
+       datap := C.CBytes(payload)
+       defer C.free(datap)
+
+       C.write_bytes_array(buf.payload, datap, C.int(len))
+
+       return m.SendBuf(buf)
+}
+
+func (m *RMRClient) SendBuf(txBuffer *C.rmr_mbuf_t) bool {
+       for i := 0; i < 10; i++ {
+               txBuffer.state = 0
+               txBuffer := C.rmr_send_msg(m.context, txBuffer)
+               if txBuffer == nil {
+                       break
+               } else if txBuffer.state != C.RMR_OK {
+                       if txBuffer.state != C.RMR_ERR_RETRY {
+                               time.Sleep(100 * time.Microsecond)
+                               m.UpdateStatCounter("TransmitError")
+                       }
+                       for j := 0; j < 100 && txBuffer.state == C.RMR_ERR_RETRY; j++ {
+                               txBuffer = C.rmr_send_msg(m.context, txBuffer)
+                       }
+               }
+
+               if txBuffer.state == C.RMR_OK {
+                       m.UpdateStatCounter("Transmitted")
+                       return true
+               }
+       }
+       m.UpdateStatCounter("TransmitError")
+       return false
+}
+
+func (m *RMRClient) UpdateStatCounter(name string) {
+       m.mux.Lock()
+       m.stat[name].Inc()
+       m.mux.Unlock()
+}
+
+func (m *RMRClient) RegisterMetrics() {
+       m.stat = Metric.RegisterCounterGroup(RMRCounterOpts, "RMR")
+}
+
+// To be removed ...
+func (m *RMRClient) GetStat() (r RMRStatistics) {
+       return
+}
+
+func (m *RMRClient) Wait() {
+       m.wg.Wait()
+}
+
+func (m *RMRClient) IsReady() bool {
+       return m.ready != 0
+}
+
+func (m *RMRClient) GetRicMessageId(mid string) int {
+       return RICMessageTypes[mid]
+}
diff --git a/pkg/xapp/xapp.go b/pkg/xapp/xapp.go
new file mode 100755 (executable)
index 0000000..d796afe
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+==================================================================================
+  Copyright (c) 2019 AT&T Intellectual Property.
+  Copyright (c) 2019 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.
+==================================================================================
+*/
+
+package xapp
+
+import (
+       "fmt"
+       "github.com/spf13/viper"
+       "net/http"
+)
+
+var (
+       // XApp is an application instance
+       Rmr      *RMRClient
+       Sdl      *SDLClient
+       UeNib    *UENIBClient
+       Rnib     *RNIBClient
+       Resource *Router
+       Metric   *Metrics
+       Logger   Log
+       Config   Configurator
+)
+
+func init() {
+       // Load xapp configuration
+       Logger = LoadConfig()
+
+       Logger.SetLevel(viper.GetInt("logger.level"))
+       Rmr = NewRMRClient()
+       Resource = NewRouter()
+       Config = Configurator{}
+       UeNib = NewUENIBClient()
+       Metric = NewMetrics(viper.GetString("metrics.url"), viper.GetString("metrics.namespace"), Resource.router)
+
+       if viper.IsSet("db.namespaces") {
+               namespaces := viper.GetStringSlice("db.namespaces")
+               if namespaces[0] != "" {
+                       Sdl = NewSDLClient(viper.GetStringSlice("db.namespaces")[0])
+               }
+               if namespaces[1] != "" {
+                       Rnib = NewRNIBClient(viper.GetStringSlice("db.namespaces")[1])
+               }
+       } else {
+               Sdl = NewSDLClient(viper.GetString("db.namespace"))
+       }
+}
+
+func Run(c MessageConsumer) {
+       go http.ListenAndServe(viper.GetString("local.host"), Resource.router)
+
+       Logger.Info(fmt.Sprintf("Xapp started, listening on: %s", viper.GetString("local.host")))
+
+       Sdl.TestConnection()
+       Rmr.Start(c)
+}
diff --git a/pkg/xapp/xapp_test.go b/pkg/xapp/xapp_test.go
new file mode 100755 (executable)
index 0000000..51ac171
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+==================================================================================
+  Copyright (c) 2019 AT&T Intellectual Property.
+  Copyright (c) 2019 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.
+==================================================================================
+*/
+
+package xapp
+
+import (
+       "github.com/gorilla/mux"
+       "net/http"
+       "net/http/httptest"
+       "os"
+       "strings"
+       "testing"
+       "time"
+)
+
+type Consumer struct {
+}
+
+func (m Consumer) Consume(mtype, sid, len int, payload []byte) (err error) {
+       Sdl.Store("myKey", payload)
+       return nil
+}
+
+// Test cases
+func TestMain(m *testing.M) {
+       // Just run on the background (for coverage)
+       go Run(Consumer{})
+
+       code := m.Run()
+       os.Exit(code)
+}
+
+func TestGetHealthCheckRetursServiceUnavailableError(t *testing.T) {
+       req, _ := http.NewRequest("GET", "/ric/v1/health/ready", nil)
+       response := executeRequest(req)
+
+       checkResponseCode(t, http.StatusServiceUnavailable, response.Code)
+}
+
+func TestGetHealthCheckReturnsSuccess(t *testing.T) {
+       // Wait until RMR is up-and-running
+       for Rmr.IsReady() == false {
+               time.Sleep(time.Duration(2) * time.Second)
+       }
+
+       req, _ := http.NewRequest("GET", "/ric/v1/health/ready", nil)
+       response := executeRequest(req)
+
+       checkResponseCode(t, http.StatusOK, response.Code)
+}
+
+func TestInjectQuerySinglePath(t *testing.T) {
+       var handler = func(w http.ResponseWriter, r *http.Request) {
+       }
+
+       Resource.InjectQueryRoute("/ric/v1/user", handler, "GET", "foo", "bar")
+
+       req, _ := http.NewRequest("GET", "/ric/v1/user?foo=bar", nil)
+       response := executeRequest(req)
+       checkResponseCode(t, http.StatusOK, response.Code)
+}
+
+func TestInjectQueryMultiplePaths(t *testing.T) {
+       var handler = func(w http.ResponseWriter, r *http.Request) {
+       }
+
+       Resource.InjectQueryRoute("/ric/v1/user", handler, "GET", "foo", "bar", "id", "mykey")
+
+       req, _ := http.NewRequest("GET", "/ric/v1/user?foo=bar&id=mykey", nil)
+       response := executeRequest(req)
+       checkResponseCode(t, http.StatusOK, response.Code)
+}
+
+func TestInjectQueryFailures(t *testing.T) {
+       var handler = func(w http.ResponseWriter, r *http.Request) {
+       }
+
+       Resource.InjectQueryRoute("/ric/v1/user", handler, "GET", "foo", "bar", "id", "mykey")
+
+       req, _ := http.NewRequest("GET", "/ric/v1/user?invalid=bar&no=mykey", nil)
+       response := executeRequest(req)
+       checkResponseCode(t, http.StatusNotFound, response.Code)
+}
+
+func TestMessagesReceivedSuccessfully(t *testing.T) {
+       for i := 0; i < 100; i++ {
+               Rmr.Send(10004, 1111, 100, []byte{1, 2, 3, 4, 5, 6})
+       }
+
+       // Allow time to process the messages
+       time.Sleep(time.Duration(2) * time.Second)
+
+       stats := getMetrics(t)
+       if !strings.Contains(stats, "ricxapp_RMR_Transmitted 100") {
+               t.Errorf("Error: ricxapp_RMR_Transmitted value incorrect")
+       }
+
+       if !strings.Contains(stats, "ricxapp_RMR_Received 100") {
+               t.Errorf("Error: ricxapp_RMR_Received value incorrect")
+       }
+
+       if !strings.Contains(stats, "ricxapp_RMR_TransmitError 0") {
+               t.Errorf("Error: ricxapp_RMR_TransmitError value incorrect")
+       }
+
+       if !strings.Contains(stats, "ricxapp_RMR_ReceiveError 0") {
+               t.Errorf("Error: ricxapp_RMR_ReceiveError value incorrect")
+       }
+
+       if !strings.Contains(stats, "ricxapp_SDL_Stored 100") {
+               t.Errorf("Error: ricxapp_SDL_Stored value incorrect")
+       }
+
+       if !strings.Contains(stats, "ricxapp_SDL_StoreError 0") {
+               t.Errorf("Error: ricxapp_SDL_StoreError value incorrect")
+       }
+}
+
+func TestGetgNBList(t *testing.T) {
+       Rnib.Store("Kiikale", "Hello")
+       Rnib.Store("mykey", "myval")
+
+       v, _ := Rnib.GetgNBList()
+       if v["Kiikale"] != "Hello" || v["mykey"] != "myval" {
+               t.Errorf("Error: GetgNBList failed!")
+       }
+}
+
+func TestSubscribeChannels(t *testing.T) {
+       var NotificationCb = func(ch string, events ...string) {
+               if ch != "channel1" {
+                       t.Errorf("Error: Callback function called with incorrect params")
+               }
+       }
+
+       if err := Sdl.Subscribe(NotificationCb, "channel1"); err != nil {
+               t.Errorf("Error: Subscribe failed: %v", err)
+       }
+       time.Sleep(time.Duration(2) * time.Second)
+
+       if err := Sdl.StoreAndPublish("channel1", "event", "key1", "data1"); err != nil {
+               t.Errorf("Error: Publish failed: %v", err)
+       }
+}
+
+func TestTeardown(t *testing.T) {
+       Sdl.Clear()
+       Rnib.Clear()
+}
+
+// Helper functions
+func executeRequest(req *http.Request) *httptest.ResponseRecorder {
+       rr := httptest.NewRecorder()
+       vars := map[string]string{"id": "1"}
+       req = mux.SetURLVars(req, vars)
+       Resource.router.ServeHTTP(rr, req)
+
+       return rr
+}
+
+func checkResponseCode(t *testing.T, expected, actual int) {
+       if expected != actual {
+               t.Errorf("Expected response code %d. Got %d\n", expected, actual)
+       }
+}
+
+func getMetrics(t *testing.T) string {
+       req, _ := http.NewRequest("GET", "/ric/v1/metrics", nil)
+       response := executeRequest(req)
+
+       return response.Body.String()
+}
\ No newline at end of file
diff --git a/test/manifest/config-file-rx.yaml b/test/manifest/config-file-rx.yaml
new file mode 100755 (executable)
index 0000000..4b1c3d0
--- /dev/null
@@ -0,0 +1,38 @@
+#   Copyright (c) 2019 AT&T Intellectual Property.
+#   Copyright (c) 2019 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.
+
+"local":
+  "host": ":8080"
+"logger":
+  "level": 4
+"rmr":
+  "protPort": "tcp:4560"
+  "maxSize": 2072
+  "numWorkers": 1
+"db":
+  "host": "localhost"
+  "port": 6379
+  "namespaces": ["sdl", "rnib"]
+"test":
+  "mode": "forwarder"
+  "mtype": 10005
+  "subId": 2222
+  "size": 100
+  "rate": 10
+  "amount": 10
+  "rounds": 1
+  "store": 0
+  "waitForAck": 0
+
diff --git a/test/manifest/config-file-tx.yaml b/test/manifest/config-file-tx.yaml
new file mode 100755 (executable)
index 0000000..c3ba458
--- /dev/null
@@ -0,0 +1,38 @@
+#   Copyright (c) 2019 AT&T Intellectual Property.
+#   Copyright (c) 2019 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.
+
+"local":
+  "host": ":8080"
+"logger":
+  "level": 4
+"rmr":
+  "protPort": "tcp:4591"
+  "maxSize": 2072
+  "numWorkers": 1
+"db":
+  "host": "localhost"
+  "port": 6379
+  "namespaces": ["sdl", "rnib"]
+"test":
+  "mode": "generator"
+  "mtype": 10004
+  "subId": 1111
+  "size": 100
+  "rate": 10
+  "amount": 10
+  "rounds": 1
+  "store": 0
+  "waitForAck": 0
+
diff --git a/test/manifest/gnb-sim.yaml b/test/manifest/gnb-sim.yaml
new file mode 100755 (executable)
index 0000000..bb05747
--- /dev/null
@@ -0,0 +1,37 @@
+---
+apiVersion: v1
+kind: Pod
+metadata:
+  name: gnb-sim
+  namespace: ricxapp
+spec:
+  hostname: gnb-sim
+  containers:
+  - name: gnb-sim
+    image: 192.168.0.6:5000/gnb-sim:latest
+    env:
+    - name: RMR_SEED_RT
+      value: "/opt/gnbsim/uta_rtg_ric.rt"
+    ports:
+      - name: sim
+        containerPort: 4591
+        protocol: TCP
+      - name: rep
+        containerPort: 4560
+        protocol: TCP
+    command: ["/bin/bash", "-c", "--"]
+    args: ["while true; do sleep 30; done;"]
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: gnb-sim-service
+  namespace: ricxapp
+spec:
+  type: ClusterIP
+  ports:
+    - port: 4591
+      targetPort: 4591
+      protocol: TCP
+      name: sim
+
diff --git a/test/manifest/uta_rtg_ric.rt b/test/manifest/uta_rtg_ric.rt
new file mode 100755 (executable)
index 0000000..afe9319
--- /dev/null
@@ -0,0 +1,4 @@
+newrt|start
+rte|10004|uemgr-service:4560,anr-service:4560,dualco-service:4560
+rte|10005|gnb-sim-service:4591
+newrt|end
diff --git a/test/xapp/forwarder.go b/test/xapp/forwarder.go
new file mode 100755 (executable)
index 0000000..03a3154
--- /dev/null
@@ -0,0 +1,38 @@
+package main
+
+import (
+       "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/xapp"
+)
+
+type Forwarder struct {
+}
+
+func (m Forwarder) Consume(mtype, subId, len int, payload []byte) (err error) {
+       xapp.Logger.Debug("Message received - type=%d subId=%d len=%d", mtype, subId, len)
+
+       // Store data and reply with the same message payload
+       if xapp.Config.GetInt("test.store") != 0 {
+               xapp.Sdl.Store("myKey", payload)
+       }
+
+       mid := xapp.Config.GetInt("test.mtype")
+       if mid != 0 {
+               mtype = mid
+       } else {
+               mtype = mtype + 1
+       }
+
+       sid := xapp.Config.GetInt("test.subId")
+       if sid != 0 {
+               subId = sid
+       }
+
+       if ok := xapp.Rmr.Send(mtype, subId, len, payload); !ok {
+               xapp.Logger.Info("Rmr.Send failed ...")
+       }
+       return
+}
+
+func forwarder() {
+       xapp.Run(Forwarder{})
+}
diff --git a/test/xapp/generator.go b/test/xapp/generator.go
new file mode 100755 (executable)
index 0000000..4a86edb
--- /dev/null
@@ -0,0 +1,99 @@
+package main
+
+import (
+       "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/xapp"
+       "sync"
+       "time"
+)
+
+var (
+       wg     sync.WaitGroup
+       mux    sync.Mutex
+       rx     int
+       tx     int
+       failed int
+)
+
+type Generator struct {
+}
+
+func (m Generator) Consume(mtype, subId, len int, payload []byte) (err error) {
+       xapp.Logger.Debug("message received - type=%d subId=%d len=%d", mtype, subId, len)
+
+       mux.Lock()
+       rx++
+       mux.Unlock()
+
+       ack := xapp.Config.GetInt("test.waitForAck")
+       if ack != 0 {
+               wg.Done()
+       }
+
+       return nil
+}
+
+func waitForMessages() {
+       done := make(chan struct{})
+       go func() {
+               wg.Wait()
+               close(done)
+       }()
+
+       select {
+       case <-done:
+       // All done!
+       case <-time.After(5000 * time.Millisecond):
+               xapp.Logger.Warn("Message waiting timed out!")
+       }
+}
+
+func runTests(mtype, subId, amount, msize, ack int) {
+       tx = 0
+       rx = 0
+       s := make([]byte, msize, msize)
+
+       start := time.Now()
+       for i := 0; i < amount; i++ {
+               if ok := xapp.Rmr.Send(mtype, subId, msize, s); ok {
+                       tx++
+                       if ack != 0 {
+                               wg.Add(1)
+                       }
+               } else {
+                       failed++
+               }
+       }
+
+       // Wait until all replies are received, or timeout occurs
+       waitForMessages()
+
+       elapsed := time.Since(start)
+       xapp.Logger.Info("amount=%d|tx=%d|rx=%d|failed=%d|time=%v\n", amount, tx, rx, failed, elapsed)
+}
+
+func generator() {
+       // Start RMR and wait until engine is ready
+       go xapp.Rmr.Start(Generator{})
+       for xapp.Rmr.IsReady() == false {
+               time.Sleep(time.Duration(2) * time.Second)
+       }
+
+       // Read parameters
+       interval := 1000000 * 1.0 / xapp.Config.GetInt("test.rate")
+       mtype := xapp.Config.GetInt("test.mtype")
+       subId := xapp.Config.GetInt("test.subId")
+       amount := xapp.Config.GetInt("test.amount")
+       size := xapp.Config.GetInt("test.size")
+       ack := xapp.Config.GetInt("test.waitForAck")
+       rounds := xapp.Config.GetInt("test.rounds")
+
+       // Now generate message load as per request
+       for i := 0; i < rounds; i++ {
+               runTests(mtype, subId, amount, size, ack)
+               if interval != 0 {
+                       time.Sleep(time.Duration(interval) * time.Microsecond)
+               }
+       }
+
+       return
+}
diff --git a/test/xapp/gnbsim.go b/test/xapp/gnbsim.go
new file mode 100755 (executable)
index 0000000..8a46c58
--- /dev/null
@@ -0,0 +1,15 @@
+package main
+
+import (
+       "github.com/spf13/viper"
+)
+
+// A simple XApp sample program that use "xapp" skeleton
+
+func main() {
+       if viper.GetString("test.mode") == "generator" {
+               generator()
+       } else {
+               forwarder()
+       }
+}