From: Mohamed Abukar Date: Fri, 7 Feb 2020 13:58:55 +0000 (+0200) Subject: Initial commit X-Git-Tag: v0.4.4~8 X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=commitdiff_plain;h=f0347270cb8bb359cf43af09d2a6ec89ee75630b;p=ric-plt%2Fo1.git Initial commit Contributed: Erkki Hietala Change-Id: I40f528311fa0444ff507613c6f6c1bb9d93a7598 Signed-off-by: Mohamed Abukar --- diff --git a/Dockerfile b/Dockerfile new file mode 100755 index 0000000..1fa513b --- /dev/null +++ b/Dockerfile @@ -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 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 index 0000000..e69de29 diff --git a/agent/Makefile b/agent/Makefile new file mode 100755 index 0000000..02aca3e --- /dev/null +++ b/agent/Makefile @@ -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 index 0000000..eb5062e --- /dev/null +++ b/agent/cli/o1-cli.go @@ -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 = "<%s/><%s/>" + editConfigXml = "<%s/>%s" +) + +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 index 0000000..76d19b6 --- /dev/null +++ b/agent/cmd/o1agent.go @@ -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 index 0000000..7bada6d --- /dev/null +++ b/agent/config/config-file.json @@ -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 index 0000000..33f13e4 --- /dev/null +++ b/agent/config/uta_rtg.rt @@ -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 index 0000000..f322edb --- /dev/null +++ b/agent/go.mod @@ -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 index 0000000..7daaca4 --- /dev/null +++ b/agent/go.sum @@ -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 index 0000000..9fc912e --- /dev/null +++ b/agent/pkg/nbi/helper.c @@ -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 +#include "helper.h" +#include "_cgo_export.h" +#include +#include + + +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 index 0000000..514fb8a --- /dev/null +++ b/agent/pkg/nbi/helper.h @@ -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 + +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 index 0000000..c148ce9 --- /dev/null +++ b/agent/pkg/nbi/nbi.go @@ -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 +#include +#include +#include +#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 index 0000000..8a1acd2 --- /dev/null +++ b/agent/pkg/nbi/nbi_test.go @@ -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 index 0000000..7525436 --- /dev/null +++ b/agent/pkg/nbi/types.go @@ -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 +#include +*/ +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 index 0000000..4306601 --- /dev/null +++ b/agent/pkg/sbi/sbi.go @@ -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 index 0000000..c15b725 --- /dev/null +++ b/agent/pkg/sbi/types.go @@ -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 index 0000000..369eb9c --- /dev/null +++ b/agent/test/o1-create-configmap.xml @@ -0,0 +1,15 @@ + + + ueec + ricxapp + + true + + + 1214 + 66 + + + + + diff --git a/agent/test/o1-create-xapp.xml b/agent/test/o1-create-xapp.xml new file mode 100755 index 0000000..600f5fd --- /dev/null +++ b/agent/test/o1-create-xapp.xml @@ -0,0 +1,10 @@ + + + + ueec + ueec-xapp + 0.0.1 + ricxapp + + + 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 index 0000000..74b8d8d --- /dev/null +++ b/agent/yang/o-ran-sc-ric-ueec-config-v1.yang @@ -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 index 0000000..1217de3 --- /dev/null +++ b/agent/yang/o-ran-sc-ric-xapp-desc-v1.yang @@ -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 index 0000000..0a4517b --- /dev/null +++ b/config/supervisord.conf @@ -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 index 0000000..b9299ce --- /dev/null +++ b/container-tag.yaml @@ -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 index 0000000..0bf3038 --- /dev/null +++ b/manager/src/process-state.py @@ -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 = ('') + +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 = ("" + "" + "") + 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 += ('' + '' + '' + '' + '' + '' + '' + '' + '' + '') + finally: + html_data += ("
ProcessDate and timeFrom stateto statePidFdsMemCpu
'+str(proc)+''+str(data['time'])+''+str(data['from_state'])+''+str(data['state'])+''+str(pid)+''+descriptors+''+memory_usage+''+cpu_usage+'
") + + 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 = ("supervisor process event handler"+TABLE_STYLE+ + "" + "

Supervisor Process Event Handler

" + "
Status code: "+str(HTTP_STATUS['code'])+"
" + "

") + s.wfile.write(html_data) + html = get_process_html_info() + s.wfile.write(html) + s.wfile.write("") + 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() +