blob: 00f8aba55617dcaf427a8b7727820a2b61930a10 [file] [log] [blame]
Mohamed Abukar2e78e422019-06-02 11:45:52 +03001/*
2==================================================================================
3 Copyright (c) 2019 AT&T Intellectual Property.
4 Copyright (c) 2019 Nokia
5
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
9
10 http://www.apache.org/licenses/LICENSE-2.0
11
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
17==================================================================================
18*/
19
20package xapp
21
22import (
23 "encoding/json"
Mohamed Abukar0ce03382021-02-24 10:52:25 +020024 "fmt"
Mohamed Abukarf4668932020-09-15 09:40:07 +030025 "io/ioutil"
Mohamed Abukar2e78e422019-06-02 11:45:52 +030026 "net/http"
wahidw413abf52020-12-15 12:17:09 +000027 "os"
Juha Hyttinen78a34e72022-05-19 14:33:33 +030028 "path"
29 "strings"
wahidw413abf52020-12-15 12:17:09 +000030
Mohamed Abukar4623ad92021-06-16 15:33:47 +000031 "github.com/gorilla/mux"
32 "github.com/spf13/viper"
33
wahidw413abf52020-12-15 12:17:09 +000034 "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/models"
Mohamed Abukar2e78e422019-06-02 11:45:52 +030035)
36
37const (
wahidw413abf52020-12-15 12:17:09 +000038 ReadyURL = "/ric/v1/health/ready"
39 AliveURL = "/ric/v1/health/alive"
40 ConfigURL = "/ric/v1/cm/{name}"
41 AppConfigURL = "/ric/v1/config"
Mohamed Abukar2e78e422019-06-02 11:45:52 +030042)
43
Mohamed Abukar256c3042020-12-30 17:48:12 +020044var (
45 healthReady bool
46)
47
Mohamed Abukar2e78e422019-06-02 11:45:52 +030048type StatusCb func() bool
49
50type Router struct {
51 router *mux.Router
52 cbMap []StatusCb
53}
54
55func NewRouter() *Router {
56 r := &Router{
57 router: mux.NewRouter().StrictSlash(true),
58 cbMap: make([]StatusCb, 0),
59 }
60
61 // Inject default routes for health probes
62 r.InjectRoute(ReadyURL, readyHandler, "GET")
63 r.InjectRoute(AliveURL, aliveHandler, "GET")
Mohamed Abukarf4668932020-09-15 09:40:07 +030064 r.InjectRoute(ConfigURL, configHandler, "POST")
wahidw413abf52020-12-15 12:17:09 +000065 r.InjectRoute(AppConfigURL, appconfigHandler, "GET")
Mohamed Abukar2e78e422019-06-02 11:45:52 +030066
67 return r
68}
69
70func (r *Router) serviceChecker(inner http.HandlerFunc) http.HandlerFunc {
71 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
Mohamed Abukar82651fe2022-03-04 06:14:52 +000072 Logger.Debug("restapi: method=%s url=%s", req.Method, req.URL.RequestURI())
Mohamed Abukar9cfde092020-04-28 17:11:33 +030073 if req.URL.RequestURI() == AliveURL || r.CheckStatus() {
Mohamed Abukar2e78e422019-06-02 11:45:52 +030074 inner.ServeHTTP(w, req)
75 } else {
76 respondWithJSON(w, http.StatusServiceUnavailable, nil)
77 }
78 })
79}
80
81func (r *Router) InjectRoute(url string, handler http.HandlerFunc, method string) *mux.Route {
Juha Hyttinen5bd72732020-08-14 11:38:06 +030082 return r.router.Path(url).HandlerFunc(r.serviceChecker(handler)).Methods(method)
Mohamed Abukar2e78e422019-06-02 11:45:52 +030083}
84
85func (r *Router) InjectQueryRoute(url string, h http.HandlerFunc, m string, q ...string) *mux.Route {
Juha Hyttinen5bd72732020-08-14 11:38:06 +030086 return r.router.Path(url).HandlerFunc(r.serviceChecker(h)).Methods(m).Queries(q...)
87}
88
89func (r *Router) InjectRoutePrefix(prefix string, handler http.HandlerFunc) *mux.Route {
90 return r.router.PathPrefix(prefix).HandlerFunc(r.serviceChecker(handler))
Mohamed Abukar2e78e422019-06-02 11:45:52 +030091}
92
93func (r *Router) InjectStatusCb(f StatusCb) {
94 r.cbMap = append(r.cbMap, f)
95}
96
97func (r *Router) CheckStatus() (status bool) {
98 if len(r.cbMap) == 0 {
99 return true
100 }
101
102 for _, f := range r.cbMap {
103 status = f()
104 }
105 return
106}
107
Mohamed Abukar0ce03382021-02-24 10:52:25 +0200108func (r *Router) GetSymptomDataParams(w http.ResponseWriter, req *http.Request) SymptomDataParams {
109 params := SymptomDataParams{}
110 queryParams := req.URL.Query()
111
Mohamed Abukardba83df2021-06-17 05:15:08 +0000112 Logger.Info("GetSymptomDataParams: %+v", queryParams)
113
Mohamed Abukar0ce03382021-02-24 10:52:25 +0200114 for p := range queryParams {
115 if p == "timeout" {
116 fmt.Sscanf(p, "%d", &params.Timeout)
117 }
118 if p == "fromtime" {
119 fmt.Sscanf(p, "%d", &params.FromTime)
120 }
121 if p == "totime" {
122 fmt.Sscanf(p, "%d", &params.ToTime)
123 }
124 }
125 return params
126}
127
Mohamed Abukar3602bf82021-04-04 18:07:52 +0300128func (r *Router) CollectDefaultSymptomData(fileName string, data interface{}) string {
129 baseDir := Config.GetString("controls.symptomdata.baseDir")
130 if baseDir == "" {
131 baseDir = "/tmp/xapp/"
132 }
133
134 if err := Util.CreateDir(baseDir); err != nil {
135 Logger.Error("CreateDir failed: %v", err)
136 return ""
137 }
138
Juha Hyttinen78a34e72022-05-19 14:33:33 +0300139 //
Mohamed Abukar3602bf82021-04-04 18:07:52 +0300140 if metrics, err := r.GetLocalMetrics(GetPortData("http").Port); err == nil {
141 if err := Util.WriteToFile(baseDir+"metrics.json", metrics); err != nil {
142 Logger.Error("writeToFile failed for metrics.json: %v", err)
143 }
144 }
145
Juha Hyttinen78a34e72022-05-19 14:33:33 +0300146 //
Mohamed Abukar3602bf82021-04-04 18:07:52 +0300147 if data != nil {
148 if b, err := json.MarshalIndent(data, "", " "); err == nil {
149 Util.WriteToFile(baseDir+fileName, string(b))
150 }
151 }
152
Juha Hyttinen78a34e72022-05-19 14:33:33 +0300153 //
154 cfile := viper.ConfigFileUsed()
155 input, err := ioutil.ReadFile(cfile)
156 if err == nil {
157 Util.WriteToFile(baseDir+path.Base(cfile), string(input))
158 } else {
Mohamed Abukar3602bf82021-04-04 18:07:52 +0300159 Logger.Error("ioutil.ReadFile failed: %v", err)
Mohamed Abukar3602bf82021-04-04 18:07:52 +0300160 }
161
Juha Hyttinen78a34e72022-05-19 14:33:33 +0300162 //
163 Util.WriteToFile(baseDir+"environment", strings.Join(os.Environ(), "\n"))
164
165 //
166 rtPath := os.Getenv("RMR_STASH_RT")
167 if rtPath != "" {
168 input, err = ioutil.ReadFile(rtPath)
169 if err == nil {
170 Util.WriteToFile(baseDir+"rttable.txt", string(input))
171 } else {
172 Logger.Error("ioutil.ReadFile failed: %v", err)
173 }
174 }
175
Mohamed Abukar3602bf82021-04-04 18:07:52 +0300176 return baseDir
177}
178
Mohamed Abukar0ce03382021-02-24 10:52:25 +0200179func (r *Router) SendSymptomDataJson(w http.ResponseWriter, req *http.Request, data interface{}, n string) {
180 w.Header().Set("Content-Type", "application/json")
181 w.Header().Set("Content-Disposition", "attachment; filename="+n)
182 w.WriteHeader(http.StatusOK)
183 if data != nil {
Mohamed Abukar3602bf82021-04-04 18:07:52 +0300184 response, _ := json.MarshalIndent(data, "", " ")
Mohamed Abukar0ce03382021-02-24 10:52:25 +0200185 w.Write(response)
186 }
187}
188
189func (r *Router) SendSymptomDataFile(w http.ResponseWriter, req *http.Request, baseDir, zipFile string) {
Mohamed Abukar0ce03382021-02-24 10:52:25 +0200190
191 var fileList []string
192 fileList = Util.FetchFiles(baseDir, fileList)
Juha Hyttinen78a34e72022-05-19 14:33:33 +0300193 tmpFileName, err := Util.ZipFilesToTmpFile(baseDir, "symptom", fileList)
Mohamed Abukar0ce03382021-02-24 10:52:25 +0200194 if err != nil {
Juha Hyttinen78a34e72022-05-19 14:33:33 +0300195 r.SendSymptomDataError(w, req, err.Error())
Mohamed Abukar0ce03382021-02-24 10:52:25 +0200196 return
197 }
Juha Hyttinen78a34e72022-05-19 14:33:33 +0300198 defer os.Remove(tmpFileName)
Mohamed Abukar0ce03382021-02-24 10:52:25 +0200199
200 w.Header().Set("Content-Disposition", "attachment; filename="+zipFile)
Juha Hyttinen78a34e72022-05-19 14:33:33 +0300201 http.ServeFile(w, req, tmpFileName)
Mohamed Abukar0ce03382021-02-24 10:52:25 +0200202}
203
204func (r *Router) SendSymptomDataError(w http.ResponseWriter, req *http.Request, message string) {
205 w.Header().Set("Content-Disposition", "attachment; filename=error_status.txt")
206 http.Error(w, message, http.StatusInternalServerError)
207}
208
Mohamed Abukar060448c2021-03-09 08:27:31 +0200209func (r *Router) GetLocalMetrics(port int) (string, error) {
210 resp, err := http.Get(fmt.Sprintf("http://localhost:%d/ric/v1/metrics", port))
211 if err != nil {
Mohamed Abukar82651fe2022-03-04 06:14:52 +0000212 Logger.Error("GetLocalMetrics: http.Get failed: %v", err)
Mohamed Abukar060448c2021-03-09 08:27:31 +0200213 return "", err
214 }
215 defer resp.Body.Close()
216
217 metrics, err := ioutil.ReadAll(resp.Body)
218 if err != nil {
Mohamed Abukar82651fe2022-03-04 06:14:52 +0000219 Logger.Error("GetLocalMetrics: ioutil.ReadAll failed: %v", err)
Mohamed Abukar060448c2021-03-09 08:27:31 +0200220 return "", err
221 }
222
223 return string(metrics), nil
224}
225
Mohamed Abukar256c3042020-12-30 17:48:12 +0200226func IsHealthProbeReady() bool {
227 return healthReady
228}
229
Mohamed Abukar2e78e422019-06-02 11:45:52 +0300230func readyHandler(w http.ResponseWriter, r *http.Request) {
Mohamed Abukar256c3042020-12-30 17:48:12 +0200231 healthReady = true
Mohamed Abukar2e78e422019-06-02 11:45:52 +0300232 respondWithJSON(w, http.StatusOK, nil)
233}
234
235func aliveHandler(w http.ResponseWriter, r *http.Request) {
236 respondWithJSON(w, http.StatusOK, nil)
237}
238
Mohamed Abukarf4668932020-09-15 09:40:07 +0300239func configHandler(w http.ResponseWriter, r *http.Request) {
240 xappName := mux.Vars(r)["name"]
241 if xappName == "" || r.Body == nil {
242 respondWithJSON(w, http.StatusBadRequest, nil)
243 return
244 }
245 defer r.Body.Close()
246
247 body, err := ioutil.ReadAll(r.Body)
248 if err != nil {
249 Logger.Error("ioutil.ReadAll failed: %v", err)
250 respondWithJSON(w, http.StatusInternalServerError, nil)
251 return
252 }
253
254 if err := PublishConfigChange(xappName, string(body)); err != nil {
255 respondWithJSON(w, http.StatusInternalServerError, nil)
256 return
257 }
258
259 respondWithJSON(w, http.StatusOK, nil)
260}
261
Mohamed Abukar2e78e422019-06-02 11:45:52 +0300262func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
263 w.Header().Set("Content-Type", "application/json")
264 w.WriteHeader(code)
265 if payload != nil {
266 response, _ := json.Marshal(payload)
267 w.Write(response)
268 }
269}
wahidw413abf52020-12-15 12:17:09 +0000270
271func appconfigHandler(w http.ResponseWriter, r *http.Request) {
272
273 Logger.Info("Inside appconfigHandler")
274
275 var appconfig models.XappConfigList
276 var metadata models.ConfigMetadata
277 var xappconfig models.XAppConfig
278 name := viper.GetString("name")
279 configtype := "json"
280 metadata.XappName = &name
281 metadata.ConfigType = &configtype
282
283 configFile, err := os.Open("/opt/ric/config/config-file.json")
284 if err != nil {
285 Logger.Error("Cannot open config file: %v", err)
286 respondWithJSON(w, http.StatusInternalServerError, nil)
287 // return nil,errors.New("Could Not parse the config file")
288 }
289
290 body, err := ioutil.ReadAll(configFile)
291
292 defer configFile.Close()
293
294 xappconfig.Metadata = &metadata
295 xappconfig.Config = string(body)
296
297 appconfig = append(appconfig, &xappconfig)
298
299 respondWithJSON(w, http.StatusOK, appconfig)
300
301 //return appconfig,nil
302}