X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?p=nonrtric%2Frapp%2Fransliceassurance.git;a=blobdiff_plain;f=smoversion%2Fstub%2Fsimulator.go;fp=smoversion%2Fstub%2Fsimulator.go;h=aef85d80b84fb43608f35fff08292e7144040201;hp=0000000000000000000000000000000000000000;hb=5542d26d709c996977d3992c58b273ce83e21e16;hpb=71e113b81dbc68694aec281c85c4472640dc8095 diff --git a/smoversion/stub/simulator.go b/smoversion/stub/simulator.go new file mode 100644 index 0000000..aef85d8 --- /dev/null +++ b/smoversion/stub/simulator.go @@ -0,0 +1,377 @@ +// - +// ========================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 = 7000 + +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.MethodPut) + + 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.VES_O_RAN_SC_HELLO_WORLD_PM_STREAMING_OUTPUT/myG/C1", 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"]) + + distUnitFunctions := getDistributedUnitFunctionMessage(vars["O-DU-ID"]) + + respondWithJSON(w, http.StatusOK, distUnitFunctions) +} + +func getDistributedUnitFunctionMessage(oduId string) messages.ORanDuRestConf { + + var policies []messages.RRMPolicyRatio + keys := make(map[string]bool) + for _, entry := range data { + if _, value := keys[entry.policyRatioId]; !value { + keys[entry.policyRatioId] = true + 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: "310", + MobileNetworkCode: "150", + SliceDifferentiator: entry.sd, + SliceServiceType: entry.sst, + }, + }, + } + policies = append(policies, message) + } + } + + var publicLandMobileNetworks []messages.PublicLandMobileNetworks + for _, entry := range data { + publicLandMobileNetwork := messages.PublicLandMobileNetworks{ + MobileCountryCode: "310", + MobileNetworkCode: "150", + SliceDifferentiator: entry.sd, + SliceServiceType: entry.sst, + } + publicLandMobileNetworks = append(publicLandMobileNetworks, publicLandMobileNetwork) + } + + var supportedSnssaiSubcounterInstances []messages.SupportedSnssaiSubcounterInstances + for _, entry := range data { + supportedSnssaiSubcounterInstance := messages.SupportedSnssaiSubcounterInstances{ + SliceDifferentiator: entry.sd, + SliceServiceType: entry.sst, + } + supportedSnssaiSubcounterInstances = append(supportedSnssaiSubcounterInstances, supportedSnssaiSubcounterInstance) + } + + cell := messages.Cell{ + Id: "cell-1", + LocalId: 1, + PhysicalCellId: 1, + BaseStationChannelBandwidth: messages.BaseStationChannelBandwidth{ + Uplink: 83000, + Downlink: 80000, + SupplementaryUplink: 84000, + }, + OperationalState: "enabled", + TrackingAreaCode: 10, + AdmState: "unlocked", + PublicLandMobileNetworks: publicLandMobileNetworks, + SupportedMeasurements: []messages.SupportedMeasurements{ + { + PerformanceMeasurementType: "o-ran-sc-du-hello-world:user-equipment-average-throughput-uplink", + SupportedSnssaiSubcounterInstances: supportedSnssaiSubcounterInstances, + }, + { + PerformanceMeasurementType: "o-ran-sc-du-hello-world:user-equipment-average-throughput-downlink", + SupportedSnssaiSubcounterInstances: supportedSnssaiSubcounterInstances, + }, + }, + TrafficState: "active", + AbsoluteRadioFrequencyChannelNumber: messages.AbsoluteRadioFrequencyChannelNumber{ + Uplink: 14000, + Downlink: 15000, + SupplementaryUplink: 14500, + }, + UserLabel: "cell-1", + SynchronizationSignalBlock: messages.SynchronizationSignalBlock{ + Duration: 2, + FrequencyChannelNumber: 12, + Periodicity: 10, + SubcarrierSpacing: 30, + Offset: 3, + }, + } + + distUnitFunction := messages.DistributedUnitFunction{ + Id: oduId, + OperationalState: "enabled", + AdmState: "unlocked", + UserLabel: oduId, + Cell: []messages.Cell{ + cell, + }, + RRMPolicyRatio: policies, + } + + duRRMPolicyRatio := messages.ORanDuRestConf{ + DistributedUnitFunction: []messages.DistributedUnitFunction{ + distUnitFunction, + }, + } + + return duRRMPolicyRatio +} + +func updateRRMPolicyDedicatedRatio(w http.ResponseWriter, r *http.Request) { + var policies struct { + RRMPolicies []messages.RRMPolicyRatio `json:"radio-resource-management-policy-ratio"` + } + decoder := json.NewDecoder(r.Body) + + if err := decoder.Decode(&policies); err != nil { + respondWithError(w, http.StatusBadRequest, "Invalid request payload") + return + } + defer r.Body.Close() + + prMessages := policies.RRMPolicies + log.Infof("Post request to update RRMPolicyDedicatedRatio %+v", prMessages) + 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 { + log.Infof("update Policy Dedicated Ratio: value for policy %+v\n Old value: %v New value: %v ", policy, entry.policyDedicatedRatio, policy.RRMPolicyDedicatedRatio) + entry.policyDedicatedRatio = policy.RRMPolicyDedicatedRatio + 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 + } + randomEventId := rand.Intn(10000) + messagesToSend = append(messagesToSend, generateMeasurementEntry(entry)) + + message := messages.StdDefinedMessage{ + Event: messages.Event{ + CommonEventHeader: messages.CommonEventHeader{ + Domain: "stndDefined", + EventId: "pm-1_16442" + strconv.Itoa(randomEventId), + EventName: "stndDefined_performanceMeasurementStreaming", + EventType: "performanceMeasurementStreaming", + Sequence: 825, + Priority: "Low", + ReportingEntityId: "", + ReportingEntityName: "O-DU-1122", + SourceId: "", + SourceName: "O-DU-1122", + StartEpochMicrosec: 1644252450000000, + LastEpochMicrosec: 1644252480000000, + NfNamingCode: "SIM-O-DU", + NfVendorName: "O-RAN-SC SIM Project", + StndDefinedNamespace: "o-ran-sc-du-hello-world-pm-streaming-oas3", + TimeZoneOffset: "+00:00", + Version: "4.1", + VesEventListenerVersion: "7.2.1", + }, + 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: "pm-1_1644252450", + StartTime: "2022-02-07T16:47:30.0Z", + AdministrativeState: "unlocked", + OperationalState: "enabled", + UserLabel: "pm", + JobTag: "my-job-tag", + GranularityPeriod: 30, + Measurements: messagesToSend, + }, + }, + }, + } + + fmt.Printf("Sending Dmaap message:\n %+v\n", message) + + messageAsByteArray, _ := json.Marshal(message) + response := [1]string{string(messageAsByteArray)} + + time.Sleep(time.Duration(rand.Intn(3)) * time.Second) + respondWithJSON(w, http.StatusOK, response) + + messagesToSend = nil +} + +func generateMeasurementEntry(entry *SliceAssuranceInformation) messages.Measurement { + + measurementTypeInstanceReference := "/o-ran-sc-du-hello-world:network-function/distributed-unit-functions[id='" + entry.duId + "']/cell[id='" + entry.cellId + "']/supported-measurements[performance-measurement-type='(urn:o-ran-sc:yang:o-ran-sc-du-hello-world?revision=2021-11-23)" + 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 +}