blob: a5ccedfb9cc4351b27fb2a3cba1d24db1c813472 [file] [log] [blame]
elinuxhenrikc78feea2022-04-07 17:01:47 +02001// -
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
21package main
22
23import (
24 "crypto/tls"
25 "encoding/json"
26 "fmt"
27 "net/http"
28 "os"
29 "os/signal"
30 "syscall"
31
32 "github.com/gorilla/mux"
33 log "github.com/sirupsen/logrus"
34 "oransc.org/usecase/oruclosedloop/internal/config"
35 "oransc.org/usecase/oruclosedloop/internal/linkfailure"
36 "oransc.org/usecase/oruclosedloop/internal/repository"
37 "oransc.org/usecase/oruclosedloop/internal/restclient"
38)
39
40const jobId = "14e7bb84-a44d-44c1-90b7-6995a92ad43c"
elinuxhenrikc7847c92022-06-02 14:48:38 +020041
elinuxhenrikc78feea2022-04-07 17:01:47 +020042var job_definition interface{}
43
44var jobRegistrationInfo = struct {
45 InfoTypeID string `json:"info_type_id"`
46 JobResultURI string `json:"job_result_uri"`
47 JobOwner string `json:"job_owner"`
48 JobDefinition interface{} `json:"job_definition"`
49}{
50 InfoTypeID: "STD_Fault_Messages",
51 JobResultURI: "",
52 JobOwner: "O-RU Closed Loop Usecase",
53 JobDefinition: job_definition,
54}
55
56var client restclient.HTTPClient
57var configuration *config.Config
58var linkfailureConfig linkfailure.Configuration
59var lookupService repository.LookupService
60var consumerPort string
61var started bool
62
63func init() {
64 doInit()
65}
66
67func doInit() {
68 configuration = config.New()
69
70 log.SetLevel(configuration.LogLevel)
71 log.Debug("Using configuration: ", configuration)
72
73 consumerPort = fmt.Sprint(configuration.ConsumerPort)
74 jobRegistrationInfo.JobResultURI = configuration.ConsumerHost + ":" + consumerPort
75
76 linkfailureConfig = linkfailure.Configuration{
77 SDNRAddress: configuration.SDNRAddress,
78 SDNRUser: configuration.SDNRUser,
79 SDNRPassword: configuration.SDNPassword,
80 }
81}
82
83func main() {
84 if err := validateConfiguration(configuration); err != nil {
85 log.Fatalf("Unable to start consumer due to configuration error: %v", err)
86 }
87
88 csvFileHelper := repository.NewCsvFileHelperImpl()
89 if initErr := initializeLookupService(csvFileHelper, configuration.ORUToODUMapFile); initErr != nil {
90 log.Fatalf("Unable to create LookupService due to inability to get O-RU-ID to O-DU-ID map. Cause: %v", initErr)
91 }
92
93 var cert tls.Certificate
94 if c, err := restclient.CreateClientCertificate(configuration.ConsumerCertPath, configuration.ConsumerKeyPath); err == nil {
95 cert = c
96 } else {
elinuxhenrikc7847c92022-06-02 14:48:38 +020097 log.Fatalf("Stopping consumer due to error: %v", err)
elinuxhenrikc78feea2022-04-07 17:01:47 +020098 }
99 client = restclient.CreateRetryClient(cert)
100
101 go func() {
102 startServer()
103 os.Exit(1) // If the startServer function exits, it is because there has been a failure in the server, so we exit.
104 }()
105
106 go func() {
107 deleteOnShutdown(make(chan os.Signal, 1))
108 os.Exit(0)
109 }()
110
111 keepConsumerAlive()
112}
113
114func validateConfiguration(configuration *config.Config) error {
115 if configuration.ConsumerHost == "" || configuration.ConsumerPort == 0 {
116 return fmt.Errorf("consumer host and port must be provided")
117 }
118
119 if configuration.ConsumerCertPath == "" || configuration.ConsumerKeyPath == "" {
120 return fmt.Errorf("missing CONSUMER_CERT and/or CONSUMER_KEY")
121 }
122
123 return nil
124}
125
126func initializeLookupService(csvFileHelper repository.CsvFileHelper, csvFile string) error {
127 lookupService = repository.NewLookupServiceImpl(csvFileHelper, csvFile)
128 return lookupService.Init()
129}
130
131func getRouter() *mux.Router {
132 messageHandler := linkfailure.NewLinkFailureHandler(lookupService, linkfailureConfig, client)
133
134 r := mux.NewRouter()
135 r.HandleFunc("/", messageHandler.MessagesHandler).Methods(http.MethodPost).Name("messageHandler")
136 r.HandleFunc("/status", statusHandler).Methods(http.MethodGet).Name("status")
137 r.HandleFunc("/admin/start", startHandler).Methods(http.MethodPost).Name("start")
138 r.HandleFunc("/admin/stop", stopHandler).Methods(http.MethodPost).Name("stop")
139
140 return r
141}
142
143func startServer() {
144 var err error
145 if restclient.IsUrlSecure(configuration.ConsumerHost) {
146 err = http.ListenAndServeTLS(fmt.Sprintf(":%v", configuration.ConsumerPort), configuration.ConsumerCertPath, configuration.ConsumerKeyPath, getRouter())
147 } else {
148 err = http.ListenAndServe(fmt.Sprintf(":%v", configuration.ConsumerPort), getRouter())
149 }
150 if err != nil {
151 log.Errorf("Server stopped unintentionally due to: %v. Deleteing job.", err)
152 if deleteErr := deleteJob(); deleteErr != nil {
153 log.Errorf("Unable to delete consumer job due to: %v. Please remove job %v manually.", deleteErr, jobId)
154 }
155 }
156}
157
158func keepConsumerAlive() {
159 forever := make(chan int)
160 <-forever
161}
162
163func startHandler(w http.ResponseWriter, r *http.Request) {
164 body, _ := json.Marshal(jobRegistrationInfo)
165 putErr := restclient.PutWithoutAuth(configuration.InfoCoordinatorAddress+"/data-consumer/v1/info-jobs/"+jobId, body, client)
166 if putErr != nil {
167 http.Error(w, fmt.Sprintf("Unable to register consumer job due to: %v.", putErr), http.StatusBadRequest)
168 return
169 }
170 log.Debug("Registered job.")
171 started = true
172}
173
174func stopHandler(w http.ResponseWriter, r *http.Request) {
175 deleteErr := deleteJob()
176 if deleteErr != nil {
177 http.Error(w, fmt.Sprintf("Unable to delete consumer job due to: %v. Please remove job %v manually.", deleteErr, jobId), http.StatusBadRequest)
178 return
179 }
180 log.Debug("Deleted job.")
181 started = false
182}
183
184func statusHandler(w http.ResponseWriter, r *http.Request) {
185 runStatus := "started"
186 if !started {
187 runStatus = "stopped"
188 }
189 fmt.Fprintf(w, `{"status": "%v"}`, runStatus)
190}
191
192func deleteOnShutdown(s chan os.Signal) {
193 signal.Notify(s, os.Interrupt)
194 signal.Notify(s, syscall.SIGTERM)
195 <-s
196 log.Info("Shutting down gracefully.")
197 if err := deleteJob(); err != nil {
198 log.Errorf("Unable to delete job on shutdown due to: %v. Please remove job %v manually.", err, jobId)
199 }
200}
201
202func deleteJob() error {
203 return restclient.Delete(configuration.InfoCoordinatorAddress+"/data-consumer/v1/info-jobs/"+jobId, client)
204}