2 // ========================LICENSE_START=================================
5 // Copyright (C) 2021: Nordix Foundation
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
11 // http://www.apache.org/licenses/LICENSE-2.0
13 // Unless required by applicable law or agreed to in writing, software
14 // distributed under the License is distributed on an "AS IS" BASIS,
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 // See the License for the specific language governing permissions and
17 // limitations under the License.
18 // ========================LICENSE_END===================================
35 "github.com/gorilla/mux"
36 "oransc.org/usecase/oduclosedloop/messages"
38 log "github.com/sirupsen/logrus"
41 const THRESHOLD_TPUT int = 700
43 type SliceAssuranceInformation struct {
53 policyDedicatedRatio int
56 var data []*SliceAssuranceInformation
57 var messagesToSend []messages.Measurement
60 lines, err := GetCsvFromFile("test-data.csv")
64 for _, line := range lines {
65 sai := SliceAssuranceInformation{
71 metricValue: toInt(line[5]),
72 policyRatioId: line[6],
73 policyMaxRatio: toInt(line[7]),
74 policyMinRatio: toInt(line[8]),
75 policyDedicatedRatio: toInt(line[9]),
77 data = append(data, &sai)
81 func GetCsvFromFile(name string) ([][]string, error) {
82 if csvFile, err := os.Open(name); err == nil {
84 reader := csv.NewReader(csvFile)
85 reader.FieldsPerRecord = -1
86 if csvData, err := reader.ReadAll(); err == nil {
96 func toInt(num string) int {
97 res, err := strconv.Atoi(num)
105 rand.Seed(time.Now().UnixNano())
107 portSdnr := flag.Int("sdnr-port", 3904, "The port this SDNR stub will listen on")
108 portDmaapMR := flag.Int("dmaap-port", 3905, "The port this Dmaap message router will listen on")
113 wg := new(sync.WaitGroup)
119 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)
120 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)
121 r.HandleFunc("/events/unauthenticated.PERFORMANCE_MEASUREMENTS", sendDmaapMRMessages).Methods(http.MethodGet)
123 fmt.Println("Starting SDNR stub on port: ", *portSdnr)
125 log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", *portSdnr), r))
132 r.HandleFunc("/events/unauthenticated.PERFORMANCE_MEASUREMENTS", sendDmaapMRMessages).Methods(http.MethodGet)
134 fmt.Println("Starting DmaapMR stub on port: ", *portDmaapMR)
136 log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", *portDmaapMR), r))
143 func getSdnrResponseMessage(w http.ResponseWriter, r *http.Request) {
145 log.Info("Get messages for RRM Policy Ratio information for O-Du ID ", vars["O-DU-ID"])
147 message := messages.ORanDuRestConf{
148 DistributedUnitFunction: messages.DistributedUnitFunction{
150 RRMPolicyRatio: getPolicyRatioMessage(),
153 respondWithJSON(w, http.StatusOK, message)
156 func getPolicyRatioMessage() []messages.RRMPolicyRatio {
157 var policies []messages.RRMPolicyRatio
159 for _, entry := range data {
161 message := messages.RRMPolicyRatio{
162 Id: entry.policyRatioId,
164 UserLabel: entry.policyRatioId,
165 RRMPolicyMaxRatio: entry.policyMaxRatio,
166 RRMPolicyMinRatio: entry.policyMinRatio,
167 RRMPolicyDedicatedRatio: entry.policyDedicatedRatio,
169 RRMPolicyMembers: []messages.RRMPolicyMember{
171 MobileCountryCode: "046",
172 MobileNetworkCode: "651",
173 SliceDifferentiator: entry.sd,
174 SliceServiceType: entry.sst,
178 policies = append(policies, message)
183 func updateRRMPolicyDedicatedRatio(w http.ResponseWriter, r *http.Request) {
184 log.Info("Post request to update RRMPolicyDedicatedRatio")
186 var prMessages []messages.RRMPolicyRatio
187 decoder := json.NewDecoder(r.Body)
189 if err := decoder.Decode(&prMessages); err != nil {
190 respondWithError(w, http.StatusBadRequest, "Invalid request payload")
195 findAndUpdatePolicy(prMessages)
196 respondWithJSON(w, http.StatusOK, map[string]string{"status": "200"})
199 func findAndUpdatePolicy(rRMPolicyRatio []messages.RRMPolicyRatio) {
200 for _, policy := range rRMPolicyRatio {
201 for _, entry := range data {
202 if entry.policyRatioId == policy.Id {
203 entry.policyDedicatedRatio = policy.RRMPolicyDedicatedRatio
204 log.Info("New value for Policy dedicated ratio: ", entry.policyDedicatedRatio)
205 if entry.metricValue > THRESHOLD_TPUT {
206 entry.metricValue = rand.Intn(THRESHOLD_TPUT)
208 messagesToSend = append(messagesToSend, generateMeasurementEntry(entry))
214 func respondWithError(w http.ResponseWriter, code int, message string) {
215 respondWithJSON(w, code, map[string]string{"error": message})
218 func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
219 response, _ := json.Marshal(payload)
221 w.Header().Set("Content-Type", "application/json")
226 func sendDmaapMRMessages(w http.ResponseWriter, r *http.Request) {
227 log.Info("Send Dmaap messages")
228 entry := data[rand.Intn(5)]
230 maxTput := THRESHOLD_TPUT + 100
231 randomTput := rand.Intn(maxTput-THRESHOLD_TPUT+1) + THRESHOLD_TPUT
232 if randomTput%3 == 0 {
233 log.Info("Using tput value higher than THRESHOLD_TPUT ", randomTput)
234 entry.metricValue = randomTput
237 messagesToSend = append(messagesToSend, generateMeasurementEntry(entry))
239 message := messages.StdDefinedMessage{
240 Event: messages.Event{
241 CommonEventHeader: messages.CommonEventHeader{
242 Domain: "stndDefined",
243 StndDefinedNamespace: "o-ran-sc-du-hello-world-pm-streaming-oas3",
245 StndDefinedFields: messages.StndDefinedFields{
246 StndDefinedFieldsVersion: "1.0",
247 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",
250 Measurements: messagesToSend,
256 time.Sleep(time.Duration(rand.Intn(3)) * time.Second)
257 respondWithJSON(w, http.StatusOK, message)
262 func generateMeasurementEntry(entry *SliceAssuranceInformation) messages.Measurement {
264 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) + "]"
265 meas := messages.Measurement{
267 MeasurementTypeInstanceReference: measurementTypeInstanceReference,
268 Value: entry.metricValue,