Initial commit 51/2451/1
authorMohamed Abukar <abukar.mohamed@nokia.com>
Fri, 7 Feb 2020 13:58:55 +0000 (15:58 +0200)
committerMohamed Abukar <abukar.mohamed@nokia.com>
Fri, 7 Feb 2020 14:09:54 +0000 (16:09 +0200)
Contributed: Erkki Hietala <erkki.hietala@nokia.com>

Change-Id: I40f528311fa0444ff507613c6f6c1bb9d93a7598
Signed-off-by: Mohamed Abukar <abukar.mohamed@nokia.com>
24 files changed:
Dockerfile [new file with mode: 0755]
LICENSE [new file with mode: 0644]
README.md [new file with mode: 0644]
agent/Makefile [new file with mode: 0755]
agent/cli/o1-cli.go [new file with mode: 0755]
agent/cmd/o1agent.go [new file with mode: 0755]
agent/config/config-file.json [new file with mode: 0755]
agent/config/uta_rtg.rt [new file with mode: 0755]
agent/go.mod [new file with mode: 0644]
agent/go.sum [new file with mode: 0644]
agent/pkg/nbi/helper.c [new file with mode: 0755]
agent/pkg/nbi/helper.h [new file with mode: 0755]
agent/pkg/nbi/nbi.go [new file with mode: 0755]
agent/pkg/nbi/nbi_test.go [new file with mode: 0755]
agent/pkg/nbi/types.go [new file with mode: 0755]
agent/pkg/sbi/sbi.go [new file with mode: 0755]
agent/pkg/sbi/types.go [new file with mode: 0755]
agent/test/o1-create-configmap.xml [new file with mode: 0755]
agent/test/o1-create-xapp.xml [new file with mode: 0755]
agent/yang/o-ran-sc-ric-ueec-config-v1.yang [new file with mode: 0755]
agent/yang/o-ran-sc-ric-xapp-desc-v1.yang [new file with mode: 0755]
config/supervisord.conf [new file with mode: 0755]
container-tag.yaml [new file with mode: 0644]
manager/src/process-state.py [new file with mode: 0755]

diff --git a/Dockerfile b/Dockerfile
new file mode 100755 (executable)
index 0000000..1fa513b
--- /dev/null
@@ -0,0 +1,214 @@
+#   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.
+
+#----------------------------------------------------------
+FROM nexus3.o-ran-sc.org:10004/bldr-ubuntu18-c-go:2-u18.04-nng AS o2mediator-build
+
+RUN apt-get update -y && apt-get install -y jq \
+      git \
+      cmake \
+      build-essential \
+      vim \
+      supervisor \
+      libpcre3-dev \
+      pkg-config \
+      libavl-dev \
+      libev-dev \
+      libprotobuf-c-dev \
+      protobuf-c-compiler \
+      libssh-dev \
+      libssl-dev \
+      swig \
+      iputils-ping \
+      python-dev
+
+ENV PATH="/usr/local/go/bin:${PATH}"
+ENV GOPATH="/go"
+
+# ======================================================================
+# First make the netconf sysrepo stuff
+# add netconf user
+RUN \
+      adduser --system netconf && \
+      echo "netconf:netconf" | chpasswd
+
+# generate ssh keys for netconf user
+RUN \
+      mkdir -p /home/netconf/.ssh && \
+      ssh-keygen -A && \
+      ssh-keygen -t dsa -P '' -f /home/netconf/.ssh/id_dsa && \
+      cat /home/netconf/.ssh/id_dsa.pub > /home/netconf/.ssh/authorized_keys
+
+# use /opt/dev as working directory
+RUN mkdir /opt/dev
+WORKDIR /opt/dev
+
+# libyang
+RUN \
+      cd /opt/dev && \
+      git clone https://github.com/CESNET/libyang.git && \
+      cd libyang && mkdir build && cd build && \
+      cmake -DCMAKE_BUILD_TYPE:String="Release" -DENABLE_BUILD_TESTS=OFF .. && \
+      make -j2 && \
+      make install && \
+      ldconfig
+
+# sysrepo
+RUN \
+      cd /opt/dev && \
+      git clone https://github.com/sysrepo/sysrepo.git && \
+      cd sysrepo && mkdir build && cd build && \
+      cmake -DCMAKE_BUILD_TYPE:String="Release" -DSR_RPC_CB_TIMEOUT=30000 -DENABLE_TESTS=OFF -DREPOSITORY_LOC:PATH=/etc/sysrepo .. && \
+      make -j2 && \
+      make install && make sr_clean && \
+      ldconfig
+
+# libnetconf2
+RUN \
+      cd /opt/dev && \
+      git clone https://github.com/CESNET/libnetconf2.git && \
+      cd libnetconf2 && mkdir build && cd build && \
+      cmake -DCMAKE_BUILD_TYPE:String="Release" -DENABLE_BUILD_TESTS=OFF .. && \
+      make -j2 && \
+      make install && \
+      ldconfig
+
+# netopeer2
+RUN \
+      cd /opt/dev && \
+      git clone https://github.com/CESNET/Netopeer2.git && \
+      cd Netopeer2/server && mkdir build && cd build && \
+      cmake -DCMAKE_BUILD_TYPE:String="Release" .. && \
+      make -j2 && \
+      make install && \
+      cd ../../cli && mkdir build && cd build && \
+      cmake -DCMAKE_BUILD_TYPE:String="Release" .. && \
+      make -j2 && \
+      make install
+      
+# ======================================================================
+
+# RMR
+ARG RMRVERSION=1.11.0
+ARG RMRLIBURL=https://packagecloud.io/o-ran-sc/staging/packages/debian/stretch/rmr_${RMRVERSION}_amd64.deb/download.deb
+ARG RMRDEVURL=https://packagecloud.io/o-ran-sc/staging/packages/debian/stretch/rmr-dev_${RMRVERSION}_amd64.deb/download.deb
+
+RUN wget --content-disposition ${RMRLIBURL} && dpkg -i rmr_${RMRVERSION}_amd64.deb
+RUN wget --content-disposition ${RMRDEVURL} && dpkg -i rmr-dev_${RMRVERSION}_amd64.deb
+RUN rm -f rmr_${RMRVERSION}_amd64.deb rmr-dev_${RMRVERSION}_amd64.deb
+RUN ldconfig
+
+# Swagger
+RUN mkdir -p /go/bin
+RUN cd /go/bin \
+    && wget --quiet https://github.com/go-swagger/go-swagger/releases/download/v0.19.0/swagger_linux_amd64 \
+    && mv swagger_linux_amd64 swagger \
+    && chmod +x swagger
+
+RUN mkdir -p /go/src/ws
+WORKDIR "/go/src/ws/agent"
+
+# Module prepare (if go.mod/go.sum updated)
+COPY agent /go/src/ws
+RUN GO111MODULE=on go mod download
+
+RUN mkdir -p api \
+    && mkdir -p pkg \
+    && git clone "https://gerrit.o-ran-sc.org/r/ric-plt/appmgr" \
+    && cp appmgr/api/appmgr_rest_api.yaml api/ \
+    && rm -rf appmgr
+    
+# build and test
+COPY . /go/src/ws
+
+# generate swagger client
+RUN /go/bin/swagger generate client -f api/appmgr_rest_api.yaml -t pkg/ -m appmgrmodel -c appmgrclient
+# build the o1agent
+RUN GO111MODULE=on GO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o o1agent cmd/o1agent.go
+
+COPY . /go/src/ws
+
+# make the data model based on the ric yang model
+RUN /usr/local/bin/sysrepoctl -i /go/src/ws/agent/yang/o-ran-sc-ric-xapp-desc-v1.yang
+RUN /usr/local/bin/sysrepoctl -i /go/src/ws/agent/yang/o-ran-sc-ric-ueec-config-v1.yang
+
+CMD ["/bin/bash"]
+
+#----------------------------------------------------------
+FROM ubuntu:18.04 as o1mediator
+
+RUN apt-get update -y && apt-get install -y jq \
+      net-tools \
+      tcpdump \
+      netcat \
+      keychain \
+      nano \
+      supervisor \
+      openssl \
+      python-pip \
+      libpcre3-dev \
+      pkg-config \
+      libavl-dev \
+      libev-dev \
+      libprotobuf-c-dev \
+      protobuf-c-compiler \
+      libssh-dev \
+      libssl-dev \
+      swig \
+      python-dev \
+      && pip install supervisor-stdout \
+      && pip install psutil \
+      && apt-get clean
+
+RUN rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
+
+# add netconf user
+RUN \
+      adduser --system netconf && \
+      echo "netconf:netconf" | chpasswd
+
+# generate ssh keys for netconf user
+RUN \
+      mkdir -p /home/netconf/.ssh && \
+      ssh-keygen -A && \
+      ssh-keygen -t dsa -P '' -f /home/netconf/.ssh/id_dsa && \
+      cat /home/netconf/.ssh/id_dsa.pub > /home/netconf/.ssh/authorized_keys
+
+# copy the supervisor config
+ARG CONFIGDIR=/etc/supervisor
+RUN mkdir -p ${CONFIGDIR}
+COPY config/supervisord.conf ${CONFIGDIR}/supervisord.conf
+    
+# libraries and binaries & config
+COPY --from=o2mediator-build /usr/local/share/ /usr/local/share/
+COPY --from=o2mediator-build /usr/local/etc/ /usr/local/etc/
+COPY --from=o2mediator-build /usr/local/bin/ /usr/local/bin/
+COPY --from=o2mediator-build /usr/local/lib/ /usr/local/lib/
+RUN ldconfig
+
+# copy yang models with data
+COPY --from=o2mediator-build /etc/sysrepo /etc/sysrepo
+
+COPY --from=o2mediator-build /go/src/ws/agent/o1agent /usr/local/bin
+COPY --from=o2mediator-build /go/src/ws/manager/src/process-state.py /usr/local/bin
+RUN mkdir -p /etc/o1agent
+COPY --from=o2mediator-build /go/src/ws/agent/config/* /etc/o1agent/
+
+# ports available outside 8080 for mediator and 9001 supervise http control interrface
+# port 830 for netconf client ssh session
+# port 3000 for process-event handler web server
+EXPOSE 9001 830 8080 3000
+
+CMD ["/usr/bin/supervisord"]
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..593a0ef
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,31 @@
+==================================================================================
+Unless otherwise specified, all software contained herein is licensed
+under the Apache License, Version 2.0 (the "Software License");
+you may not use this software except in compliance with the Software
+License. You may obtain a copy of the Software License at
+http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the Software License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the Software License for the specific language governing permissions
+and limitations under the Software License.
+==================================================================================
+Unless otherwise specified, all documentation contained herein is licensed
+under the Creative Commons License, Attribution 4.0 Intl. (the
+"Documentation License"); you may not use this documentation except in
+compliance with the Documentation License. You may obtain a copy of the
+Documentation License at
+https://creativecommons.org/licenses/by/4.0/
+Unless required by applicable law or agreed to in writing, documentation
+distributed under the Documentation License is distributed on an "AS IS"
+BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+implied. See the Documentation License for the specific language governing
+permissions and limitations under the Documentation License.
+==================================================================================
+
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/agent/Makefile b/agent/Makefile
new file mode 100755 (executable)
index 0000000..02aca3e
--- /dev/null
@@ -0,0 +1,42 @@
+
+#   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.
+
+.DEFAULT: go-build
+
+default: go-build
+
+build: go-build
+
+test: go-test
+
+#------------------------------------------------------------------------------
+#
+#------------------------------------------------------------------------------
+ROOT_DIR:=$(dir $(abspath $(lastword $(MAKEFILE_LIST))))
+CACHE_DIR?=$(abspath $(ROOT_DIR)/cache)
+
+#------------------------------------------------------------------------------
+#
+# Build and test targets
+#
+#------------------------------------------------------------------------------
+
+XAPP_NAME:=o1agent
+XAPP_ROOT:=cmd
+include build/make.go.mk 
+
+XAPP_TESTENV:=RMR_SEED_RT=config/uta_rtg_ut.rt CFG_FILE=config/config-file.json
+XAPP_BUILDDEPS:=
diff --git a/agent/cli/o1-cli.go b/agent/cli/o1-cli.go
new file mode 100755 (executable)
index 0000000..eb5062e
--- /dev/null
@@ -0,0 +1,116 @@
+package main
+
+import (
+       "bytes"
+       "encoding/json"
+       "flag"
+       "fmt"
+       "github.com/Juniper/go-netconf/netconf"
+       xj "github.com/basgys/goxml2json"
+       "golang.org/x/crypto/ssh"
+       "io/ioutil"
+       "log"
+       "strings"
+       "time"
+)
+
+var (
+       host     = flag.String("host", "localhost", "Hostname")
+       username = flag.String("username", "netconf", "Username")
+       passwd   = flag.String("password", "netconf", "Password")
+       source   = flag.String("source", "running", "Source datastore")
+       target   = flag.String("target", "running", "Target datastore")
+       subtree  = flag.String("subtree", "netconf-server", "Subtree or module to select")
+       file     = flag.String("file", "", "Configuration file")
+       action   = flag.String("action", "get", "Netconf command: get or edit")
+       timeout  = flag.Int("timeout", 30, "Timeout")
+
+       getConfigXml  = "<get-config><source><%s/></source><filter type=\"subtree\"><%s/></filter></get-config>"
+       editConfigXml = "<edit-config><target><%s/></target><config>%s</config></edit-config>"
+)
+
+func main() {
+       if flag.Parse(); flag.Parsed() == false {
+               log.Fatal("Syntax error!")
+               return
+       }
+
+       switch *action {
+       case "get":
+               getConfig()
+       case "edit":
+               editConfig()
+       }
+}
+
+func getConfig() {
+       session := startSSHSession()
+       if session == nil {
+               return
+       }
+       defer session.Close()
+
+       cmd := netconf.RawMethod(fmt.Sprintf(getConfigXml, *source, *subtree))
+       reply, err := session.Exec(cmd)
+       if err != nil {
+               log.Fatal(err)
+               return
+       }
+       displayReply(reply.RawReply)
+}
+
+func editConfig() {
+       if *file == "" {
+               log.Fatal("Configuration file missing!")
+               return
+       }
+
+       session := startSSHSession()
+       if session == nil {
+               return
+       }
+       defer session.Close()
+
+       if data, err := ioutil.ReadFile(*file); err == nil {
+               cmd := netconf.RawMethod(fmt.Sprintf(editConfigXml, *target, data))
+               reply, err := session.Exec(cmd)
+               if err != nil {
+                       log.Fatal(err)
+                       return
+               }
+               displayReply(reply.RawReply)
+       }
+}
+
+func startSSHSession() *netconf.Session {
+       sshConfig := &ssh.ClientConfig{
+               User:            *username,
+               Auth:            []ssh.AuthMethod{ssh.Password(*passwd)},
+               HostKeyCallback: ssh.InsecureIgnoreHostKey(),
+               Timeout:         time.Duration(*timeout) * time.Second,
+       }
+
+       session, err := netconf.DialSSH(*host, sshConfig)
+       if err != nil {
+               log.Fatal(err)
+               return nil
+       }
+       return session
+}
+
+func prettyPrint(b string) string {
+       var out bytes.Buffer
+       if err := json.Indent(&out, []byte(b), "", "  "); err == nil {
+               return string(out.Bytes())
+       }
+       return ""
+}
+
+func displayReply(rawReply string) {
+       xml := strings.NewReader(rawReply)
+       json, err := xj.Convert(xml)
+       if err != nil {
+               log.Fatal("Something went sore ... XML is invalid!")
+       }
+       fmt.Println(prettyPrint(json.String()))
+}
diff --git a/agent/cmd/o1agent.go b/agent/cmd/o1agent.go
new file mode 100755 (executable)
index 0000000..76d19b6
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+==================================================================================
+  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 main
+
+import (
+       "os"
+    "os/signal"
+    "syscall"
+
+       "github.com/spf13/viper"
+       "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/xapp"
+       "gerrit.oran-osc.org/r/ric-plt/o1mediator/pkg/sbi"
+       "gerrit.oran-osc.org/r/ric-plt/o1mediator/pkg/nbi"
+)
+
+type O1Agent struct {
+       rmrReady        bool
+       nbiClient       *nbi.Nbi
+       sigChan         chan os.Signal
+}
+
+func (o O1Agent) Consume(rp *xapp.RMRParams) (err error) {
+       xapp.Logger.Debug("Message received!")
+       return nil
+}
+
+func (o *O1Agent) ConfigChangeHandler(f string) {
+       xapp.Logger.Debug("Config changed!")
+}
+
+func (o *O1Agent) StatusCB() bool {
+       if !o.rmrReady {
+               xapp.Logger.Info("RMR not ready yet!")
+       }
+       return true
+}
+
+func (o *O1Agent) Run() {
+       xapp.Logger.SetMdc("o1agent", "0.3.1")
+       xapp.SetReadyCB(func(d interface{}) { o.rmrReady = true }, true)
+       xapp.AddConfigChangeListener(o.ConfigChangeHandler)
+       xapp.Resource.InjectStatusCb(o.StatusCB)
+
+       signal.Notify(o.sigChan, syscall.SIGINT, syscall.SIGTERM)
+       go o.Sighandler()
+
+       xapp.Run(o)
+}
+
+func (o *O1Agent) Sighandler() {
+       xapp.Logger.Info("Signal handler installed!")
+
+       <- o.sigChan
+       o.nbiClient.Stop()
+       os.Exit(1)
+}
+
+func NewO1Agent() *O1Agent {
+       host := viper.GetString("sbi.appmgrService")
+       baseUrl := viper.GetString("sbi.baseUrl")
+       prot := viper.GetString("sbi.proto")
+       timeout := viper.GetInt("sbi.timeout")
+
+       sbiClient := sbi.NewSBIClient(host, baseUrl, []string{prot}, timeout)
+
+       return &O1Agent{
+               rmrReady: false,
+               nbiClient: nbi.NewNbi(sbiClient),
+               sigChan: make(chan os.Signal, 1),
+       }
+}
+
+func main() {
+       o1Agent := NewO1Agent()
+
+       if ok := o1Agent.nbiClient.Start(); !ok {
+               xapp.Logger.Error("NBI initialization failed!")
+               return
+       }
+       
+       o1Agent.Run()
+}
diff --git a/agent/config/config-file.json b/agent/config/config-file.json
new file mode 100755 (executable)
index 0000000..7bada6d
--- /dev/null
@@ -0,0 +1,32 @@
+{
+    "local": {
+        "host": ":8080"
+    },
+    "logger": {
+        "level": 4
+    },
+    "rmr": {
+        "protPort": "tcp:4560",
+        "maxSize": 65536,
+        "numWorkers": 1
+    },
+    "sbi": {
+        "appmgrService": "service-ricplt-appmgr-http:8080",
+        "baseUrl": "/ric/v1",
+        "proto": "http",
+        "timeout": 30
+    },
+    "nbi": {
+        "schemas": ["o-ran-sc-ric-xapp-desc-v1", "o-ran-sc-ric-ueec-config-v1"],
+        "modulePath": "/opt/ric/yang/",
+        "moduleFile": "ric-model.yang",
+        "xpaths": {
+            "xapp": "/ric-model:ric/xapps//*",
+            "config": "/ric-model:ric/config//*",
+            "data":"/ric-model:ric/xapps/status"
+        }
+    },
+    "controls": {
+        "active": true
+    }
+}
diff --git a/agent/config/uta_rtg.rt b/agent/config/uta_rtg.rt
new file mode 100755 (executable)
index 0000000..33f13e4
--- /dev/null
@@ -0,0 +1,16 @@
+newrt|start
+rte|12011|localhost:4560
+rte|12012|localhost:4560
+rte|12021|localhost:4560
+rte|12022|localhost:4560
+rte|12050|localhost:4560
+rte|12010|localhost:4560
+rte|12020|localhost:4560
+rte|10270|localhost:4560
+rte|10271|localhost:4560
+rte|10272|localhost:4560
+rte|10280|localhost:4560
+rte|10050|localhost:4560
+rte|10350|localhost:4560
+newrt|end
+
diff --git a/agent/go.mod b/agent/go.mod
new file mode 100644 (file)
index 0000000..f322edb
--- /dev/null
@@ -0,0 +1,37 @@
+module gerrit.oran-osc.org/r/ric-plt/o1mediator
+
+go 1.12
+
+replace gerrit.o-ran-sc.org/r/ric-plt/xapp-frame => gerrit.o-ran-sc.org/r/ric-plt/xapp-frame.git v0.0.21
+
+replace gerrit.o-ran-sc.org/r/ric-plt/sdlgo => gerrit.o-ran-sc.org/r/ric-plt/sdlgo.git v0.5.0
+
+replace gerrit.o-ran-sc.org/r/com/golog => gerrit.o-ran-sc.org/r/com/golog.git v0.0.1
+
+require (
+       gerrit.o-ran-sc.org/r/com/golog v0.0.1
+       gerrit.o-ran-sc.org/r/ric-plt/xapp-frame v0.0.0-00010101000000-000000000000
+       github.com/Juniper/go-netconf v0.1.1 // indirect
+       github.com/coreos/go-etcd v2.0.0+incompatible // indirect
+       github.com/fsnotify/fsnotify v1.4.7
+       github.com/go-openapi/errors v0.19.2
+       github.com/go-openapi/loads v0.19.4
+       github.com/go-openapi/runtime v0.19.7
+       github.com/go-openapi/spec v0.19.4
+       github.com/go-openapi/strfmt v0.19.3
+       github.com/go-openapi/swag v0.19.5
+       github.com/go-openapi/validate v0.19.4
+       github.com/gorilla/mux v1.7.1
+       github.com/jessevdk/go-flags v1.4.0
+       github.com/orcaman/concurrent-map v0.0.0-20190314100340-2693aad1ed75
+       github.com/segmentio/ksuid v1.0.2
+       github.com/spf13/viper v1.4.0
+       github.com/stretchr/testify v1.4.0
+       github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8 // indirect
+       github.com/valyala/fastjson v1.4.1
+       github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
+       github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
+       github.com/xeipuuv/gojsonschema v1.1.0
+       github.com/ziutek/telnet v0.0.0-20180329124119-c3b780dc415b // indirect
+       golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297
+)
diff --git a/agent/go.sum b/agent/go.sum
new file mode 100644 (file)
index 0000000..7daaca4
--- /dev/null
@@ -0,0 +1,320 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+gerrit.o-ran-sc.org/r/com/golog.git v0.0.1 h1:9RfO/Whehaaq5KiJTT7s+YOzmi9mT1C3HktfhwwMEmw=
+gerrit.o-ran-sc.org/r/com/golog.git v0.0.1/go.mod h1:b8YB31U8/4iRpABioeSzGi/YMzOQ/Zq7hrJmmXKqlJk=
+gerrit.o-ran-sc.org/r/ric-plt/nodeb-rnib.git/common v1.0.21 h1:eK9nUZOTMJ/EnMpH9bkWtMgOvCn3u4+PNCb9gu10s6w=
+gerrit.o-ran-sc.org/r/ric-plt/nodeb-rnib.git/common v1.0.21/go.mod h1:QJ1uPPZosGbhxUWpUpeM5fLqFHdnWTrVnvW2DgyOCes=
+gerrit.o-ran-sc.org/r/ric-plt/nodeb-rnib.git/entities v1.0.21 h1:PQ/Mu2ol+8Oh/0BqCWWhPlVVoRCg5dQDEGm4+Opp5w4=
+gerrit.o-ran-sc.org/r/ric-plt/nodeb-rnib.git/entities v1.0.21/go.mod h1:GXiXLz4ORBeIr0FLIbzENRykgh3Po5uPkX2jICxnRF0=
+gerrit.o-ran-sc.org/r/ric-plt/nodeb-rnib.git/reader v1.0.21 h1:N3UbqJ9WqC8JEz/TwHHwZwCFAW6VTlZLpD5lnbdD+Y8=
+gerrit.o-ran-sc.org/r/ric-plt/nodeb-rnib.git/reader v1.0.21/go.mod h1:SQBZLy1HP94i1vQ3y730wGFsrHqZtgPaEkzPgtqBNw0=
+gerrit.o-ran-sc.org/r/ric-plt/sdlgo.git v0.1.1/go.mod h1:2Y8gw2jqj9urI8VFqFQn7BX0J3A852+YrXVV9V8gOt4=
+gerrit.o-ran-sc.org/r/ric-plt/sdlgo.git v0.5.0 h1:+P3XuWKSaMbzh5PNtrW9gkZlCN0hKrZq+Cn8JetwBys=
+gerrit.o-ran-sc.org/r/ric-plt/sdlgo.git v0.5.0/go.mod h1:y2WhrCvdLkAKdH+ySdHSOSehACJkTMyZghCGVcqoZzc=
+gerrit.o-ran-sc.org/r/ric-plt/xapp-frame.git v0.0.19/go.mod h1:c7zYW0EOxX1wQ4p1yigO2t+RjtwYSVdEXxSA+IqFxIk=
+gerrit.o-ran-sc.org/r/ric-plt/xapp-frame.git v0.0.21 h1:Ur+OXnHXlRQG9mp5B05SfS/0ZmuORwC8+SQJKuIjQNw=
+gerrit.o-ran-sc.org/r/ric-plt/xapp-frame.git v0.0.21/go.mod h1:WHzMFLWFYnKZzAT76Lu8wXqcM9MQ9hHM0sxlV45icSw=
+gerrit.oran-osc.org/r/ric-plt/sdlgo.git v0.0.1 h1:l2dl31r++3xhgCumTzwvuo0/F415eqU4aFk/uDQ4WnM=
+gerrit.oran-osc.org/r/ric-plt/sdlgo.git v0.0.1/go.mod h1:LVhkNS82IofJTBK/VYPKiYed9MG/3OFwvWC6MGSDw1w=
+github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/Juniper/go-netconf v0.1.1 h1:5fx/T7L2Fwq51UnESPOP1CXgGCs7IYxR/pnyC5quu/k=
+github.com/Juniper/go-netconf v0.1.1/go.mod h1:2Fy6tQTWnL//D/Ll1hb0RYXN4jndcTyneRn6xj5E1VE=
+github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
+github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+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/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
+github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
+github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
+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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
+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/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+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/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
+github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
+github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
+github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
+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-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
+github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
+github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
+github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
+github.com/go-openapi/analysis v0.19.4/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
+github.com/go-openapi/analysis v0.19.5 h1:8b2ZgKfKIUTVQpTb77MoRDIMEIwvDVw40o3aOXdfYzI=
+github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU=
+github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
+github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
+github.com/go-openapi/errors v0.19.2 h1:a2kIyV3w+OS3S97zxUndRVD46+FhGOUBDFY7nmu4CsY=
+github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
+github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
+github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
+github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
+github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
+github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
+github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
+github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
+github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
+github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
+github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
+github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=
+github.com/go-openapi/loads v0.19.3/go.mod h1:YVfqhUCdahYwR3f3iiwQLhicVRvLlU/WO5WPaZvcvSI=
+github.com/go-openapi/loads v0.19.4 h1:5I4CCSqoWzT+82bBkNIvmLc0UOsoKKQ4Fz+3VxOB7SY=
+github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk=
+github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
+github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
+github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
+github.com/go-openapi/runtime v0.19.7 h1:b2zcE9GCjDVtguugU7+S95vkHjwQEjz/lB+8LOuA9Nw=
+github.com/go-openapi/runtime v0.19.7/go.mod h1:dhGWCTKRXlAfGnQG0ONViOZpjfg0m2gUt9nTQPQZuoo=
+github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
+github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
+github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
+github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
+github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo=
+github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
+github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
+github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
+github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
+github.com/go-openapi/strfmt v0.19.2/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
+github.com/go-openapi/strfmt v0.19.3 h1:eRfyY5SkaNJCAwmmMcADjY31ow9+N7MCLW7oRkbsINA=
+github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
+github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
+github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
+github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
+github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
+github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7vS9k0lo6zwJo=
+github.com/go-openapi/validate v0.19.4 h1:LGjO87VyXY3bIKjlYpXSFuLRG2mTeuYlZyeNwFFWpyM=
+github.com/go-openapi/validate v0.19.4/go.mod h1:BkJ0ZmXui7yB0bJXWSXgLPNTmbLVeX/3D1xn/N9mMUM=
+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-redis/redis v6.15.3+incompatible h1:NZ0O90AhLSvSrvLZ/S9h7D4kl1mW2PrKyxL7MyBKO2g=
+github.com/go-redis/redis v6.15.3+incompatible/go.mod h1:W2YCLaZryXHirdd9QqwkiVUxCQsrx8SbLq9Uqk7JS7A=
+github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
+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/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+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/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+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/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
+github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
+github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+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/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+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/orcaman/concurrent-map v0.0.0-20190314100340-2693aad1ed75 h1:IV56VwUb9Ludyr7s53CMuEh4DdTnnQtEPLEgLyJ0kHI=
+github.com/orcaman/concurrent-map v0.0.0-20190314100340-2693aad1ed75/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI=
+github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
+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/pkg/errors v0.8.1/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/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
+github.com/segmentio/ksuid v1.0.2 h1:9yBfKyw4ECGTdALaF09Snw3sLJmYIX6AbPJrAy6MrDc=
+github.com/segmentio/ksuid v1.0.2/go.mod h1:BXuJDr2byAiHuQaQtSKoXh1J0YmUDurywOXgB2w+OSU=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
+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/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
+github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
+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/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
+github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+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/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
+github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/valyala/fastjson v1.4.1 h1:hrltpHpIpkaxll8QltMU8c3QZ5+qIiCL8yKqPFJI/yE=
+github.com/valyala/fastjson v1.4.1/go.mod h1:nV6MsjxL2IMJQUoHDIrjEI7oLyeqK6aBD7EFWPsvP8o=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v1.1.0 h1:ngVtJC9TY/lg0AA/1k48FYhBrhRoFlEmWzsehpNAaZg=
+github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
+github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+github.com/ziutek/telnet v0.0.0-20180329124119-c3b780dc415b h1:VfPXB/wCGGt590QhD1bOpv2J/AmC/RJNTg/Q59HKSB0=
+github.com/ziutek/telnet v0.0.0-20180329124119-c3b780dc415b/go.mod h1:IZpXDfkJ6tWD3PhBK5YzgQT+xJWh7OsdwiG8hA2MkO4=
+go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
+go.mongodb.org/mongo-driver v1.1.1 h1:Sq1fR+0c58RME5EoqKdjkiQAmPjmfHlZOoRI6fTUOcs=
+go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
+go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+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/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56 h1:ZpKuNIejY8P0ExLOVyKhb0WsgG8UdvHXe6TWjY7eL6k=
+golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+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-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
+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/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+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/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f h1:25KHgbfyiSm6vwQLbM3zZIe1v9p/3ea4Rz+nnM5K/i4=
+golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+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/check.v1 v1.0.0-20180628173108-788fd7840127/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/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
+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.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
+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=
+gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/agent/pkg/nbi/helper.c b/agent/pkg/nbi/helper.c
new file mode 100755 (executable)
index 0000000..9fc912e
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+==================================================================================
+  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.
+==================================================================================
+*/
+
+#include <string.h>
+#include "helper.h"
+#include "_cgo_export.h"
+#include <sysrepo.h>
+#include <libyang/libyang.h>
+
+
+int module_change_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, sr_event_t event, uint32_t req_id, void *priv_ctx) {
+    return nbiModuleChangeCB(session, (char *)module_name, (char *)xpath, event, (int)req_id);
+}
+
+char * yang_data_sr2json(sr_session_ctx_t *session, const char *module_name, sr_event_t event, sr_change_oper_t *operation) {
+    sr_change_iter_t *it = NULL;
+    int rc = SR_ERR_OK;
+    sr_change_oper_t oper;
+    sr_val_t *old_value = NULL;
+    sr_val_t *new_value = NULL;
+    sr_val_t *val = NULL;
+    char *json_str = NULL;
+    struct lyd_node *root = NULL;
+
+    rc = sr_get_changes_iter(session, "//." , &it);
+    if (rc != SR_ERR_OK) {
+        goto cleanup;
+    }
+
+    while ((rc = sr_get_change_next(session, it, &oper, &old_value, &new_value)) == SR_ERR_OK) {
+        *operation = oper;
+        val = oper == SR_OP_CREATED ? new_value : old_value;
+        char *leaf = sr_val_to_str(val);
+        if (root) {
+            lyd_new_path(root, NULL, val->xpath, (void *) leaf, 0, 1);
+        } else {
+            root = lyd_new_path(NULL, sr_get_context(sr_session_get_connection(session)), val->xpath, (void *) leaf, 0, 1);
+        }
+
+        sr_free_val(old_value);
+        sr_free_val(new_value);
+        if (leaf) {
+            free(leaf);
+        }
+    }
+
+    if (root) {
+        lyd_print_mem(&json_str, root, LYD_JSON, LYP_WITHSIBLINGS | LYP_FORMAT);
+        printf("\n%s\n", json_str);
+    }
+
+cleanup:
+    sr_free_change_iter(it);
+    return json_str;
+}
+
+char * get_data_json(sr_session_ctx_t *session, const char *module_name) {
+    struct lyd_node *data;
+    char *json_str = NULL;
+    int rc;
+    char xpath[100];
+
+    sprintf(xpath, "/%s:ric", module_name);
+
+    rc = sr_get_data(session, xpath, 0, 0, 0, &data);
+    if (rc != SR_ERR_OK) {
+        fprintf(stdout, "\nsr_get_data failed: %d\n", rc);
+        return NULL;
+    }
+
+    if (data) {
+        lyd_print_mem(&json_str, data, LYD_JSON, LYP_WITHSIBLINGS | LYP_FORMAT);
+    }
+    return json_str;
+}
+
+sr_val_t *get_val(sr_val_t *val, size_t i) {
+       return &val[i];
+}
\ No newline at end of file
diff --git a/agent/pkg/nbi/helper.h b/agent/pkg/nbi/helper.h
new file mode 100755 (executable)
index 0000000..514fb8a
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+==================================================================================
+  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.
+==================================================================================
+*/
+
+#ifndef HELPER_H
+#define HELPER_H
+
+#include <sysrepo.h>
+
+sr_val_t *get_val(sr_val_t *val, size_t i);
+
+int module_change_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, sr_event_t event, uint32_t request_id, void *private_data);
+
+char * yang_data_sr2json(sr_session_ctx_t *session, const char *module_name, sr_event_t event, sr_change_oper_t *operation);
+
+char * get_data_json(sr_session_ctx_t *session, const char *module_name);
+
+#endif
diff --git a/agent/pkg/nbi/nbi.go b/agent/pkg/nbi/nbi.go
new file mode 100755 (executable)
index 0000000..c148ce9
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+==================================================================================
+  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 nbi
+
+import (       
+       "time"
+       "errors"
+       "fmt"
+       "strings"
+       "unsafe"
+       "encoding/json"
+       "github.com/spf13/viper"
+       "github.com/valyala/fastjson"
+
+       "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/xapp"
+       "gerrit.oran-osc.org/r/ric-plt/o1mediator/pkg/sbi"
+)
+
+/*
+#cgo LDFLAGS: -lsysrepo -lyang
+
+#include <stdio.h>
+#include <limits.h>
+#include <sysrepo.h>
+#include <sysrepo/values.h>
+#include "helper.h"
+*/
+import "C"
+
+var sbiClient sbi.SBIClientInterface
+var nbiClient *Nbi
+var log = xapp.Logger
+
+func NewNbi(s sbi.SBIClientInterface) *Nbi {
+       sbiClient = s
+
+       nbiClient = &Nbi{
+               schemas:  viper.GetStringSlice("nbi.schemas"),
+               cleanupChan: make(chan bool),
+       }
+       return nbiClient
+}
+
+func (n *Nbi) Start() bool {
+       if ok := n.Setup(n.schemas); !ok {
+               log.Error("NBI: SYSREPO initialization failed, bailing out!")
+               return false
+       }
+       log.Info("NBI: SYSREPO initialization done ... processing O1 requests!")
+
+       return true
+}
+
+func (n *Nbi) Stop() { 
+       C.sr_unsubscribe(n.subscription)
+       C.sr_session_stop(n.session)
+       C.sr_disconnect(n.connection)
+
+       log.Info("NBI: SYSREPO cleanup done gracefully!")
+}
+
+func (n *Nbi) Setup(schemas []string) bool {
+       rc := C.sr_connect(0, &n.connection)
+       if C.SR_ERR_OK != rc {
+               log.Error("NBI: sr_connect failed: %s", C.GoString(C.sr_strerror(rc)))
+               return false
+       }
+
+       rc = C.sr_session_start(n.connection, C.SR_DS_RUNNING, &n.session)
+       if C.SR_ERR_OK != rc {
+               log.Error("NBI: sr_session_start failed: %s", C.GoString(C.sr_strerror(rc)))
+               return false
+       }
+
+       for {
+               if ok := n.DoSubscription(schemas); ok == true {
+                       break
+               }
+               time.Sleep(time.Duration(5 * time.Second))
+       }
+       return true
+}
+
+func (n *Nbi) DoSubscription(schemas []string) bool {
+       log.Info("Subscribing YANG modules ... %v", schemas)
+       for _, module := range schemas {
+               modName := C.CString(module)
+               defer C.free(unsafe.Pointer(modName))
+
+               if done := n.SubscribeModule(modName); !done {
+                       return false
+               }
+       }
+       return true
+}
+
+func (n *Nbi) SubscribeModule(module *C.char) bool {
+       rc := C.sr_module_change_subscribe(n.session, module, nil, C.sr_module_change_cb(C.module_change_cb), nil, 0, 0, &n.subscription)
+       if C.SR_ERR_OK != rc {
+               log.Info("NBI: sr_module_change_subscribe failed: %s", C.GoString(C.sr_strerror(rc)))
+               return false
+       }
+       return true
+}
+
+//export nbiModuleChangeCB
+func nbiModuleChangeCB(session *C.sr_session_ctx_t, module *C.char, xpath *C.char, event C.sr_event_t, reqId C.int) C.int {
+       changedModule := C.GoString(module)
+       changedXpath := C.GoString(xpath)
+
+       log.Info("NBI: Module change callback - event='%d' module=%s xpath=%s reqId=%d", event, changedModule, changedXpath, reqId)
+
+       if C.SR_EV_CHANGE == event {
+               configJson := C.yang_data_sr2json(session, module, event, &nbiClient.oper)
+               err := nbiClient.ManageXapps(changedModule, C.GoString(configJson), int(nbiClient.oper))
+               if err != nil {
+                       return C.SR_ERR_OPERATION_FAILED
+               }
+       }
+
+       if C.SR_EV_DONE == event {
+               configJson := C.get_data_json(session, module)
+               err := nbiClient.ManageConfigmaps(changedModule, C.GoString(configJson), int(nbiClient.oper))
+               if err != nil {
+                       return C.SR_ERR_OPERATION_FAILED
+               }
+       }
+
+       return C.SR_ERR_OK
+}
+
+func (n *Nbi) ManageXapps(module, configJson string, oper int) error {
+       log.Info("ManageXapps: module=%s configJson=%s", module, configJson)
+
+       if configJson == "" || module != "o-ran-sc-ric-xapp-desc-v1" {
+               return nil
+       }
+
+       root := fmt.Sprintf("%s:ric", module)
+       jsonList, err := n.ParseJsonArray(configJson, root, "xapps", "xapp")
+       if err != nil {
+               return err
+       }
+
+       for _, m := range jsonList {
+               xappName := string(m.GetStringBytes("name"))
+               namespace := string(m.GetStringBytes("namespace"))
+               relName := string(m.GetStringBytes("release-name"))
+               version := string(m.GetStringBytes("version"))
+
+               desc := sbiClient.BuildXappDescriptor(xappName, namespace, relName, version)
+               switch oper {
+                       case C.SR_OP_CREATED:
+                               return sbiClient.DeployXapp(desc)
+                       case C.SR_OP_DELETED:
+                               return sbiClient.UndeployXapp(desc)
+                       default:
+                               return errors.New(fmt.Sprintf("Operation '%d' not supported!", oper))
+               }
+       }
+       return nil
+}
+
+func (n *Nbi) ManageConfigmaps(module, configJson string, oper int) error {
+       log.Info("ManageConfig: module=%s configJson=%s", module, configJson)
+       
+       if configJson == "" || module != "o-ran-sc-ric-ueec-config-v1" {
+               return nil
+       }
+
+       if oper != C.SR_OP_MODIFIED {
+               return errors.New(fmt.Sprintf("Operation '%d' not supported!", oper))
+       }
+
+       value, err := n.ParseJson(configJson)
+       if err != nil {
+               return err
+       }
+
+       root := fmt.Sprintf("%s:ric", module)
+       appName := string(value.GetStringBytes(root, "config", "name"))
+       namespace := string(value.GetStringBytes(root, "config", "namespace"))
+       control := value.Get(root, "config", "control").String()
+
+       var f interface{}
+       err = json.Unmarshal([]byte(strings.ReplaceAll(control, "\\", "")), &f)
+       if err != nil {
+               log.Info("json.Unmarshal failed: %v", err)
+               return err
+       }
+
+       xappConfig := sbiClient.BuildXappConfig(appName, namespace, f)
+       return sbiClient.ModifyXappConfig(xappConfig)
+}
+
+func (n *Nbi) ParseJson(dsContent string) (*fastjson.Value, error) {
+       var p fastjson.Parser
+       v, err := p.Parse(dsContent)
+       if err != nil {
+               log.Info("fastjson.Parser failed: %v", err)
+       }
+       return v, err
+}
+
+func (n *Nbi) ParseJsonArray(dsContent, model, top, elem string) ([]*fastjson.Value, error) {
+       v, err := n.ParseJson(dsContent)
+       if err != nil {
+               return nil, err
+       }
+       return v.GetArray(model, top, elem), nil
+}
diff --git a/agent/pkg/nbi/nbi_test.go b/agent/pkg/nbi/nbi_test.go
new file mode 100755 (executable)
index 0000000..8a1acd2
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+==================================================================================
+  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 nbi
+
+import (
+       "os"    
+       "time"
+       "encoding/json"
+       "testing"
+       "net"
+    "net/http"
+    "net/http/httptest"
+       "github.com/stretchr/testify/assert"
+
+       apimodel "gerrit.oran-osc.org/r/ric-plt/o1mediator/pkg/appmgrmodel"
+       "gerrit.oran-osc.org/r/ric-plt/o1mediator/pkg/sbi"
+       
+)
+
+var XappConfig = `{
+       "o-ran-sc-ric-ueec-config-v1:ric": {
+         "config": {
+               "name": "ueec",
+               "namespace": "ricxapp",
+               "control": {
+                 "active": true,
+                 "interfaceId": {
+                       "globalENBId": {
+                         "plmnId": "1234",
+                         "eNBId": "55"
+                       }
+                 }
+               }
+         }
+       }
+  }`
+
+var XappDescriptor = `{
+       "o-ran-sc-ric-xapp-desc-v1:ric": {
+         "xapps": {
+               "xapp": [
+                 {
+                       "name": "ueec",
+                       "release-name": "ueec-xapp",
+                       "version": "0.0.1",
+                       "namespace": "ricxapp"
+                 }
+               ]
+         }
+       }
+  }`
+
+var n *Nbi
+
+// Test cases
+func TestMain(M *testing.M) {
+       n = NewNbi(sbi.NewSBIClient("localhost:8080", "/ric/v1/", []string{"http"}, 5))
+       go n.Start()
+       time.Sleep(time.Duration(1) * time.Second)
+
+       os.Exit(M.Run())
+}
+
+func TestModifyConfigmap(t *testing.T) {
+       ts := CreateHTTPServer(t, "PUT", "/ric/v1/config", http.StatusOK, apimodel.ConfigValidationErrors{})
+       defer ts.Close()
+
+       var f interface{}
+       err := json.Unmarshal([]byte(XappConfig), &f)
+       assert.Equal(t, true, err == nil)
+
+       err = n.ManageConfigmaps("o-ran-sc-ric-ueec-config-v1", XappConfig, 1)
+       assert.Equal(t, true, err == nil)
+}
+
+func TestDeployXApp(t *testing.T) {
+       ts := CreateHTTPServer(t, "POST", "/ric/v1/xapps", http.StatusCreated, apimodel.Xapp{})
+       defer ts.Close()
+
+       var f interface{}
+       err := json.Unmarshal([]byte(XappDescriptor), &f)
+       assert.Equal(t, true, err == nil)
+
+       err = n.ManageXapps("o-ran-sc-ric-xapp-desc-v1", XappDescriptor, 0)
+       assert.Equal(t, true, err == nil)
+}
+
+func TestUnDeployXApp(t *testing.T) {
+       ts := CreateHTTPServer(t, "DELETE", "/ric/v1/xapps/ueec-xapp", http.StatusNoContent, apimodel.Xapp{})
+       defer ts.Close()
+
+       var f interface{}
+       err := json.Unmarshal([]byte(XappDescriptor), &f)
+       assert.Equal(t, true, err == nil)
+
+       err = n.ManageXapps("o-ran-sc-ric-xapp-desc-v1", XappDescriptor, 2)
+       assert.Equal(t, true, err == nil)
+}
+
+func TestGetDeployedXapps(t *testing.T) {
+       ts := CreateHTTPServer(t, "GET", "/ric/v1/xapps", http.StatusOK, apimodel.AllDeployedXapps{})
+       defer ts.Close()
+
+       err := sbiClient.GetDeployedXapps()
+       assert.Equal(t, true, err == nil)
+}
+
+func TestErrorCases(t *testing.T) {
+       // Invalid config
+       err := n.ManageXapps("o-ran-sc-ric-xapp-desc-v1", "", 2)
+       assert.Equal(t, true, err == nil)
+
+       // Invalid module
+       err = n.ManageXapps("", "{}", 2)
+       assert.Equal(t, true, err == nil)
+
+       // Unexpected module
+       err = n.ManageXapps("o-ran-sc-ric-ueec-config-v1", "{}", 2)
+       assert.Equal(t, true, err == nil)
+
+       // Invalid operation
+       err = n.ManageXapps("o-ran-sc-ric-ueec-config-v1", XappDescriptor, 1)
+       assert.Equal(t, true, err == nil)
+
+       // Invalid config
+       err = n.ManageConfigmaps("o-ran-sc-ric-ueec-config-v1", "", 1)
+       assert.Equal(t, true, err == nil)
+
+       // Invalid module
+       err = n.ManageConfigmaps("", "{}", 1)
+       assert.Equal(t, true, err == nil)
+
+       // Unexpected module
+       err = n.ManageConfigmaps("o-ran-sc-ric-xapp-desc-v1", "{}", 0)
+       assert.Equal(t, true, err == nil)
+
+       // Invalid operation
+       err = n.ManageConfigmaps("o-ran-sc-ric-ueec-config-v1", "{}", 0)
+       assert.Equal(t, true, err != nil)
+}
+
+func TestTeardown(t *testing.T) {
+       n.Stop()
+}
+
+func CreateHTTPServer(t *testing.T, method, url string, status int, respData interface{}) *httptest.Server {
+       l, err := net.Listen("tcp", "localhost:8080")
+       if err != nil {
+                       t.Error("Failed to create listener: " + err.Error())
+       }
+       ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+               assert.Equal(t, r.Method, method)
+               assert.Equal(t, r.URL.String(), url)
+               w.Header().Add("Content-Type", "application/json")
+               w.WriteHeader(status)
+               b, _ := json.Marshal(respData)
+               w.Write(b)
+       }))
+       ts.Listener.Close()
+       ts.Listener = l
+
+       ts.Start()
+
+       return ts
+}
+
+func DescMatcher(result, expected *apimodel.XappDescriptor) bool {
+       if *result.XappName == *expected.XappName && result.HelmVersion == expected.HelmVersion &&
+               result.Namespace == expected.Namespace && result.ReleaseName == expected.ReleaseName {
+               return true
+       }
+       return false
+}
+
+func ConfigMatcher(result, expected *apimodel.XAppConfig) bool {
+       if *result.Metadata.XappName == *expected.Metadata.XappName &&
+          *result.Metadata.Namespace == *expected.Metadata.Namespace {
+               return true
+       }
+       return false
+}
diff --git a/agent/pkg/nbi/types.go b/agent/pkg/nbi/types.go
new file mode 100755 (executable)
index 0000000..7525436
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+==================================================================================
+  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 nbi
+
+/*
+#cgo LDFLAGS: -lsysrepo
+
+#include <sysrepo.h>
+#include <sysrepo/values.h>
+*/
+import "C"
+
+type Nbi struct {
+       schemas      []string
+       connection   *C.sr_conn_ctx_t
+       session      *C.sr_session_ctx_t
+  subscription *C.sr_subscription_ctx_t
+  oper          C.sr_change_oper_t
+       cleanupChan  chan bool
+}
diff --git a/agent/pkg/sbi/sbi.go b/agent/pkg/sbi/sbi.go
new file mode 100755 (executable)
index 0000000..4306601
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+==================================================================================
+  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 sbi
+
+import (
+       "time"
+       httptransport "github.com/go-openapi/runtime/client"
+       "github.com/go-openapi/strfmt"
+       
+       "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/xapp"
+       apiclient "gerrit.oran-osc.org/r/ric-plt/o1mediator/pkg/appmgrclient"
+       apixapp "gerrit.oran-osc.org/r/ric-plt/o1mediator/pkg/appmgrclient/xapp"
+       apimodel "gerrit.oran-osc.org/r/ric-plt/o1mediator/pkg/appmgrmodel"
+)
+
+var log = xapp.Logger
+
+func NewSBIClient(host, baseUrl string, prot []string, timo int) *SBIClient {
+       return &SBIClient{host, baseUrl, prot, time.Duration(timo) * time.Second}
+}
+
+func (s *SBIClient) CreateTransport() *apiclient.RICAppmgr {
+       return apiclient.New(httptransport.New(s.host, s.baseUrl, s.prot), strfmt.Default)
+}
+
+func (s *SBIClient) BuildXappDescriptor(name, namespace, release, version string) *apimodel.XappDescriptor {
+       return &apimodel.XappDescriptor{
+               XappName:    &name,
+               HelmVersion: version,
+               ReleaseName: release,
+               Namespace:   namespace,
+       }
+}
+
+func (s *SBIClient) DeployXapp(xappDesc *apimodel.XappDescriptor) error {
+       params := apixapp.NewDeployXappParamsWithTimeout(s.timeout).WithXappDescriptor(xappDesc)
+       log.Info("SBI: DeployXapp=%v", params)
+
+       result, err := s.CreateTransport().Xapp.DeployXapp(params)
+       if err != nil {
+               log.Error("SBI: DeployXapp unsuccessful: %v", err)
+       } else {
+               log.Info("SBI: DeployXapp successful: payload=%v", result.Payload)
+       }
+       return err
+}
+
+func (s *SBIClient) UndeployXapp(xappDesc *apimodel.XappDescriptor) error {
+       name := *xappDesc.XappName
+       if xappDesc.ReleaseName != "" {
+               name = xappDesc.ReleaseName
+       }
+
+       params := apixapp.NewUndeployXappParamsWithTimeout(s.timeout).WithXAppName(name)
+       log.Info("SBI: UndeployXapp=%v", params)
+
+       result, err := s.CreateTransport().Xapp.UndeployXapp(params)
+       if err != nil {
+               log.Error("SBI: UndeployXapp unsuccessful: %v", err)
+       } else {
+               log.Info("SBI: UndeployXapp successful: payload=%v", result)
+       }
+       return err
+}
+
+func (s *SBIClient) GetDeployedXapps() error {
+       params := apixapp.NewGetAllXappsParamsWithTimeout(s.timeout)
+       result, err := s.CreateTransport().Xapp.GetAllXapps(params)
+       if err != nil {
+               log.Error("GET unsuccessful: %v", err)
+       } else {
+               log.Info("GET successful: payload=%v", result.Payload)
+       }
+       return err
+}
+
+func (s *SBIClient) BuildXappConfig(name, namespace string, configData interface{}) *apimodel.XAppConfig {
+       metadata := &apimodel.ConfigMetadata{
+               XappName:    &name,
+               Namespace: &namespace,
+       }
+
+       return &apimodel.XAppConfig{
+               Metadata: metadata,
+               Config: configData,
+       }
+}
+
+func (s *SBIClient) ModifyXappConfig(xappConfig *apimodel.XAppConfig) error {
+       params := apixapp.NewModifyXappConfigParamsWithTimeout(s.timeout).WithXAppConfig(xappConfig)
+       result, err := s.CreateTransport().Xapp.ModifyXappConfig(params)
+       if err != nil {
+               log.Error("SBI: ModifyXappConfig unsuccessful: %v", err)
+       } else {
+               log.Info("SBI: ModifyXappConfig successful: payload=%v", result.Payload)
+       }
+       return err
+}
diff --git a/agent/pkg/sbi/types.go b/agent/pkg/sbi/types.go
new file mode 100755 (executable)
index 0000000..c15b725
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+==================================================================================
+  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 sbi
+
+import (
+       "time"
+
+       apimodel "gerrit.oran-osc.org/r/ric-plt/o1mediator/pkg/appmgrmodel"
+)
+
+type SBIClient struct {
+       host    string
+       baseUrl string
+       prot    []string
+       timeout time.Duration
+}
+
+type SBIClientInterface interface {
+       BuildXappDescriptor(name, version, release, namespace string) *apimodel.XappDescriptor
+       DeployXapp(xappDesc *apimodel.XappDescriptor) error
+       UndeployXapp(xappDesc *apimodel.XappDescriptor) error
+       GetDeployedXapps() error
+
+       BuildXappConfig(name, namespace string, configData interface{}) *apimodel.XAppConfig
+       ModifyXappConfig(xappConfig *apimodel.XAppConfig) error
+}
diff --git a/agent/test/o1-create-configmap.xml b/agent/test/o1-create-configmap.xml
new file mode 100755 (executable)
index 0000000..369eb9c
--- /dev/null
@@ -0,0 +1,15 @@
+<ric xmlns="urn:o-ran:ric:ueec-config:1.0">
+    <config xmlns="urn:o-ran:ric:ueec-config:1.0" xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0" xc:operation="replace">
+            <name>ueec</name>
+            <namespace>ricxapp</namespace>
+            <control>
+                <active>true</active>
+                <interfaceId>
+                    <globalENBId>
+                        <plmnId>1214</plmnId>
+                        <eNBId>66</eNBId>
+                    </globalENBId>
+                </interfaceId>
+            </control>
+    </config>
+</ric>
diff --git a/agent/test/o1-create-xapp.xml b/agent/test/o1-create-xapp.xml
new file mode 100755 (executable)
index 0000000..600f5fd
--- /dev/null
@@ -0,0 +1,10 @@
+<ric xmlns="urn:o-ran:ric:xapp-desc:1.0">
+    <xapps xmlns="urn:o-ran:ric:xapp-desc:1.0">
+        <xapp xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0" xc:operation="create">
+            <name>ueec</name>
+            <release-name>ueec-xapp</release-name>
+            <version>0.0.1</version>
+            <namespace>ricxapp</namespace>
+        </xapp>
+    </xapps>    
+</ric>
diff --git a/agent/yang/o-ran-sc-ric-ueec-config-v1.yang b/agent/yang/o-ran-sc-ric-ueec-config-v1.yang
new file mode 100755 (executable)
index 0000000..74b8d8d
--- /dev/null
@@ -0,0 +1,63 @@
+module o-ran-sc-ric-ueec-config-v1 {
+    yang-version 1.1;
+    namespace "urn:o-ran:ric:ueec-config:1.0";
+    prefix rxad;
+
+    organization
+        "O-RAN Software Community";
+    contact
+        "www.o-ran.org";
+    description
+        "This module defines configuration parameters of UEEC xApp
+
+        Copyright 2020 the O-RAN Alliance.
+
+        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.";
+
+    revision 2020-01-29 {
+        description
+            "initial revision";
+        reference
+            "O-RAN-OAM-Interface-Specification (O1)";
+    }
+    
+    grouping subscription {
+        leaf active {
+            type boolean;
+        }
+        container interfaceId {
+            container globalENBId {
+                leaf plmnId {
+                    type string;
+                }
+                leaf eNBId {
+                    type uint64;
+                }
+            }
+        }
+    }
+
+    container ric {
+        container config {
+            leaf name {
+                type string;
+            }
+            leaf namespace {
+                type string;
+            }
+            container control {
+                uses subscription;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/agent/yang/o-ran-sc-ric-xapp-desc-v1.yang b/agent/yang/o-ran-sc-ric-xapp-desc-v1.yang
new file mode 100755 (executable)
index 0000000..1217de3
--- /dev/null
@@ -0,0 +1,73 @@
+module o-ran-sc-ric-xapp-desc-v1 {
+    yang-version 1.1;
+    namespace "urn:o-ran:ric:xapp-desc:1.0";
+    prefix rxad;
+
+    organization
+        "O-RAN Software Community";
+    contact
+        "www.o-ran.org";
+    description
+        "This module defines a generic xApp descriptor used for xApp lifecycle management
+
+        Copyright 2020 the O-RAN Alliance.
+
+        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.";
+
+    revision 2020-01-29 {
+        description
+            "initial revision";
+        reference
+            "O-RAN-OAM-Interface-Specification (O1)";
+    }
+
+    // LCM: Generic xApp descriptor passed to xApp Manager (or OCO) during xApp deployment/undeployment
+    grouping xapp-descriptor {
+        leaf name {
+            mandatory true;
+            type string;
+            description
+                "Name of the xApp in helm chart";
+        }
+        leaf release-name {
+            type string;
+            description
+                "Name of the xapp to be visible in Kubernetes";
+        }
+        leaf version {
+            type string;
+            description
+                "The exact xapp helm chart version to install";
+        }
+        leaf namespace {
+            type string;
+            description
+                "Name of the namespace to which xApp is deployed in Kubernetes";
+        }
+        leaf override-file {
+            type string;
+            description
+                "JSON string of override file for 'helm install' command";
+        }
+    }
+    
+    // Top-level (root) manager object
+    container ric {
+        container xapps {
+            list xapp {
+                key "name";
+                uses xapp-descriptor;
+            }
+        }        
+    }
+}
\ No newline at end of file
diff --git a/config/supervisord.conf b/config/supervisord.conf
new file mode 100755 (executable)
index 0000000..0a4517b
--- /dev/null
@@ -0,0 +1,36 @@
+[supervisord]
+nodaemon=true
+logfile=/var/log/supervisor/supervisord.log
+pidfile=/var/run/supervisord.pid
+childlogdir=/var/log/supervisor
+loglevel=debug
+
+[supervisorctl]
+serverurl = unix:///tmp/supervisor.sock
+prompt = mysupervisor
+
+[inet_http_server]
+port = *:9001
+
+[eventlistener:process-state]
+command=python /usr/local/bin/process-state.py
+events=PROCESS_STATE
+autorestart=true
+startretries=3
+buffer_size=1024
+priority=1
+
+[program:netopeer2-server]
+command=/usr/local/bin/netopeer2-server -d
+autorestart=true
+startretries=10
+redirect_stderr=true
+priority=4
+
+[program:o1agent]
+environment=RMR_SEED_RT=/etc/o1agent/uta_rtg.rt
+command=/usr/local/bin/o1agent -f /etc/o1agent/config-file.json
+autorestart=true
+startretries=10
+redirect_stderr=true
+priority=5
diff --git a/container-tag.yaml b/container-tag.yaml
new file mode 100644 (file)
index 0000000..b9299ce
--- /dev/null
@@ -0,0 +1,5 @@
+# The Jenkins job requires a tag to build the Docker image.
+# By default this file is in the docker build directory,
+# but the location can configured in the JJB template.
+---
+tag: 0.3.1
diff --git a/manager/src/process-state.py b/manager/src/process-state.py
new file mode 100755 (executable)
index 0000000..0bf3038
--- /dev/null
@@ -0,0 +1,294 @@
+#!/usr/bin/env python
+#   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.
+#
+
+#
+#   This script does have the event handler in supervisor to follow the process state. 
+#   Main parent process follows the events from the supervised daemon and the child process
+#   provides the process status info via HTTP server interface.
+#   When any of the process state change to PROCESS_STATE_FATAL, then the http server will 
+#   respond with status code 500 to indicate faulty operation, normal status code is 200 (working).
+#   Set the script configuration to supervisord.conf as follow:
+#
+#   [eventlistener:process-state]
+#   command=python process-state.py
+#   autorestart=true
+#   startretries=3
+#   events=PROCESS_STATE
+#
+#   Following is the example supervisor daemon status for each process.
+#
+#   ver:3.0 server:supervisor serial:16 pool:process-state poolserial:16 eventname:PROCESS_STATE_FATAL len:62
+#   processname:sleep-exit groupname:sleep-exit from_state:BACKOFF
+#
+#   Process states are: PROCESS_STATE_STARTING, PROCESS_STATE_RUNNING, PROCESS_STATE_STOPPING, 
+#   PROCESS_STATE_STOPPEDPROCESS_STATE_BACKOFF, PROCESS_STATE_FATAL
+#
+
+import os
+import sys
+import signal
+import datetime
+import argparse
+import psutil
+import threading
+import BaseHTTPServer
+
+# needed for python socket library broken pipe WA
+from multiprocessing import Process, Manager, Value, Lock
+
+global HTTP_STATUS
+global PROCESS_STATE
+global DEBUG
+
+TABLE_STYLE = ('<style>'
+    'div { font-family: Arial, Helvetica, sans-serif; font-size:8px;}'
+    'p { font-family: Arial, Helvetica, sans-serif;}'
+    'h1 { font-family: Arial, Helvetica, sans-serif; font-size:30px; font-weight: bold; color:#A63434;}'
+    'table, th, td { border: 1px solid black; border-collapse: collapse; font-size:12px;}'
+    'th, td { padding: 3px 10px 3px 10px; text-align: left;}'
+    'th.thr, td.tdr { padding: 3px 10px 3px 10px; text-align: right;}'
+    'th.thc, td.tdc { padding: 3px 10px 3px 10px; text-align: center;}'
+    'table#t1 tr:nth-child(even) { background-color: #eee;}'
+    'table#t1 tr:nth-child(odd) { background-color: #ADC0DC;}'
+    'table#t1 th { background-color: #214D8B; color: white;}</style>')
+
+def get_pid_info(pid):
+    pdata = None
+    if pid is not 0:
+        try:
+            process = psutil.Process(pid)
+            # these are the item lists
+            files = process.open_files()
+            # get the open files and connections and count number of fd str
+            sockets = process.connections()
+            descriptors = str(files)+str(sockets)
+            count = descriptors.count("fd=")
+            pdata = {"pid": process.pid,
+                    "status": process.status(),
+                    "cpu": process.cpu_percent(interval=0.2),
+                    "descriptors": count,
+                    "memory": process.memory_info().rss}
+        except (psutil.ZombieProcess, psutil.AccessDenied, psutil.NoSuchProcess):
+            pdata = None
+    return pdata
+
+def get_process_html_info():
+    global PROCESS_STATE
+    
+    try:
+        html_data = ("<table width='800px' id='t1'>"
+                    "<thead><tr><th>Process</th><th>Date and time</th><th>From state</th><th>to state</th>"
+                    "<th class=thc>Pid</th><th class=thc>Fds</th><th class=thc>Mem</th><th class=thc>Cpu</th></tr></thead><tbody>")
+        for proc,data in PROCESS_STATE.items():
+            pid = 0
+            descriptors = ""
+            memory_usage = ""
+            cpu_usage = ""
+            if data['pid'] is not None:
+                pdata = get_pid_info(data['pid'])
+                if pdata is not None:
+                    pid = pdata['pid']
+                    descriptors = str(pdata['descriptors'])
+                    memory_usage = str(pdata['memory']/1024)+" Kb"
+                    cpu_usage = str(pdata['cpu'])+" %"
+            html_data += ('<tr>'
+                            '<td>'+str(proc)+'</td>'
+                            '<td>'+str(data['time'])+'</td>'
+                            '<td>'+str(data['from_state'])+'</td>'
+                            '<td>'+str(data['state'])+'</td>'
+                            '<td class=tdr>'+str(pid)+'</td>'
+                            '<td class=tdr>'+descriptors+'</td>'
+                            '<td class=tdr>'+memory_usage+'</td>'
+                            '<td class=tdr>'+cpu_usage+'</td>'
+                          '</tr>')
+    finally:
+        html_data += ("</tbody></table>")
+
+    return html_data
+
+# responds to http request according to the process status
+class myHTTPHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+    global HTTP_STATUS
+    global REFRESH_TIME    
+    global PROCESS_STATE
+
+    # write HEAD and GET to client and then close
+    def do_HEAD(s):
+        s.send_response(HTTP_STATUS['code'])
+        s.send_header("Server-name", "supervisor-process-stalker 1.0")
+        s.send_header("Content-type", "text/html")
+        s.end_headers()
+        s.wfile.close()
+    def do_GET(s):
+        try:
+            """Respond to a GET request."""
+            s.send_response(HTTP_STATUS['code'])
+            s.send_header("Server-name", "supervisor-process-stalker 1.0")
+            s.send_header("Content-type", "text/html")
+            s.end_headers()
+            html_data = ("<html><head><title>supervisor process event handler</title>"+TABLE_STYLE+
+                        "<meta http-equiv='refresh' content='"+str(REFRESH_TIME)+"'/></head>"
+                        "<body><h1>Supervisor Process Event Handler</h1>"
+                        "<div><table width='800px' id='t1'><tr><td>Status code: "+str(HTTP_STATUS['code'])+"</td></tr></table></div>"
+                        "<p> </p>")
+            s.wfile.write(html_data)
+            html = get_process_html_info()
+            s.wfile.write(html)
+            s.wfile.write("</body></html>")
+            s.wfile.close()
+        except (IOError):
+            pass
+            
+    # make processing silent - otherwise will mess up the event handler
+    def log_message(self, format, *args):
+        return        
+        
+def HTTPServerProcess(address, port, http_status, process_state):
+    global HTTP_STATUS
+    global PROCESS_STATE
+    
+    # copy the process status global variable
+    PROCESS_STATE = process_state
+    HTTP_STATUS = http_status
+    
+    server = BaseHTTPServer.HTTPServer
+    try:
+        # redirect stdout to stderr so that the HTTP server won't kill the supervised STDIN/STDOUT interface
+        sys.stdout = sys.stderr
+        # Create a web server and define the handler to manage the
+        # incoming request
+        server = server((address, port), myHTTPHandler)
+        # Wait forever for incoming http requests
+        server.serve_forever()
+
+    except KeyboardInterrupt:
+        write_stderr('^C received, shutting down the web server')
+        server.socket.close()
+
+def dict_print(d):
+    for proc,data in d.items():
+        write_stderr(str(proc))
+        for key,val in data.items():
+            write_stderr(str(key)+' is '+str(val))
+
+# this is the default logging, only for supervised communication
+def write_stdout(s):
+    # only eventlistener protocol messages may be sent to stdout
+    sys.stdout.write(s)
+    sys.stdout.flush()
+
+def write_stderr(s):
+    global DEBUG
+    # this can be used for debug logging - stdout not allowed
+    sys.stderr.write(s)
+    sys.stderr.flush()
+
+def main():
+    global REFRESH_TIME
+    global DEBUG
+
+    manager = Manager()
+    # stores the process status info
+    PROCESS_STATE = manager.dict()
+    #HTTP_STATUS_CODE = Value('d', True)
+    HTTP_STATUS = manager.dict()
+    HTTP_STATUS['code'] = 200
+    
+    write_stderr("HTTP STATUS SET TO "+str(HTTP_STATUS['code']))
+   
+    # default http meta key refresh time in seconds
+    REFRESH_TIME = 3
+
+    # init the default values
+    ADDRESS = "0.0.0.0"     # bind to all interfaces
+    PORT = 3000             # web server listen port
+    DEBUG = False           # no logging
+    
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--port', dest='port', help='HTTP server listen port, default 3000', required=False, type=int)
+    parser.add_argument('--debug', dest='debug', help='sets the debug mode for logging', required=False, action='store_true')
+    parser.add_argument('--address', dest='address', help='IP listen address (e.g. 172.16.0.3), default all interfaces', required=False, type=str)
+    parser.add_argument('--refresh', dest='refresh', help='HTTP auto refresh time in second default is 3 seconds', required=False, type=int)
+    args = parser.parse_args()
+
+    if args.port is not None:
+        PORT = args.port
+    if args.address is not None:
+        ADDRESS = args.address
+    if args.debug is not False:
+        DEBUG = True;
+
+    # Start the http server, bind to address
+    httpServer = Process(target=HTTPServerProcess, args=(ADDRESS, PORT, HTTP_STATUS, PROCESS_STATE))
+    httpServer.start()
+
+    # set the signal handler this phase
+    signal.signal(signal.SIGQUIT, doExit)
+    signal.signal(signal.SIGTERM, doExit)
+    signal.signal(signal.SIGINT, doExit)
+    signal.signal(signal.SIGCLD, doExit)
+    
+    while httpServer.is_alive():
+        # transition from ACKNOWLEDGED to READY
+
+        write_stdout('READY\n')
+        # read header line and print it to stderr
+        line = sys.stdin.readline()
+        write_stderr(line)
+
+        # read event payload and print it to stderr
+        headers = dict([ x.split(':') for x in line.split() ])
+        process_state = headers['eventname']
+        
+        if process_state == 'PROCESS_STATE_FATAL':
+            write_stderr('Status changed to FATAL')
+            HTTP_STATUS['code'] = 500
+        
+        short_state = process_state.replace('PROCESS_STATE_', '')
+        length = int(headers['len'])
+        data = sys.stdin.read(length)
+        write_stderr(data)
+        
+        process = dict([ x.split(':') for x in data.split() ])
+        pid = 0
+        if 'pid' in process:
+            pid = int(process['pid'])
+        now = datetime.datetime.now()
+        timestamp=str(now.strftime("%Y/%m/%d %H:%M:%S"))
+        PROCESS_STATE[process['processname']] = {'time': timestamp, 'state': short_state, 'from_state': process['from_state'],
+                                                 'pid':pid}
+        # transition from READY to ACKNOWLEDGED
+        write_stdout('RESULT 2\nOK')
+    httpServer.join()
+
+def kill_child_processes():
+    procs = psutil.Process().children()
+    # send SIGTERM
+    for p in procs:
+        try:
+            p.terminate()
+        except psutil.NoSuchProcess:
+            pass
+
+def doExit(signalNumber, frame):
+    write_stderr("Got signal: "+str(signalNumber)+" need to exit ...")
+    kill_child_processes()
+    exit(0)
+
+if __name__ == '__main__':
+    main()
+