blob: b2ea7adf791757002fdd01ce86eae7c949812dfa [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 {
107 params := SymptomDataParams{}
108 queryParams := req.URL.Query()
109
110 for p := range queryParams {
111 if p == "timeout" {
112 fmt.Sscanf(p, "%d", &params.Timeout)
113 }
114 if p == "fromtime" {
115 fmt.Sscanf(p, "%d", &params.FromTime)
116 }
117 if p == "totime" {
118 fmt.Sscanf(p, "%d", &params.ToTime)
119 }
120 }
121 return params
122}
123
Mohamed Abukar3602bf82021-04-04 18:07:52 +0300124func (r *Router) CollectDefaultSymptomData(fileName string, data interface{}) string {
125 baseDir := Config.GetString("controls.symptomdata.baseDir")
126 if baseDir == "" {
127 baseDir = "/tmp/xapp/"
128 }
129
130 if err := Util.CreateDir(baseDir); err != nil {
131 Logger.Error("CreateDir failed: %v", err)
132 return ""
133 }
134
135 if metrics, err := r.GetLocalMetrics(GetPortData("http").Port); err == nil {
136 if err := Util.WriteToFile(baseDir+"metrics.json", metrics); err != nil {
137 Logger.Error("writeToFile failed for metrics.json: %v", err)
138 }
139 }
140
141 if data != nil {
142 if b, err := json.MarshalIndent(data, "", " "); err == nil {
143 Util.WriteToFile(baseDir+fileName, string(b))
144 }
145 }
146
147 rtPath := os.Getenv("RMR_STASH_RT")
148 if rtPath == "" {
149 return baseDir
150 }
151
152 input, err := ioutil.ReadFile(rtPath)
153 if err != nil {
154 Logger.Error("ioutil.ReadFile failed: %v", err)
155 return baseDir
156 }
157
158 Util.WriteToFile(baseDir+"rttable.txt", string(input))
159 return baseDir
160}
161
Mohamed Abukar0ce03382021-02-24 10:52:25 +0200162func (r *Router) SendSymptomDataJson(w http.ResponseWriter, req *http.Request, data interface{}, n string) {
163 w.Header().Set("Content-Type", "application/json")
164 w.Header().Set("Content-Disposition", "attachment; filename="+n)
165 w.WriteHeader(http.StatusOK)
166 if data != nil {
Mohamed Abukar3602bf82021-04-04 18:07:52 +0300167 response, _ := json.MarshalIndent(data, "", " ")
Mohamed Abukar0ce03382021-02-24 10:52:25 +0200168 w.Write(response)
169 }
170}
171
172func (r *Router) SendSymptomDataFile(w http.ResponseWriter, req *http.Request, baseDir, zipFile string) {
173 // Compress and reply with attachment
174 tmpFile, err := ioutil.TempFile("", "symptom")
175 if err != nil {
176 r.SendSymptomDataError(w, req, "Failed to create a tmp file: "+err.Error())
177 return
178 }
179 defer os.Remove(tmpFile.Name())
180
181 var fileList []string
182 fileList = Util.FetchFiles(baseDir, fileList)
183 err = Util.ZipFiles(tmpFile, baseDir, fileList)
184 if err != nil {
185 r.SendSymptomDataError(w, req, "Failed to zip the files: "+err.Error())
186 return
187 }
188
189 w.Header().Set("Content-Disposition", "attachment; filename="+zipFile)
190 http.ServeFile(w, req, tmpFile.Name())
191}
192
193func (r *Router) SendSymptomDataError(w http.ResponseWriter, req *http.Request, message string) {
194 w.Header().Set("Content-Disposition", "attachment; filename=error_status.txt")
195 http.Error(w, message, http.StatusInternalServerError)
196}
197
Mohamed Abukar060448c2021-03-09 08:27:31 +0200198func (r *Router) GetLocalMetrics(port int) (string, error) {
199 resp, err := http.Get(fmt.Sprintf("http://localhost:%d/ric/v1/metrics", port))
200 if err != nil {
201 return "", err
202 }
203 defer resp.Body.Close()
204
205 metrics, err := ioutil.ReadAll(resp.Body)
206 if err != nil {
207 return "", err
208 }
209
210 return string(metrics), nil
211}
212
Mohamed Abukar256c3042020-12-30 17:48:12 +0200213func IsHealthProbeReady() bool {
214 return healthReady
215}
216
Mohamed Abukar2e78e422019-06-02 11:45:52 +0300217func readyHandler(w http.ResponseWriter, r *http.Request) {
Mohamed Abukar256c3042020-12-30 17:48:12 +0200218 healthReady = true
Mohamed Abukar2e78e422019-06-02 11:45:52 +0300219 respondWithJSON(w, http.StatusOK, nil)
220}
221
222func aliveHandler(w http.ResponseWriter, r *http.Request) {
223 respondWithJSON(w, http.StatusOK, nil)
224}
225
Mohamed Abukarf4668932020-09-15 09:40:07 +0300226func configHandler(w http.ResponseWriter, r *http.Request) {
227 xappName := mux.Vars(r)["name"]
228 if xappName == "" || r.Body == nil {
229 respondWithJSON(w, http.StatusBadRequest, nil)
230 return
231 }
232 defer r.Body.Close()
233
234 body, err := ioutil.ReadAll(r.Body)
235 if err != nil {
236 Logger.Error("ioutil.ReadAll failed: %v", err)
237 respondWithJSON(w, http.StatusInternalServerError, nil)
238 return
239 }
240
241 if err := PublishConfigChange(xappName, string(body)); err != nil {
242 respondWithJSON(w, http.StatusInternalServerError, nil)
243 return
244 }
245
246 respondWithJSON(w, http.StatusOK, nil)
247}
248
Mohamed Abukar2e78e422019-06-02 11:45:52 +0300249func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
250 w.Header().Set("Content-Type", "application/json")
251 w.WriteHeader(code)
252 if payload != nil {
253 response, _ := json.Marshal(payload)
254 w.Write(response)
255 }
256}
wahidw413abf52020-12-15 12:17:09 +0000257
258func appconfigHandler(w http.ResponseWriter, r *http.Request) {
259
260 Logger.Info("Inside appconfigHandler")
261
262 var appconfig models.XappConfigList
263 var metadata models.ConfigMetadata
264 var xappconfig models.XAppConfig
265 name := viper.GetString("name")
266 configtype := "json"
267 metadata.XappName = &name
268 metadata.ConfigType = &configtype
269
270 configFile, err := os.Open("/opt/ric/config/config-file.json")
271 if err != nil {
272 Logger.Error("Cannot open config file: %v", err)
273 respondWithJSON(w, http.StatusInternalServerError, nil)
274 // return nil,errors.New("Could Not parse the config file")
275 }
276
277 body, err := ioutil.ReadAll(configFile)
278
279 defer configFile.Close()
280
281 xappconfig.Metadata = &metadata
282 xappconfig.Config = string(body)
283
284 appconfig = append(appconfig, &xappconfig)
285
286 respondWithJSON(w, http.StatusOK, appconfig)
287
288 //return appconfig,nil
289}