ADD /config/keystore.jks /opt/app/policy-agent/etc/cert/keystore.jks
ADD /config/truststore.jks /opt/app/policy-agent/etc/cert/truststore.jks
-RUN groupadd -g 999 appuser && \
- useradd -r -u 999 -g appuser appuser
-RUN chown -R appuser:appuser /opt/app/policy-agent
-RUN chown -R appuser:appuser /var/log/policy-agent
-USER appuser
+RUN chmod -R 777 /opt/app/policy-agent/config/
+RUN chmod -R 777 /opt/app/policy-agent/data/
ADD target/${JAR} /opt/app/policy-agent/policy-agent.jar
CMD ["java", "-jar", "/opt/app/policy-agent/policy-agent.jar"]
RUN mkdir -p /var/log/dmaap-adaptor-service
RUN mkdir -p /opt/app/dmaap-adaptor-service/etc/cert/
RUN mkdir -p /var/dmaap-adaptor-service
+RUN chmod -R 777 /var/dmaap-adaptor-service
ADD /config/application.yaml /opt/app/dmaap-adaptor-service/config/application.yaml
ADD /config/application_configuration.json /opt/app/dmaap-adaptor-service/data/application_configuration.json_example
ADD /config/keystore.jks /opt/app/dmaap-adaptor-service/etc/cert/keystore.jks
ADD /config/truststore.jks /opt/app/dmaap-adaptor-service/etc/cert/truststore.jks
-
-RUN groupadd -g 999 appuser && \
- useradd -r -u 999 -g appuser appuser
-RUN chown -R appuser:appuser /var/dmaap-adaptor-service/
-RUN chown -R appuser:appuser /opt/app/dmaap-adaptor-service/
-USER appuser
+RUN chmod -R 777 /opt/app/dmaap-adaptor-service/config/
ADD target/${JAR} /opt/app/dmaap-adaptor-service/dmaap-adaptor.jar
CMD ["java", "-jar", "/opt/app/dmaap-adaptor-service/dmaap-adaptor.jar"]
#DMAAP_MEDIATOR_GO
DMAAP_MEDIATOR_GO_BASE="nexus3.o-ran-sc.org:10004/o-ran-sc/nonrtric-dmaap-mediator-producer"
-DMAAP_MEDIATOR_GO_TAG="1.0,0"
+DMAAP_MEDIATOR_GO_TAG="1.0.0"
#DMAAP_MEDIATOR_JAVA
DMAAP_MEDIATOR_JAVA_BASE="nexus3.o-ran-sc.org:10003/o-ran-sc/nonrtric-dmaap-adaptor"
RUN mkdir -p /var/log/information-coordinator-service
RUN mkdir -p /opt/app/information-coordinator-service/etc/cert/
RUN mkdir -p /var/information-coordinator-service
+RUN chmod -R 777 /var/information-coordinator-service
EXPOSE 8083 8434
ADD /config/keystore.jks /opt/app/information-coordinator-service/etc/cert/keystore.jks
ADD /config/truststore.jks /opt/app/information-coordinator-service/etc/cert/truststore.jks
-RUN groupadd -g 999 appuser && \
- useradd -r -u 999 -g appuser appuser
-RUN chown -R appuser:appuser /opt/app/information-coordinator-service
-RUN chown -R appuser:appuser /var/information-coordinator-service
-RUN chown -R appuser:appuser /var/log/information-coordinator-service
-USER appuser
+
+RUN chmod -R 777 /opt/app/information-coordinator-service/config/
CMD ["java", "-jar", "/opt/app/information-coordinator-service/information-coordinator-service.jar"]
EXPOSE 8680 8633
-RUN groupadd -g 999 appuser && \
- useradd -r -u 999 -g appuser appuser
-RUN chown -R appuser:appuser /opt/app/r-app-catalogue/
-RUN chown -R appuser:appuser /var/log/r-app-catalogue/
-USER appuser
-
ADD /config/application.yaml /opt/app/r-app-catalogue/config/application.yaml
ADD /config/r-app-catalogue-keystore.jks /opt/app/r-app-catalogue/etc/cert/keystore.jks
ADD target/${JAR} /opt/app/r-app-catalogue/r-app-catalogue.jar
+
+RUN chmod -R 777 /opt/app/r-app-catalogue/config/
+
CMD ["java", "-jar", "/opt/app/r-app-catalogue/r-app-catalogue.jar"]
RUN chmod +x start.sh
-RUN groupadd -g 999 appuser && \
- useradd -r -u 999 -g appuser appuser
-
-## add permissions for appuser user
-RUN chown -R appuser:appuser /usr/src/app/ && chmod -R 755 /usr/src/app/ && \
- chown -R appuser:appuser /var/log/nginx && \
- chown -R appuser:appuser /var/lib/nginx && \
- chown -R appuser:appuser /etc/nginx/conf.d
-RUN touch /var/run/nginx.pid && \
- chown -R appuser:appuser /var/run/nginx.pid
-
-USER appuser
-
CMD [ "./start.sh" ]
-# user www-data;
+user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
WORKDIR /usr/src/app
COPY http_proxy.js .
-USER node
-
CMD [ "node", "http_proxy.js" ]
\ No newline at end of file
RUN chmod +x start.sh
-RUN groupadd -g 999 appuser && \
- useradd -r -u 999 -g appuser appuser
-## add permissions for appuser user
-RUN chown -R appuser:appuser /usr/src/app/ && chmod -R 755 /usr/src/app/ && \
- chown -R appuser:appuser /var/log/nginx && \
- chown -R appuser:appuser /var/lib/nginx && \
- chown -R appuser:appuser /etc/nginx/conf.d
-RUN touch /var/run/nginx.pid && \
- chown -R appuser:appuser /var/run/nginx.pid
-
-USER appuser
-
CMD [ "./start.sh" ]
\ No newline at end of file
-# user www-data;
+user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
RUN apt-get update
RUN apt-get install -y nginx=1.14.*
-RUN groupadd -g 999 appuser && \
- useradd -r -u 999 -g appuser appuser
-
-## add permissions for appuser user
-RUN chown -R appuser:appuser /usr/src/app/ && chmod -R 755 /usr/src/app/ && \
- chown -R appuser:appuser /var/log/nginx && \
- chown -R appuser:appuser /var/lib/nginx && \
- chown -R appuser:appuser /etc/nginx/conf.d
-RUN touch /var/run/nginx.pid && \
- chown -R appuser:appuser /var/run/nginx.pid
-
-USER appuser
-
CMD [ "./start.sh" ]
-# user www-data;
+user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
--- /dev/null
+#==================================================================================
+# Copyright (C) 2021: Nordix Foundation
+#
+# 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.
+#
+#==================================================================================
+##
+## Build
+##
+FROM nexus3.o-ran-sc.org:10001/golang:1.17-bullseye AS build
+
+WORKDIR /app
+
+COPY go.mod ./
+COPY go.sum ./
+RUN go mod download
+
+COPY . ./
+
+RUN go build -o /oduclosedloop-sliceassurance
+
+##
+## Deploy
+##
+FROM gcr.io/distroless/base-debian10
+
+WORKDIR /
+
+## Copy from "build" stage
+COPY --from=build /oduclosedloop-sliceassurance .
+
+USER nonroot:nonroot
+
+ENTRYPOINT ["/oduclosedloop-sliceassurance"]
\ No newline at end of file
--- /dev/null
+# O-RAN-SC Non-RealTime RIC O-DU Closed Loop Usecase Slice Assurance
+
+## Configuration
+
+The consumer takes a number of environment variables, described below, as configuration.
+
+>- MR_HOST **Required**. The host for Dmaap Message Router. Example: `http://mrproducer`
+>- MR_PORT **Required**. The port for the Dmaap Message Router. Example: `8095`
+>- SDNR_ADDRESS Optional. The address for SDNR. Defaults to `http://localhost:3904`.
+>- SDNR_USER Optional. The user for the SDNR. Defaults to `admin`.
+>- SDNR_PASSWORD Optional. The password for the SDNR user. Defaults to `Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U`.
+>- LOG_LEVEL Optional. The log level, which can be `Error`, `Warn`, `Info` or `Debug`. Defaults to `Info`.
+>- POLLTIME Optional. Waiting time between one pull request to Dmaap and another. Defaults to 10 sec
+
+
+## Development
+
+To make it easy to test during development of the consumer, there is a stub provided in the `stub` folder.
+
+This stub is used to simulate both received VES messages from Dmaap MR with information about performance measurements for the slices in a determinated DU and also SDNR, that sends information about Radio Resource Management Policy Ratio and allows to modify value for RRM Policy Dedicated Ratio from default to higher value.
+
+By default, SDNR stub listens to the port `3904`, but his can be overridden by passing a `--sdnr-port [PORT]` flag when starting the stub. For Dmaap MR stub default port is `3905` but it can be overriden by passing a `--dmaap-port [PORT]` flag when starting the stub.
+
+To build and start the stub, do the following:
+
+>1. cd stub
+>2. go build
+>3. ./stub [--sdnr-port <portNo>] [--dmaap-port <portNo>]
+
+## License
+
+Copyright (C) 2021 Nordix Foundation.
+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.
+
+For more information about license please see the [LICENSE](LICENSE.txt) file for details.
\ No newline at end of file
--- /dev/null
+module oransc.org/usecase/oduclosedloop
+
+go 1.17
+
+require github.com/sirupsen/logrus v1.8.1
+
+require github.com/gorilla/mux v1.8.0
+
+require golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e // indirect
--- /dev/null
+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/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
+github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
+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/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
+github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA=
+golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
--- /dev/null
+// -
+// ========================LICENSE_START=================================
+// O-RAN-SC
+// %%
+// Copyright (C) 2021: Nordix Foundation
+// %%
+// 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.
+// ========================LICENSE_END===================================
+//
+
+package config
+
+import (
+ "fmt"
+ "os"
+ "strconv"
+
+ log "github.com/sirupsen/logrus"
+)
+
+type Config struct {
+ MRHost string
+ MRPort string
+ SDNRAddress string
+ SDNRUser string
+ SDNPassword string
+ Polltime int
+ LogLevel log.Level
+}
+
+func New() *Config {
+ return &Config{
+ MRHost: getEnv("MR_HOST", ""),
+ MRPort: getEnv("MR_PORT", ""),
+ SDNRAddress: getEnv("SDNR_ADDR", "http://localhost:3904"),
+ SDNRUser: getEnv("SDNR_USER", "admin"),
+ SDNPassword: getEnv("SDNR_PASSWORD", "Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U"),
+ Polltime: getEnvAsInt("Polltime", 30),
+ LogLevel: getLogLevel(),
+ }
+}
+
+func (c Config) String() string {
+ return fmt.Sprintf("ConsumerHost: %v, ConsumerPort: %v, SDNRAddress: %v, SDNRUser: %v, SDNRPassword: %v, LogLevel: %v", c.MRHost, c.MRPort, c.SDNRAddress, c.SDNRUser, c.SDNPassword, c.LogLevel)
+}
+
+func getEnv(key string, defaultVal string) string {
+ if value, exists := os.LookupEnv(key); exists {
+ return value
+ }
+
+ return defaultVal
+}
+
+func getEnvAsInt(name string, defaultVal int) int {
+ valueStr := getEnv(name, "")
+ if value, err := strconv.Atoi(valueStr); err == nil {
+ return value
+ } else if valueStr != "" {
+ log.Warnf("Invalid int value: %v for variable: %v. Default value: %v will be used", valueStr, name, defaultVal)
+ }
+
+ return defaultVal
+}
+
+func getLogLevel() log.Level {
+ logLevelStr := getEnv("LOG_LEVEL", "Info")
+ if loglevel, err := log.ParseLevel(logLevelStr); err == nil {
+ return loglevel
+ } else {
+ log.Warnf("Invalid log level: %v. Log level will be Info!", logLevelStr)
+ return log.InfoLevel
+ }
+
+}
--- /dev/null
+// -
+// ========================LICENSE_START=================================
+// O-RAN-SC
+// %%
+// Copyright (C) 2021: Nordix Foundation
+// %%
+// 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.
+// ========================LICENSE_END===================================
+//
+
+package restclient
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+
+ log "github.com/sirupsen/logrus"
+)
+
+type Client struct {
+ httpClient *http.Client
+}
+
+func New(httpClient *http.Client) *Client {
+ return &Client{
+ httpClient: httpClient,
+ }
+}
+
+type HTTPClient interface {
+ Get(path string, v interface{}) error
+ Post(path string, payload interface{}, v interface{}) error
+}
+
+func (c *Client) Get(path string, v interface{}) error {
+ req, err := c.newRequest(http.MethodGet, path, nil)
+ if err != nil {
+ return fmt.Errorf("failed to create GET request: %w", err)
+ }
+
+ if err := c.doRequest(req, v); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (c *Client) Post(path string, payload interface{}, v interface{}) error {
+
+ s, _ := json.MarshalIndent(payload, "", "\t")
+ log.Debugf("Post request payload: " + string(s))
+
+ req, err := c.newRequest(http.MethodPost, path, payload)
+ if err != nil {
+ return fmt.Errorf("failed to create POST request: %w", err)
+ }
+
+ if err := c.doRequest(req, v); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (c *Client) newRequest(method, path string, payload interface{}) (*http.Request, error) {
+ var reqBody io.Reader
+ if payload != nil {
+ bodyBytes, err := json.Marshal(payload)
+ if err != nil {
+ return nil, fmt.Errorf("failed to marshal request body: %w", err)
+ }
+ reqBody = bytes.NewReader(bodyBytes)
+ }
+
+ req, err := http.NewRequest(method, path, reqBody)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create HTTP request: %w", err)
+ }
+
+ if reqBody != nil {
+ req.Header.Set("Content-Type", "application/json; charset=utf-8")
+ }
+ log.Debugf("Http Client Request: [%s:%s]\n", req.Method, req.URL)
+ return req, nil
+}
+
+func (c *Client) doRequest(r *http.Request, v interface{}) error {
+ resp, err := c.do(r)
+ if err != nil {
+ return err
+ }
+
+ if resp == nil {
+ return nil
+ }
+ defer resp.Body.Close()
+
+ if v == nil {
+ return nil
+ }
+
+ dec := json.NewDecoder(resp.Body)
+ if err := dec.Decode(v); err != nil {
+ return fmt.Errorf("could not parse response body: %w [%s:%s]", err, r.Method, r.URL.String())
+ }
+ log.Debugf("Http Client Response: %v\n", v)
+ return nil
+}
+
+func (c *Client) do(r *http.Request) (*http.Response, error) {
+ resp, err := c.httpClient.Do(r)
+ if err != nil {
+ return nil, fmt.Errorf("failed to make request [%s:%s]: %w", r.Method, r.URL.String(), err)
+ }
+
+ if resp.StatusCode >= http.StatusOK && resp.StatusCode <= 299 {
+ return resp, nil
+ }
+
+ defer resp.Body.Close()
+
+ return resp, fmt.Errorf("failed to do request, %d status code received", resp.StatusCode)
+}
--- /dev/null
+// -
+// ========================LICENSE_START=================================
+// O-RAN-SC
+// %%
+// Copyright (C) 2021: Nordix Foundation
+// %%
+// 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.
+// ========================LICENSE_END===================================
+//
+
+package sliceassurance
+
+import (
+ "net/http"
+ "time"
+
+ "oransc.org/usecase/oduclosedloop/internal/restclient"
+ "oransc.org/usecase/oduclosedloop/internal/structures"
+ "oransc.org/usecase/oduclosedloop/messages"
+
+ log "github.com/sirupsen/logrus"
+)
+
+const (
+ THRESHOLD_TPUT = 700
+ DEFAULT_DEDICATED_RATIO = 40
+ NEW_DEDICATED_RATIO = 50
+ NODE_ID = "O-DU-1211"
+)
+
+type App struct {
+ client restclient.HTTPClient
+ metricsPolicies *structures.SliceAssuranceMeas
+}
+
+var dmaapMRUrl string
+var sDNRUrl string
+
+func (a *App) Initialize(dmaapUrl string, sdnrUrl string) {
+ dmaapMRUrl = dmaapUrl
+ sDNRUrl = sdnrUrl
+
+ a.client = restclient.New(&http.Client{})
+ a.metricsPolicies = structures.NewSliceAssuranceMeas()
+}
+
+func (a *App) Run(topic string, pollTime int) {
+ for {
+ a.getMessagesFromDmaap(dmaapMRUrl + topic)
+
+ for key := range a.metricsPolicies.Metrics {
+ a.getRRMInformation(key.Duid)
+ }
+ a.updateDedicatedRatio()
+
+ time.Sleep(time.Second * time.Duration(pollTime))
+ }
+}
+
+func (a *App) getMessagesFromDmaap(url string) {
+ var stdMessage messages.StdDefinedMessage
+
+ a.client.Get(url, &stdMessage)
+ log.Infof("Polling new messages from DmaapMR: %v", stdMessage)
+ for _, meas := range stdMessage.GetMeasurements() {
+ //Create sliceMetric and check if metric exist and update existing one or create new one
+ if _, err := a.metricsPolicies.AddOrUpdateMetric(meas); err != nil {
+ log.Error("Metric could not be added ", err)
+ }
+ }
+}
+
+func (a *App) getRRMInformation(duid string) {
+ var duRRMPolicyRatio messages.ORanDuRestConf
+ a.client.Get(getUrlForDistributedUnitFunctions(sDNRUrl, duid), &duRRMPolicyRatio)
+
+ policies := duRRMPolicyRatio.DistributedUnitFunction.RRMPolicyRatio
+ for _, policy := range policies {
+ a.metricsPolicies.AddNewPolicy(duid, policy)
+ }
+}
+
+func (a *App) updateDedicatedRatio() {
+
+ for _, metric := range a.metricsPolicies.Metrics {
+ policy, check := a.metricsPolicies.Policies[metric.RRMPolicyRatioId]
+ //TODO What happened if dedicated ratio is already higher that default and threshold is exceed?
+ if check && policy.PolicyDedicatedRatio <= DEFAULT_DEDICATED_RATIO {
+ log.Infof("Send Post Request to update DedicatedRatio for DU id: %v Policy id: %v", metric.DUId, policy.PolicyRatioId)
+ url := getUrlUpdatePolicyDedicatedRatio(sDNRUrl, metric.DUId, policy.PolicyRatioId)
+ a.client.Post(url, policy.GetUpdateDedicatedRatioMessage(metric.SliceDiff, metric.SliceServiceType, NEW_DEDICATED_RATIO), nil)
+ }
+ }
+}
+
+func getUrlForDistributedUnitFunctions(host string, duid string) string {
+ return host + "/rests/data/network-topology:network-topology/topology=topology-netconf/node=" + NODE_ID + "/yang-ext:mount/o-ran-sc-du-hello-world:network-function/distributed-unit-functions=" + duid
+}
+
+func getUrlUpdatePolicyDedicatedRatio(host string, duid string, policyid string) string {
+ return host + "/rests/data/network-topology:network-topology/topology=topology-netconf/node=" + NODE_ID + "/yang-ext:mount/o-ran-sc-du-hello-world:network-function/distributed-unit-functions=" + duid + "/radio-resource-management-policy-ratio=" + policyid
+}
--- /dev/null
+// -
+// ========================LICENSE_START=================================
+// O-RAN-SC
+// %%
+// Copyright (C) 2021: Nordix Foundation
+// %%
+// 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.
+// ========================LICENSE_END===================================
+//
+
+package structures
+
+import "oransc.org/usecase/oduclosedloop/messages"
+
+type SliceMetric struct {
+ DUId string
+ CellId string
+ SliceDiff int
+ SliceServiceType int
+ RRMPolicyRatioId string
+ PM map[string]int
+}
+
+func NewSliceMetric(duid string, cellid string, sd int, sst int) *SliceMetric {
+ sm := SliceMetric{
+ DUId: duid,
+ CellId: cellid,
+ SliceDiff: sd,
+ SliceServiceType: sst,
+ }
+ sm.PM = make(map[string]int)
+ return &sm
+}
+
+type PolicyRatio struct {
+ PolicyRatioId string
+ PolicyMaxRatio int
+ PolicyMinRatio int
+ PolicyDedicatedRatio int
+}
+
+func NewPolicyRatio(id string, max_ratio int, min_ratio int, ded_ratio int) *PolicyRatio {
+ pr := PolicyRatio{
+ PolicyRatioId: id,
+ PolicyMaxRatio: max_ratio,
+ PolicyMinRatio: min_ratio,
+ PolicyDedicatedRatio: ded_ratio,
+ }
+ return &pr
+}
+
+func (pr *PolicyRatio) GetUpdateDedicatedRatioMessage(sd int, sst int, dedicatedRatio int) []messages.RRMPolicyRatio {
+ message := messages.RRMPolicyRatio{
+ Id: pr.PolicyRatioId,
+ AdmState: "Locked",
+ UserLabel: "Some user label",
+ RRMPolicyMaxRatio: pr.PolicyMaxRatio,
+ RRMPolicyMinRatio: pr.PolicyMinRatio,
+ RRMPolicyDedicatedRatio: dedicatedRatio,
+ ResourceType: "prb",
+ RRMPolicyMembers: []messages.RRMPolicyMember{
+ {
+ MobileCountryCode: "046",
+ MobileNetworkCode: "651",
+ SliceDifferentiator: sd,
+ SliceServiceType: sst,
+ },
+ },
+ }
+ return []messages.RRMPolicyRatio{message}
+}
--- /dev/null
+// -
+// ========================LICENSE_START=================================
+// O-RAN-SC
+// %%
+// Copyright (C) 2021: Nordix Foundation
+// %%
+// 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.
+// ========================LICENSE_END===================================
+//
+
+package structures
+
+import (
+ "fmt"
+ "regexp"
+ "strconv"
+
+ "oransc.org/usecase/oduclosedloop/messages"
+)
+
+type MapKey struct {
+ Duid string
+ sd int
+ sst int
+}
+
+type SliceAssuranceMeas struct {
+ Metrics map[MapKey]*SliceMetric
+ Policies map[string]*PolicyRatio
+}
+
+func NewSliceAssuranceMeas() *SliceAssuranceMeas {
+ s := SliceAssuranceMeas{}
+ s.Metrics = make(map[MapKey]*SliceMetric)
+ s.Policies = make(map[string]*PolicyRatio)
+ return &s
+}
+
+func (sa *SliceAssuranceMeas) AddNewPolicy(duid string, rrmPolicyRatio messages.RRMPolicyRatio) {
+ for _, policyMember := range rrmPolicyRatio.RRMPolicyMembers {
+ metric := sa.GetSliceMetric(duid, policyMember.SliceDifferentiator, policyMember.SliceServiceType)
+ if metric != nil {
+ pr := NewPolicyRatio(rrmPolicyRatio.Id, rrmPolicyRatio.RRMPolicyMaxRatio, rrmPolicyRatio.RRMPolicyMinRatio, rrmPolicyRatio.RRMPolicyDedicatedRatio)
+ sa.Policies[pr.PolicyRatioId] = pr
+ metric.RRMPolicyRatioId = rrmPolicyRatio.Id
+ }
+ }
+}
+
+func (sa *SliceAssuranceMeas) GetSliceMetric(duid string, sd int, sst int) *SliceMetric {
+ key := MapKey{duid, sd, sst}
+ value, check := sa.Metrics[key]
+
+ if check {
+ return value
+ }
+
+ return nil
+}
+
+func (sa *SliceAssuranceMeas) AddOrUpdateMetric(meas messages.Measurement) (string, error) {
+
+ var duid string
+ var sd, sst int
+
+ regex := *regexp.MustCompile(`\/network-function\/distributed-unit-functions\[id=\'(.*)\'\]/cell\[id=\'(.*)\'\]/supported-measurements\/performance-measurement-type\[\.=\'(.*)\'\]\/supported-snssai-subcounter-instances\/slice-differentiator\[\.=(\d)\]\[slice-service-type=(\d+)\]`)
+ res := regex.FindAllStringSubmatch(meas.MeasurementTypeInstanceReference, -1)
+
+ if res != nil && len(res[0]) == 6 {
+ duid = res[0][1]
+ sd = toInt(res[0][4])
+ sst = toInt(res[0][5])
+
+ key := MapKey{duid, sd, sst}
+ value, check := sa.Metrics[key]
+
+ if check {
+ sa.updateMetric(key, value, res[0][3], meas.Value)
+ } else {
+ // Only add new one if value exceeds threshold
+ sa.addMetric(res, meas.Value)
+ }
+ } else {
+ return duid, fmt.Errorf(" wrong format for MeasurementTypeInstanceReference")
+ }
+ return duid, nil
+}
+
+func (sa *SliceAssuranceMeas) addMetric(res [][]string, metricValue int) {
+ if metricValue > 700 {
+ metric := NewSliceMetric(res[0][1], res[0][2], toInt(res[0][4]), toInt(res[0][5]))
+ metric.PM[res[0][3]] = metricValue
+ key := MapKey{res[0][1], toInt(res[0][4]), toInt(res[0][5])}
+ sa.Metrics[key] = metric
+ }
+}
+
+func (sa *SliceAssuranceMeas) updateMetric(key MapKey, value *SliceMetric, metricName string, metricValue int) {
+ if metricValue < 700 {
+ delete(sa.Metrics, key)
+ } else {
+ value.PM[metricName] = metricValue
+ }
+}
+
+func toInt(num string) int {
+ res, err := strconv.Atoi(num)
+ if err != nil {
+ return -1
+ }
+ return res
+}
+
+func (sa *SliceAssuranceMeas) PrintStructures() {
+ fmt.Printf("SliceAssurance Metrics: \n")
+ for key, metric := range sa.Metrics {
+ fmt.Printf("Key: %+v\n", key)
+ fmt.Printf("Metric: %+v\n", metric)
+ }
+ fmt.Printf("SliceAssurance Policies: \n")
+ for key, metric := range sa.Policies {
+ fmt.Printf("Key: %+v\n", key)
+ fmt.Printf("Metric: %+v\n", metric)
+ }
+}
--- /dev/null
+// -
+// ========================LICENSE_START=================================
+// O-RAN-SC
+// %%
+// Copyright (C) 2021: Nordix Foundation
+// %%
+// 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.
+// ========================LICENSE_END===================================
+//
+
+package main
+
+import (
+ "fmt"
+
+ log "github.com/sirupsen/logrus"
+ "oransc.org/usecase/oduclosedloop/internal/config"
+ "oransc.org/usecase/oduclosedloop/internal/sliceassurance"
+)
+
+const TOPIC string = "/events/unauthenticated.PERFORMANCE_MEASUREMENTS"
+
+var configuration *config.Config
+
+func main() {
+ configuration = config.New()
+
+ log.SetLevel(configuration.LogLevel)
+ log.SetFormatter(&log.JSONFormatter{})
+
+ log.Debug("Using configuration: ", configuration)
+
+ dmaapUrl := configuration.MRHost + ":" + configuration.MRPort
+
+ if err := validateConfiguration(configuration); err != nil {
+ log.Fatalf("Unable to start consumer due to configuration error: %v", err)
+ }
+
+ a := sliceassurance.App{}
+ a.Initialize(dmaapUrl, configuration.SDNRAddress)
+ a.Run(TOPIC, configuration.Polltime)
+
+}
+
+func validateConfiguration(configuration *config.Config) error {
+ if configuration.MRHost == "" || configuration.MRPort == "" {
+ return fmt.Errorf("message router host and port must be provided")
+ }
+ return nil
+}
--- /dev/null
+// -
+// ========================LICENSE_START=================================
+// O-RAN-SC
+// %%
+// Copyright (C) 2021: Nordix Foundation
+// %%
+// 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.
+// ========================LICENSE_END===================================
+//
+
+package messages
+
+type ORanDuRestConf struct {
+ DistributedUnitFunction DistributedUnitFunction `json:"distributed-unit-functions"`
+}
+
+type DistributedUnitFunction struct {
+ Id string `json:"id"`
+ RRMPolicyRatio []RRMPolicyRatio `json:"radio-resource-management-policy-ratio"`
+}
+
+type RRMPolicyRatio struct {
+ Id string `json:"id"`
+ AdmState string `json:"administrative-state"`
+ UserLabel string `json:"user-label"`
+ RRMPolicyMaxRatio int `json:"radio-resource-management-policy-max-ratio"`
+ RRMPolicyMinRatio int `json:"radio-resource-management-policy-min-ratio"`
+ RRMPolicyDedicatedRatio int `json:"radio-resource-management-policy-dedicated-ratio"`
+ ResourceType string `json:"resource-type"`
+ RRMPolicyMembers []RRMPolicyMember `json:"radio-resource-management-policy-members"`
+}
+
+type RRMPolicyMember struct {
+ MobileCountryCode string `json:"mobile-country-code"`
+ MobileNetworkCode string `json:"mobile-network-code"`
+ SliceDifferentiator int `json:"slice-differentiator"`
+ SliceServiceType int `json:"slice-service-type"`
+}
--- /dev/null
+// -
+// ========================LICENSE_START=================================
+// O-RAN-SC
+// %%
+// Copyright (C) 2021: Nordix Foundation
+// %%
+// 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.
+// ========================LICENSE_END===================================
+//
+
+package messages
+
+type StdDefinedMessage struct {
+ Event Event `json:"event"`
+}
+
+type Event struct {
+ CommonEventHeader CommonEventHeader `json:"commonEventHeader"`
+ StndDefinedFields StndDefinedFields `json:"stndDefinedFields"`
+}
+
+type CommonEventHeader struct {
+ Domain string `json:"domain"`
+ StndDefinedNamespace string `json:"stndDefinedNamespace"`
+}
+
+type StndDefinedFields struct {
+ StndDefinedFieldsVersion string `json:"stndDefinedFieldsVersion"`
+ SchemaReference string `json:"schemaReference"`
+ Data Data `json:"data"`
+}
+
+type Data struct {
+ DataId string `json:"id"`
+ Measurements []Measurement `json:"measurements"`
+}
+
+type Measurement struct {
+ MeasurementTypeInstanceReference string `json:"measurement-type-instance-reference"`
+ Value int `json:"value"`
+ Unit string `json:"unit"`
+}
+
+func (message StdDefinedMessage) GetMeasurements() []Measurement {
+ return message.Event.StndDefinedFields.Data.Measurements
+}
--- /dev/null
+// -
+// ========================LICENSE_START=================================
+// O-RAN-SC
+// %%
+// Copyright (C) 2021: Nordix Foundation
+// %%
+// 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.
+// ========================LICENSE_END===================================
+//
+
+package main
+
+import (
+ "encoding/json"
+ "flag"
+ "fmt"
+ "math/rand"
+ "net/http"
+ "time"
+
+ "github.com/gorilla/mux"
+ "oransc.org/usecase/oduclosedloop/messages"
+)
+
+func main() {
+ rand.Seed(time.Now().UnixNano())
+ port := flag.Int("port", 3905, "The port this message router will listen on")
+ flag.Parse()
+
+ r := mux.NewRouter()
+ r.HandleFunc("/events/unauthenticated.PERFORMANCE_MEASUREMENTS", sendStdMessage).Methods(http.MethodGet)
+
+ fmt.Println("Starting mr on port: ", *port)
+
+ http.ListenAndServe(fmt.Sprintf(":%v", *port), r)
+
+}
+
+// Variables ::
+// DU-ID: ERICSSON-O-DU-11220
+// Cell-ID: cell1
+// Slice-Diff: 2
+// Value: 300
+func sendStdMessage(w http.ResponseWriter, r *http.Request) {
+ message := fetchMessage()
+ fmt.Println("-----------------------------------------------------------------------------")
+ fmt.Println("Sending message: ", message)
+ fmt.Println("-----------------------------------------------------------------------------")
+ response, _ := json.Marshal(message)
+ time.Sleep(time.Duration(rand.Intn(3)) * time.Second)
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ w.Write(response)
+}
+
+func fetchMessage() messages.StdDefinedMessage {
+
+ index := rand.Intn(5)
+ fmt.Println(index)
+
+ measurements := [5][]messages.Measurement{meas1, meas2, meas3, meas4, meas5}
+
+ message := messages.StdDefinedMessage{
+ Event: messages.Event{
+ CommonEventHeader: messages.CommonEventHeader{
+ Domain: "stndDefined",
+ StndDefinedNamespace: "o-ran-sc-du-hello-world-pm-streaming-oas3",
+ },
+ StndDefinedFields: messages.StndDefinedFields{
+ StndDefinedFieldsVersion: "1.0",
+ SchemaReference: "https://gerrit.o-ran-sc.org/r/gitweb?p=scp/oam/modeling.git;a=blob_plain;f=data-model/oas3/experimental/o-ran-sc-du-hello-world-oas3.json;hb=refs/heads/master",
+ Data: messages.Data{
+ DataId: "id",
+ Measurements: measurements[index],
+ },
+ },
+ },
+ }
+ return message
+}
+
+var meas1 = []messages.Measurement{
+ {
+ MeasurementTypeInstanceReference: "/network-function/distributed-unit-functions[id='ERICSSON-O-DU-11220']/cell[id='cell1']/supported-measurements/performance-measurement-type[.='user-equipment-average-throughput-downlink']/supported-snssai-subcounter-instances/slice-differentiator[.=2][slice-service-type=1]",
+ Value: 300,
+ Unit: "kbit/s",
+ },
+}
+
+var meas2 = []messages.Measurement{
+ {
+ MeasurementTypeInstanceReference: "/network-function/distributed-unit-functions[id='ERICSSON-O-DU-11220']/cell[id='cell1']/supported-measurements/performance-measurement-type[.='user-equipment-average-throughput-downlink']/supported-snssai-subcounter-instances/slice-differentiator[.=1]",
+ Value: 400,
+ Unit: "kbit/s",
+ },
+}
+
+var meas3 = []messages.Measurement{
+ {
+ MeasurementTypeInstanceReference: "/network-function/distributed-unit-functions[id='ERICSSON-O-DU-11220']/cell[id='cell1']/supported-measurements/performance-measurement-type[.='user-equipment-average-throughput-uplink']/supported-snssai-subcounter-instances/slice-differentiator[.=2][slice-service-type=2]",
+ Value: 800,
+ Unit: "kbit/s",
+ },
+}
+
+var meas4 = []messages.Measurement{
+ {
+ MeasurementTypeInstanceReference: "/network-function/distributed-unit-functions[id='ERICSSON-O-DU-11220']/cell[id='cell1']/supported-measurements/performance-measurement-type[.='user-equipment-average-throughput-downlink']/supported-snssai-subcounter-instances/slice-differentiator[.=1]",
+ Value: 750,
+ Unit: "kbit/s",
+ },
+}
+
+var meas5 = []messages.Measurement{
+ {
+ MeasurementTypeInstanceReference: "/network-function/distributed-unit-functions[id='ERICSSON-O-DU-11220']/cell[id='cell1']/supported-measurements/performance-measurement-type[.='user-equipment-average-throughput-downlink']/supported-snssai-subcounter-instances/[slice-differentiator[.=2]][slice-service-type=1]",
+ Value: 900,
+ Unit: "kbit/s",
+ },
+}
--- /dev/null
+// -
+// ========================LICENSE_START=================================
+// O-RAN-SC
+// %%
+// Copyright (C) 2021: Nordix Foundation
+// %%
+// 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.
+// ========================LICENSE_END===================================
+//
+
+package main
+
+import (
+ "encoding/json"
+ "flag"
+ "fmt"
+ "net/http"
+
+ "github.com/gorilla/mux"
+ "oransc.org/usecase/oduclosedloop/messages"
+)
+
+func main() {
+ port := flag.Int("port", 3904, "The port this SDNR stub will listen on")
+ flag.Parse()
+
+ r := mux.NewRouter()
+ r.HandleFunc("/rests/data/network-topology:network-topology/topology=topology-netconf/node={NODE-ID}/yang-ext:mount/o-ran-sc-du-hello-world:network-function/distributed-unit-functions={O-DU-ID}", getDistributedUnitFunctions).Methods(http.MethodGet)
+ r.HandleFunc("/rests/data/network-topology:network-topology/topology=topology-netconf/node={NODE-ID}/yang-ext:mount/o-ran-sc-du-hello-world:network-function/distributed-unit-functions={O-DU-ID}/radio-resource-management-policy-ratio={POLICY-ID}", updateRRMPolicyDedicatedRatio).Methods(http.MethodPost)
+
+ fmt.Println("Starting SDNR on port: ", *port)
+ http.ListenAndServe(fmt.Sprintf(":%v", *port), r)
+
+}
+
+func getDistributedUnitFunctions(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+
+ message := messages.ORanDuRestConf{
+ DistributedUnitFunction: messages.DistributedUnitFunction{
+ Id: vars["O-DU-ID"],
+ RRMPolicyRatio: []messages.RRMPolicyRatio{
+ {
+ Id: "rrm-pol-1",
+ AdmState: "locked",
+ UserLabel: "rrm-pol-1",
+ RRMPolicyMaxRatio: 100,
+ RRMPolicyMinRatio: "0",
+ RRMPolicyDedicatedRatio: "0",
+ ResourceType: "prb",
+ RRMPolicyMembers: []messages.RRMPolicyMember{
+ {
+ MobileCountryCode: "046",
+ MobileNetworkCode: "651",
+ SliceDifferentiator: 1,
+ SliceServiceType: 0,
+ },
+ },
+ },
+ {
+ Id: "rrm-pol-2",
+ AdmState: "unlocked",
+ UserLabel: "rrm-pol-2",
+ RRMPolicyMaxRatio: 20,
+ RRMPolicyMinRatio: "10",
+ RRMPolicyDedicatedRatio: "15",
+ ResourceType: "prb",
+ RRMPolicyMembers: []messages.RRMPolicyMember{
+ {
+ MobileCountryCode: "046",
+ MobileNetworkCode: "651",
+ SliceDifferentiator: 2,
+ SliceServiceType: 1,
+ },
+ },
+ },
+ {
+ Id: "rrm-pol-3",
+ AdmState: "unlocked",
+ UserLabel: "rrm-pol-3",
+ RRMPolicyMaxRatio: 30,
+ RRMPolicyMinRatio: "10",
+ RRMPolicyDedicatedRatio: "5",
+ ResourceType: "prb",
+ RRMPolicyMembers: []messages.RRMPolicyMember{
+ {
+ MobileCountryCode: "310",
+ MobileNetworkCode: "150",
+ SliceDifferentiator: 2,
+ SliceServiceType: 2,
+ },
+ },
+ },
+ },
+ },
+ }
+
+ respondWithJSON(w, http.StatusOK, message)
+}
+
+func updateRRMPolicyDedicatedRatio(w http.ResponseWriter, r *http.Request) {
+ //vars := mux.Vars(r)
+ fmt.Println("::updateRRMPolicyDedicatedRatio::")
+ var prMessage messages.DistributedUnitFunction
+ decoder := json.NewDecoder(r.Body)
+
+ if err := decoder.Decode(&prMessage); err != nil {
+ respondWithError(w, http.StatusBadRequest, "Invalid request payload")
+ return
+ }
+ defer r.Body.Close()
+
+ fmt.Println("prMessage: ", prMessage)
+ //prMessage.Id = vars["POLICY-ID"]
+
+ respondWithJSON(w, http.StatusOK, map[string]string{"status": "200"})
+}
+
+func respondWithError(w http.ResponseWriter, code int, message string) {
+ fmt.Println("-----------------------------------------------------------------------------")
+ fmt.Println("Sending error message: ", message)
+ fmt.Println("-----------------------------------------------------------------------------")
+ respondWithJSON(w, code, map[string]string{"error": message})
+}
+
+func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
+ fmt.Println("-----------------------------------------------------------------------------")
+ fmt.Println("Sending message: ", payload)
+ fmt.Println("-----------------------------------------------------------------------------")
+ response, _ := json.Marshal(payload)
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(code)
+ w.Write(response)
+}
--- /dev/null
+// -
+// ========================LICENSE_START=================================
+// O-RAN-SC
+// %%
+// Copyright (C) 2021: Nordix Foundation
+// %%
+// 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.
+// ========================LICENSE_END===================================
+//
+
+package main
+
+import (
+ "encoding/csv"
+ "encoding/json"
+ "flag"
+ "fmt"
+ "math/rand"
+ "net/http"
+ "os"
+ "strconv"
+ "sync"
+ "time"
+
+ "github.com/gorilla/mux"
+ "oransc.org/usecase/oduclosedloop/messages"
+
+ log "github.com/sirupsen/logrus"
+)
+
+const THRESHOLD_TPUT int = 700
+
+type SliceAssuranceInformation struct {
+ duId string
+ cellId string
+ sd int
+ sst int
+ metricName string
+ metricValue int
+ policyRatioId string
+ policyMaxRatio int
+ policyMinRatio int
+ policyDedicatedRatio int
+}
+
+var data []*SliceAssuranceInformation
+var messagesToSend []messages.Measurement
+
+func loadData() {
+ lines, err := GetCsvFromFile("test-data.csv")
+ if err != nil {
+ panic(err)
+ }
+ for _, line := range lines {
+ sai := SliceAssuranceInformation{
+ duId: line[0],
+ cellId: line[1],
+ sd: toInt(line[2]),
+ sst: toInt(line[3]),
+ metricName: line[4],
+ metricValue: toInt(line[5]),
+ policyRatioId: line[6],
+ policyMaxRatio: toInt(line[7]),
+ policyMinRatio: toInt(line[8]),
+ policyDedicatedRatio: toInt(line[9]),
+ }
+ data = append(data, &sai)
+ }
+}
+
+func GetCsvFromFile(name string) ([][]string, error) {
+ if csvFile, err := os.Open(name); err == nil {
+ defer csvFile.Close()
+ reader := csv.NewReader(csvFile)
+ reader.FieldsPerRecord = -1
+ if csvData, err := reader.ReadAll(); err == nil {
+ return csvData, nil
+ } else {
+ return nil, err
+ }
+ } else {
+ return nil, err
+ }
+}
+
+func toInt(num string) int {
+ res, err := strconv.Atoi(num)
+ if err != nil {
+ return -1
+ }
+ return res
+}
+
+func main() {
+ rand.Seed(time.Now().UnixNano())
+
+ portSdnr := flag.Int("sdnr-port", 3904, "The port this SDNR stub will listen on")
+ portDmaapMR := flag.Int("dmaap-port", 3905, "The port this Dmaap message router will listen on")
+ flag.Parse()
+
+ loadData()
+
+ wg := new(sync.WaitGroup)
+ wg.Add(2)
+
+ go func() {
+
+ r := mux.NewRouter()
+ r.HandleFunc("/rests/data/network-topology:network-topology/topology=topology-netconf/node={NODE-ID}/yang-ext:mount/o-ran-sc-du-hello-world:network-function/distributed-unit-functions={O-DU-ID}", getSdnrResponseMessage).Methods(http.MethodGet)
+ r.HandleFunc("/rests/data/network-topology:network-topology/topology=topology-netconf/node={NODE-ID}/yang-ext:mount/o-ran-sc-du-hello-world:network-function/distributed-unit-functions={O-DU-ID}/radio-resource-management-policy-ratio={POLICY-ID}", updateRRMPolicyDedicatedRatio).Methods(http.MethodPost)
+ r.HandleFunc("/events/unauthenticated.PERFORMANCE_MEASUREMENTS", sendDmaapMRMessages).Methods(http.MethodGet)
+
+ fmt.Println("Starting SDNR stub on port: ", *portSdnr)
+
+ log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", *portSdnr), r))
+ wg.Done()
+ }()
+
+ go func() {
+
+ r := mux.NewRouter()
+ r.HandleFunc("/events/unauthenticated.PERFORMANCE_MEASUREMENTS", sendDmaapMRMessages).Methods(http.MethodGet)
+
+ fmt.Println("Starting DmaapMR stub on port: ", *portDmaapMR)
+
+ log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", *portDmaapMR), r))
+ wg.Done()
+ }()
+
+ wg.Wait()
+}
+
+func getSdnrResponseMessage(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ log.Info("Get messages for RRM Policy Ratio information for O-Du ID ", vars["O-DU-ID"])
+
+ message := messages.ORanDuRestConf{
+ DistributedUnitFunction: messages.DistributedUnitFunction{
+ Id: vars["O-DU-ID"],
+ RRMPolicyRatio: getPolicyRatioMessage(),
+ },
+ }
+ respondWithJSON(w, http.StatusOK, message)
+}
+
+func getPolicyRatioMessage() []messages.RRMPolicyRatio {
+ var policies []messages.RRMPolicyRatio
+
+ for _, entry := range data {
+
+ message := messages.RRMPolicyRatio{
+ Id: entry.policyRatioId,
+ AdmState: "locked",
+ UserLabel: entry.policyRatioId,
+ RRMPolicyMaxRatio: entry.policyMaxRatio,
+ RRMPolicyMinRatio: entry.policyMinRatio,
+ RRMPolicyDedicatedRatio: entry.policyDedicatedRatio,
+ ResourceType: "prb",
+ RRMPolicyMembers: []messages.RRMPolicyMember{
+ {
+ MobileCountryCode: "046",
+ MobileNetworkCode: "651",
+ SliceDifferentiator: entry.sd,
+ SliceServiceType: entry.sst,
+ },
+ },
+ }
+ policies = append(policies, message)
+ }
+ return policies
+}
+
+func updateRRMPolicyDedicatedRatio(w http.ResponseWriter, r *http.Request) {
+ log.Info("Post request to update RRMPolicyDedicatedRatio")
+
+ var prMessages []messages.RRMPolicyRatio
+ decoder := json.NewDecoder(r.Body)
+
+ if err := decoder.Decode(&prMessages); err != nil {
+ respondWithError(w, http.StatusBadRequest, "Invalid request payload")
+ return
+ }
+ defer r.Body.Close()
+
+ findAndUpdatePolicy(prMessages)
+ respondWithJSON(w, http.StatusOK, map[string]string{"status": "200"})
+}
+
+func findAndUpdatePolicy(rRMPolicyRatio []messages.RRMPolicyRatio) {
+ for _, policy := range rRMPolicyRatio {
+ for _, entry := range data {
+ if entry.policyRatioId == policy.Id {
+ entry.policyDedicatedRatio = policy.RRMPolicyDedicatedRatio
+ log.Info("New value for Policy dedicated ratio: ", entry.policyDedicatedRatio)
+ if entry.metricValue > THRESHOLD_TPUT {
+ entry.metricValue = rand.Intn(THRESHOLD_TPUT)
+ }
+ messagesToSend = append(messagesToSend, generateMeasurementEntry(entry))
+ }
+ }
+ }
+}
+
+func respondWithError(w http.ResponseWriter, code int, message string) {
+ respondWithJSON(w, code, map[string]string{"error": message})
+}
+
+func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
+ response, _ := json.Marshal(payload)
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(code)
+ w.Write(response)
+}
+
+func sendDmaapMRMessages(w http.ResponseWriter, r *http.Request) {
+ log.Info("Send Dmaap messages")
+ entry := data[rand.Intn(5)]
+
+ maxTput := THRESHOLD_TPUT + 100
+ randomTput := rand.Intn(maxTput-THRESHOLD_TPUT+1) + THRESHOLD_TPUT
+ if randomTput%3 == 0 {
+ log.Info("Using tput value higher than THRESHOLD_TPUT ", randomTput)
+ entry.metricValue = randomTput
+ }
+
+ messagesToSend = append(messagesToSend, generateMeasurementEntry(entry))
+
+ message := messages.StdDefinedMessage{
+ Event: messages.Event{
+ CommonEventHeader: messages.CommonEventHeader{
+ Domain: "stndDefined",
+ StndDefinedNamespace: "o-ran-sc-du-hello-world-pm-streaming-oas3",
+ },
+ StndDefinedFields: messages.StndDefinedFields{
+ StndDefinedFieldsVersion: "1.0",
+ SchemaReference: "https://gerrit.o-ran-sc.org/r/gitweb?p=scp/oam/modeling.git;a=blob_plain;f=data-model/oas3/experimental/o-ran-sc-du-hello-world-oas3.json;hb=refs/heads/master",
+ Data: messages.Data{
+ DataId: "id",
+ Measurements: messagesToSend,
+ },
+ },
+ },
+ }
+
+ time.Sleep(time.Duration(rand.Intn(3)) * time.Second)
+ respondWithJSON(w, http.StatusOK, message)
+
+ messagesToSend = nil
+}
+
+func generateMeasurementEntry(entry *SliceAssuranceInformation) messages.Measurement {
+
+ measurementTypeInstanceReference := "/network-function/distributed-unit-functions[id='" + entry.duId + "']/cell[id='" + entry.cellId + "']/supported-measurements/performance-measurement-type[.='" + entry.metricName + "']/supported-snssai-subcounter-instances/slice-differentiator[.=" + strconv.Itoa(entry.sd) + "][slice-service-type=" + strconv.Itoa(entry.sst) + "]"
+ meas := messages.Measurement{
+
+ MeasurementTypeInstanceReference: measurementTypeInstanceReference,
+ Value: entry.metricValue,
+ Unit: "kbit/s",
+ }
+ return meas
+}
--- /dev/null
+O-DU-1211,cell-1,1,1,user-equipment-average-throughput-downlink,300,rrm-pol-1,20,10,15
+O-DU-1211,cell-1,1,1,user-equipment-average-throughput-uplink,500,rrm-pol-1,20,10,15
+O-DU-1211,cell-1,1,2,user-equipment-average-throughput-downlink,700,rrm-pol-2,20,10,15
+O-DU-1211,cell-1,1,2,user-equipment-average-throughput-uplink,400,rrm-pol-2,20,10,15
+O-DU-1211,cell-1,2,1,user-equipment-average-throughput-downlink,800,rrm-pol-3,20,10,15
+O-DU-1211,cell-1,2,1,user-equipment-average-throughput-uplink,100,rrm-pol-3,20,10,15
+O-DU-1211,cell-1,2,2,user-equipment-average-throughput-downlink,900,rrm-pol-4,20,10,15
+O-DU-1211,cell-1,2,2,user-equipment-average-throughput-uplink,500,rrm-pol-4,20,10,15
+O-DU-1211,cell-1,3,1,user-equipment-average-throughput-downlink,800,rrm-pol-5,20,10,15
+O-DU-1211,cell-1,3,1,user-equipment-average-throughput-uplink,100,rrm-pol-5,20,10,15
\ No newline at end of file
RUN pip install -r requirements.txt
-RUN groupadd -g 999 appuser && \
- useradd -r -u 999 -g appuser appuser
-USER appuser
-
CMD [ "python3", "-u", "main.py" ]
RUN pip install -r requirements.txt
-RUN groupadd -g 999 appuser && \
- useradd -r -u 999 -g appuser appuser
-
-USER appuser
-
CMD [ "python3", "-u", "message_generator.py" ]
RUN pip install -r requirements.txt
-RUN groupadd -g 999 appuser && \
- useradd -r -u 999 -g appuser appuser
-
-USER appuser
-
CMD [ "python3", "-u", "sdnr_simulator.py" ]