dcf450cbbc39eeea78019fdf94ae3a7f06b83276
[nonrtric.git] / test / usecases / odusliceassurance / goversion / stub / simulator.go
1 // -
2 //   ========================LICENSE_START=================================
3 //   O-RAN-SC
4 //   %%
5 //   Copyright (C) 2021: Nordix Foundation
6 //   %%
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
10 //
11 //        http://www.apache.org/licenses/LICENSE-2.0
12 //
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===================================
19 //
20
21 package main
22
23 import (
24         "encoding/csv"
25         "encoding/json"
26         "flag"
27         "fmt"
28         "math/rand"
29         "net/http"
30         "os"
31         "strconv"
32         "sync"
33         "time"
34
35         "github.com/gorilla/mux"
36         "oransc.org/usecase/oduclosedloop/messages"
37
38         log "github.com/sirupsen/logrus"
39 )
40
41 const THRESHOLD_TPUT int = 700
42
43 type SliceAssuranceInformation struct {
44         duId                 string
45         cellId               string
46         sd                   int
47         sst                  int
48         metricName           string
49         metricValue          int
50         policyRatioId        string
51         policyMaxRatio       int
52         policyMinRatio       int
53         policyDedicatedRatio int
54 }
55
56 var data []*SliceAssuranceInformation
57 var messagesToSend []messages.Measurement
58
59 func loadData() {
60         lines, err := GetCsvFromFile("test-data.csv")
61         if err != nil {
62                 panic(err)
63         }
64         for _, line := range lines {
65                 sai := SliceAssuranceInformation{
66                         duId:                 line[0],
67                         cellId:               line[1],
68                         sd:                   toInt(line[2]),
69                         sst:                  toInt(line[3]),
70                         metricName:           line[4],
71                         metricValue:          toInt(line[5]),
72                         policyRatioId:        line[6],
73                         policyMaxRatio:       toInt(line[7]),
74                         policyMinRatio:       toInt(line[8]),
75                         policyDedicatedRatio: toInt(line[9]),
76                 }
77                 data = append(data, &sai)
78         }
79 }
80
81 func GetCsvFromFile(name string) ([][]string, error) {
82         if csvFile, err := os.Open(name); err == nil {
83                 defer csvFile.Close()
84                 reader := csv.NewReader(csvFile)
85                 reader.FieldsPerRecord = -1
86                 if csvData, err := reader.ReadAll(); err == nil {
87                         return csvData, nil
88                 } else {
89                         return nil, err
90                 }
91         } else {
92                 return nil, err
93         }
94 }
95
96 func toInt(num string) int {
97         res, err := strconv.Atoi(num)
98         if err != nil {
99                 return -1
100         }
101         return res
102 }
103
104 func main() {
105         rand.Seed(time.Now().UnixNano())
106
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")
109         flag.Parse()
110
111         loadData()
112
113         wg := new(sync.WaitGroup)
114         wg.Add(2)
115
116         go func() {
117
118                 r := mux.NewRouter()
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)
122
123                 fmt.Println("Starting SDNR stub on port: ", *portSdnr)
124
125                 log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", *portSdnr), r))
126                 wg.Done()
127         }()
128
129         go func() {
130
131                 r := mux.NewRouter()
132                 r.HandleFunc("/events/unauthenticated.PERFORMANCE_MEASUREMENTS", sendDmaapMRMessages).Methods(http.MethodGet)
133
134                 fmt.Println("Starting DmaapMR stub on port: ", *portDmaapMR)
135
136                 log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", *portDmaapMR), r))
137                 wg.Done()
138         }()
139
140         wg.Wait()
141 }
142
143 func getSdnrResponseMessage(w http.ResponseWriter, r *http.Request) {
144         vars := mux.Vars(r)
145         log.Info("Get messages for RRM Policy Ratio information for O-Du ID ", vars["O-DU-ID"])
146
147         message := messages.ORanDuRestConf{
148                 DistributedUnitFunction: messages.DistributedUnitFunction{
149                         Id:             vars["O-DU-ID"],
150                         RRMPolicyRatio: getPolicyRatioMessage(),
151                 },
152         }
153         respondWithJSON(w, http.StatusOK, message)
154 }
155
156 func getPolicyRatioMessage() []messages.RRMPolicyRatio {
157         var policies []messages.RRMPolicyRatio
158
159         for _, entry := range data {
160
161                 message := messages.RRMPolicyRatio{
162                         Id:                      entry.policyRatioId,
163                         AdmState:                "locked",
164                         UserLabel:               entry.policyRatioId,
165                         RRMPolicyMaxRatio:       entry.policyMaxRatio,
166                         RRMPolicyMinRatio:       entry.policyMinRatio,
167                         RRMPolicyDedicatedRatio: entry.policyDedicatedRatio,
168                         ResourceType:            "prb",
169                         RRMPolicyMembers: []messages.RRMPolicyMember{
170                                 {
171                                         MobileCountryCode:   "046",
172                                         MobileNetworkCode:   "651",
173                                         SliceDifferentiator: entry.sd,
174                                         SliceServiceType:    entry.sst,
175                                 },
176                         },
177                 }
178                 policies = append(policies, message)
179         }
180         return policies
181 }
182
183 func updateRRMPolicyDedicatedRatio(w http.ResponseWriter, r *http.Request) {
184         log.Info("Post request to update RRMPolicyDedicatedRatio")
185
186         var prMessages []messages.RRMPolicyRatio
187         decoder := json.NewDecoder(r.Body)
188
189         if err := decoder.Decode(&prMessages); err != nil {
190                 respondWithError(w, http.StatusBadRequest, "Invalid request payload")
191                 return
192         }
193         defer r.Body.Close()
194
195         findAndUpdatePolicy(prMessages)
196         respondWithJSON(w, http.StatusOK, map[string]string{"status": "200"})
197 }
198
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)
207                                 }
208                                 messagesToSend = append(messagesToSend, generateMeasurementEntry(entry))
209                         }
210                 }
211         }
212 }
213
214 func respondWithError(w http.ResponseWriter, code int, message string) {
215         respondWithJSON(w, code, map[string]string{"error": message})
216 }
217
218 func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
219         response, _ := json.Marshal(payload)
220
221         w.Header().Set("Content-Type", "application/json")
222         w.WriteHeader(code)
223         w.Write(response)
224 }
225
226 func sendDmaapMRMessages(w http.ResponseWriter, r *http.Request) {
227         log.Info("Send Dmaap messages")
228         entry := data[rand.Intn(5)]
229
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
235         }
236
237         messagesToSend = append(messagesToSend, generateMeasurementEntry(entry))
238
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",
244                         },
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",
248                                 Data: messages.Data{
249                                         DataId:       "id",
250                                         Measurements: messagesToSend,
251                                 },
252                         },
253                 },
254         }
255
256         time.Sleep(time.Duration(rand.Intn(3)) * time.Second)
257         respondWithJSON(w, http.StatusOK, message)
258
259         messagesToSend = nil
260 }
261
262 func generateMeasurementEntry(entry *SliceAssuranceInformation) messages.Measurement {
263
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{
266
267                 MeasurementTypeInstanceReference: measurementTypeInstanceReference,
268                 Value:                            entry.metricValue,
269                 Unit:                             "kbit/s",
270         }
271         return meas
272 }