--- /dev/null
+# 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"]
--- /dev/null
+==================================================================================
+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.
+==================================================================================
+
--- /dev/null
+
+# 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:=
--- /dev/null
+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()))
+}
--- /dev/null
+/*
+==================================================================================
+ 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()
+}
--- /dev/null
+{
+ "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
+ }
+}
--- /dev/null
+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
+
--- /dev/null
+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
+)
--- /dev/null
+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=
--- /dev/null
+/*
+==================================================================================
+ 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
--- /dev/null
+/*
+==================================================================================
+ 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
--- /dev/null
+/*
+==================================================================================
+ 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
+}
--- /dev/null
+/*
+==================================================================================
+ 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
+}
--- /dev/null
+/*
+==================================================================================
+ 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
+}
--- /dev/null
+/*
+==================================================================================
+ 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
+}
--- /dev/null
+/*
+==================================================================================
+ 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
+}
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+[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
--- /dev/null
+# 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
--- /dev/null
+#!/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()
+