blob: 205f5f6f8a6c50120ba29d90bdf2a5548b4f8cdb [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"
28
Mohamed Abukar4623ad92021-06-16 15:33:47 +000029 "github.com/gorilla/mux"
30 "github.com/spf13/viper"
31
wahidw413abf52020-12-15 12:17:09 +000032 "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/models"
Mohamed Abukar2e78e422019-06-02 11:45:52 +030033)
34
35const (
wahidw413abf52020-12-15 12:17:09 +000036 ReadyURL = "/ric/v1/health/ready"
37 AliveURL = "/ric/v1/health/alive"
38 ConfigURL = "/ric/v1/cm/{name}"
39 AppConfigURL = "/ric/v1/config"
Mohamed Abukar2e78e422019-06-02 11:45:52 +030040)
41
Mohamed Abukar256c3042020-12-30 17:48:12 +020042var (
43 healthReady bool
44)
45
Mohamed Abukar2e78e422019-06-02 11:45:52 +030046type StatusCb func() bool
47
48type Router struct {
49 router *mux.Router
50 cbMap []StatusCb
51}
52
53func NewRouter() *Router {
54 r := &Router{
55 router: mux.NewRouter().StrictSlash(true),
56 cbMap: make([]StatusCb, 0),
57 }
58
59 // Inject default routes for health probes
60 r.InjectRoute(ReadyURL, readyHandler, "GET")
61 r.InjectRoute(AliveURL, aliveHandler, "GET")
Mohamed Abukarf4668932020-09-15 09:40:07 +030062 r.InjectRoute(ConfigURL, configHandler, "POST")
wahidw413abf52020-12-15 12:17:09 +000063 r.InjectRoute(AppConfigURL, appconfigHandler, "GET")
Mohamed Abukar2e78e422019-06-02 11:45:52 +030064
65 return r
66}
67
68func (r *Router) serviceChecker(inner http.HandlerFunc) http.HandlerFunc {
69 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
70 Logger.Info("restapi: method=%s url=%s", req.Method, req.URL.RequestURI())
Mohamed Abukar9cfde092020-04-28 17:11:33 +030071 if req.URL.RequestURI() == AliveURL || r.CheckStatus() {
Mohamed Abukar2e78e422019-06-02 11:45:52 +030072 inner.ServeHTTP(w, req)
73 } else {
74 respondWithJSON(w, http.StatusServiceUnavailable, nil)
75 }
76 })
77}
78
79func (r *Router) InjectRoute(url string, handler http.HandlerFunc, method string) *mux.Route {
Juha Hyttinen5bd72732020-08-14 11:38:06 +030080 return r.router.Path(url).HandlerFunc(r.serviceChecker(handler)).Methods(method)
Mohamed Abukar2e78e422019-06-02 11:45:52 +030081}
82
83func (r *Router) InjectQueryRoute(url string, h http.HandlerFunc, m string, q ...string) *mux.Route {
Juha Hyttinen5bd72732020-08-14 11:38:06 +030084 return r.router.Path(url).HandlerFunc(r.serviceChecker(h)).Methods(m).Queries(q...)
85}
86
87func (r *Router) InjectRoutePrefix(prefix string, handler http.HandlerFunc) *mux.Route {
88 return r.router.PathPrefix(prefix).HandlerFunc(r.serviceChecker(handler))
Mohamed Abukar2e78e422019-06-02 11:45:52 +030089}
90
91func (r *Router) InjectStatusCb(f StatusCb) {
92 r.cbMap = append(r.cbMap, f)
93}
94
95func (r *Router) CheckStatus() (status bool) {
96 if len(r.cbMap) == 0 {
97 return true
98 }
99
100 for _, f := range r.cbMap {
101 status = f()
102 }
103 return
104}
105
Mohamed Abukar0ce03382021-02-24 10:52:25 +0200106func (r *Router) GetSymptomDataParams(w http.ResponseWriter, req *http.Request) SymptomDataParams {
Mohamed Abukardba83df2021-06-17 05:15:08 +0000107 Logger.Info("GetSymptomDataParams ...")
108
Mohamed Abukar0ce03382021-02-24 10:52:25 +0200109 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
139 if metrics, err := r.GetLocalMetrics(GetPortData("http").Port); err == nil {
140 if err := Util.WriteToFile(baseDir+"metrics.json", metrics); err != nil {
141 Logger.Error("writeToFile failed for metrics.json: %v", err)
142 }
143 }
144
145 if data != nil {
146 if b, err := json.MarshalIndent(data, "", " "); err == nil {
147 Util.WriteToFile(baseDir+fileName, string(b))
148 }
149 }
150
151 rtPath := os.Getenv("RMR_STASH_RT")
152 if rtPath == "" {
153 return baseDir
154 }
155
156 input, err := ioutil.ReadFile(rtPath)
157 if err != nil {
158 Logger.Error("ioutil.ReadFile failed: %v", err)
159 return baseDir
160 }
161
162 Util.WriteToFile(baseDir+"rttable.txt", string(input))
163 return baseDir
164}
165
Mohamed Abukar0ce03382021-02-24 10:52:25 +0200166func (r *Router) SendSymptomDataJson(w http.ResponseWriter, req *http.Request, data interface{}, n string) {
167 w.Header().Set("Content-Type", "application/json")
168 w.Header().Set("Content-Disposition", "attachment; filename="+n)
169 w.WriteHeader(http.StatusOK)
170 if data != nil {
Mohamed Abukar3602bf82021-04-04 18:07:52 +0300171 response, _ := json.MarshalIndent(data, "", " ")
Mohamed Abukar0ce03382021-02-24 10:52:25 +0200172 w.Write(response)
173 }
174}
175
176func (r *Router) SendSymptomDataFile(w http.ResponseWriter, req *http.Request, baseDir, zipFile string) {
177 // Compress and reply with attachment
178 tmpFile, err := ioutil.TempFile("", "symptom")
179 if err != nil {
180 r.SendSymptomDataError(w, req, "Failed to create a tmp file: "+err.Error())
181 return
182 }
183 defer os.Remove(tmpFile.Name())
184
185 var fileList []string
186 fileList = Util.FetchFiles(baseDir, fileList)
187 err = Util.ZipFiles(tmpFile, baseDir, fileList)
188 if err != nil {
189 r.SendSymptomDataError(w, req, "Failed to zip the files: "+err.Error())
190 return
191 }
192
193 w.Header().Set("Content-Disposition", "attachment; filename="+zipFile)
194 http.ServeFile(w, req, tmpFile.Name())
195}
196
197func (r *Router) SendSymptomDataError(w http.ResponseWriter, req *http.Request, message string) {
198 w.Header().Set("Content-Disposition", "attachment; filename=error_status.txt")
199 http.Error(w, message, http.StatusInternalServerError)
200}
201
Mohamed Abukar060448c2021-03-09 08:27:31 +0200202func (r *Router) GetLocalMetrics(port int) (string, error) {
203 resp, err := http.Get(fmt.Sprintf("http://localhost:%d/ric/v1/metrics", port))
204 if err != nil {
205 return "", err
206 }
207 defer resp.Body.Close()
208
209 metrics, err := ioutil.ReadAll(resp.Body)
210 if err != nil {
211 return "", err
212 }
213
214 return string(metrics), nil
215}
216
Mohamed Abukar256c3042020-12-30 17:48:12 +0200217func IsHealthProbeReady() bool {
218 return healthReady
219}
220
Mohamed Abukar2e78e422019-06-02 11:45:52 +0300221func readyHandler(w http.ResponseWriter, r *http.Request) {
Mohamed Abukar256c3042020-12-30 17:48:12 +0200222 healthReady = true
Mohamed Abukar2e78e422019-06-02 11:45:52 +0300223 respondWithJSON(w, http.StatusOK, nil)
224}
225
226func aliveHandler(w http.ResponseWriter, r *http.Request) {
227 respondWithJSON(w, http.StatusOK, nil)
228}
229
Mohamed Abukarf4668932020-09-15 09:40:07 +0300230func configHandler(w http.ResponseWriter, r *http.Request) {
231 xappName := mux.Vars(r)["name"]
232 if xappName == "" || r.Body == nil {
233 respondWithJSON(w, http.StatusBadRequest, nil)
234 return
235 }
236 defer r.Body.Close()
237
238 body, err := ioutil.ReadAll(r.Body)
239 if err != nil {
240 Logger.Error("ioutil.ReadAll failed: %v", err)
241 respondWithJSON(w, http.StatusInternalServerError, nil)
242 return
243 }
244
245 if err := PublishConfigChange(xappName, string(body)); err != nil {
246 respondWithJSON(w, http.StatusInternalServerError, nil)
247 return
248 }
249
250 respondWithJSON(w, http.StatusOK, nil)
251}
252
Mohamed Abukar2e78e422019-06-02 11:45:52 +0300253func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
254 w.Header().Set("Content-Type", "application/json")
255 w.WriteHeader(code)
256 if payload != nil {
257 response, _ := json.Marshal(payload)
258 w.Write(response)
259 }
260}
wahidw413abf52020-12-15 12:17:09 +0000261
262func appconfigHandler(w http.ResponseWriter, r *http.Request) {
263
264 Logger.Info("Inside appconfigHandler")
265
266 var appconfig models.XappConfigList
267 var metadata models.ConfigMetadata
268 var xappconfig models.XAppConfig
269 name := viper.GetString("name")
270 configtype := "json"
271 metadata.XappName = &name
272 metadata.ConfigType = &configtype
273
274 configFile, err := os.Open("/opt/ric/config/config-file.json")
275 if err != nil {
276 Logger.Error("Cannot open config file: %v", err)
277 respondWithJSON(w, http.StatusInternalServerError, nil)
278 // return nil,errors.New("Could Not parse the config file")
279 }
280
281 body, err := ioutil.ReadAll(configFile)
282
283 defer configFile.Close()
284
285 xappconfig.Metadata = &metadata
286 xappconfig.Config = string(body)
287
288 appconfig = append(appconfig, &xappconfig)
289
290 respondWithJSON(w, http.StatusOK, appconfig)
291
292 //return appconfig,nil
293}