Change log level
[ric-plt/xapp-frame.git] / pkg / xapp / restapi.go
1 /*
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
20 package xapp
21
22 import (
23         "encoding/json"
24         "fmt"
25         "io/ioutil"
26         "net/http"
27         "os"
28
29         "github.com/gorilla/mux"
30         "github.com/spf13/viper"
31
32         "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/models"
33 )
34
35 const (
36         ReadyURL     = "/ric/v1/health/ready"
37         AliveURL     = "/ric/v1/health/alive"
38         ConfigURL    = "/ric/v1/cm/{name}"
39         AppConfigURL = "/ric/v1/config"
40 )
41
42 var (
43         healthReady bool
44 )
45
46 type StatusCb func() bool
47
48 type Router struct {
49         router *mux.Router
50         cbMap  []StatusCb
51 }
52
53 func 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")
62         r.InjectRoute(ConfigURL, configHandler, "POST")
63         r.InjectRoute(AppConfigURL, appconfigHandler, "GET")
64
65         return r
66 }
67
68 func (r *Router) serviceChecker(inner http.HandlerFunc) http.HandlerFunc {
69         return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
70                 Logger.Debug("restapi: method=%s url=%s", req.Method, req.URL.RequestURI())
71                 if req.URL.RequestURI() == AliveURL || r.CheckStatus() {
72                         inner.ServeHTTP(w, req)
73                 } else {
74                         respondWithJSON(w, http.StatusServiceUnavailable, nil)
75                 }
76         })
77 }
78
79 func (r *Router) InjectRoute(url string, handler http.HandlerFunc, method string) *mux.Route {
80         return r.router.Path(url).HandlerFunc(r.serviceChecker(handler)).Methods(method)
81 }
82
83 func (r *Router) InjectQueryRoute(url string, h http.HandlerFunc, m string, q ...string) *mux.Route {
84         return r.router.Path(url).HandlerFunc(r.serviceChecker(h)).Methods(m).Queries(q...)
85 }
86
87 func (r *Router) InjectRoutePrefix(prefix string, handler http.HandlerFunc) *mux.Route {
88         return r.router.PathPrefix(prefix).HandlerFunc(r.serviceChecker(handler))
89 }
90
91 func (r *Router) InjectStatusCb(f StatusCb) {
92         r.cbMap = append(r.cbMap, f)
93 }
94
95 func (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
106 func (r *Router) GetSymptomDataParams(w http.ResponseWriter, req *http.Request) SymptomDataParams {
107         params := SymptomDataParams{}
108         queryParams := req.URL.Query()
109
110         Logger.Info("GetSymptomDataParams: %+v", queryParams)
111
112         for p := range queryParams {
113                 if p == "timeout" {
114                         fmt.Sscanf(p, "%d", &params.Timeout)
115                 }
116                 if p == "fromtime" {
117                         fmt.Sscanf(p, "%d", &params.FromTime)
118                 }
119                 if p == "totime" {
120                         fmt.Sscanf(p, "%d", &params.ToTime)
121                 }
122         }
123         return params
124 }
125
126 func (r *Router) CollectDefaultSymptomData(fileName string, data interface{}) string {
127         baseDir := Config.GetString("controls.symptomdata.baseDir")
128         if baseDir == "" {
129                 baseDir = "/tmp/xapp/"
130         }
131
132         if err := Util.CreateDir(baseDir); err != nil {
133                 Logger.Error("CreateDir failed: %v", err)
134                 return ""
135         }
136
137         if metrics, err := r.GetLocalMetrics(GetPortData("http").Port); err == nil {
138                 if err := Util.WriteToFile(baseDir+"metrics.json", metrics); err != nil {
139                         Logger.Error("writeToFile failed for metrics.json: %v", err)
140                 }
141         }
142
143         if data != nil {
144                 if b, err := json.MarshalIndent(data, "", "  "); err == nil {
145                         Util.WriteToFile(baseDir+fileName, string(b))
146                 }
147         }
148
149         rtPath := os.Getenv("RMR_STASH_RT")
150         if rtPath == "" {
151                 return baseDir
152         }
153
154         input, err := ioutil.ReadFile(rtPath)
155         if err != nil {
156                 Logger.Error("ioutil.ReadFile failed: %v", err)
157                 return baseDir
158         }
159
160         Util.WriteToFile(baseDir+"rttable.txt", string(input))
161         return baseDir
162 }
163
164 func (r *Router) SendSymptomDataJson(w http.ResponseWriter, req *http.Request, data interface{}, n string) {
165         w.Header().Set("Content-Type", "application/json")
166         w.Header().Set("Content-Disposition", "attachment; filename="+n)
167         w.WriteHeader(http.StatusOK)
168         if data != nil {
169                 response, _ := json.MarshalIndent(data, "", " ")
170                 w.Write(response)
171         }
172 }
173
174 func (r *Router) SendSymptomDataFile(w http.ResponseWriter, req *http.Request, baseDir, zipFile string) {
175         // Compress and reply with attachment
176         tmpFile, err := ioutil.TempFile("", "symptom")
177         if err != nil {
178                 r.SendSymptomDataError(w, req, "Failed to create a tmp file: "+err.Error())
179                 return
180         }
181         defer os.Remove(tmpFile.Name())
182
183         var fileList []string
184         fileList = Util.FetchFiles(baseDir, fileList)
185         err = Util.ZipFiles(tmpFile, baseDir, fileList)
186         if err != nil {
187                 r.SendSymptomDataError(w, req, "Failed to zip the files: "+err.Error())
188                 return
189         }
190
191         w.Header().Set("Content-Disposition", "attachment; filename="+zipFile)
192         http.ServeFile(w, req, tmpFile.Name())
193 }
194
195 func (r *Router) SendSymptomDataError(w http.ResponseWriter, req *http.Request, message string) {
196         w.Header().Set("Content-Disposition", "attachment; filename=error_status.txt")
197         http.Error(w, message, http.StatusInternalServerError)
198 }
199
200 func (r *Router) GetLocalMetrics(port int) (string, error) {
201         resp, err := http.Get(fmt.Sprintf("http://localhost:%d/ric/v1/metrics", port))
202         if err != nil {
203                 Logger.Error("GetLocalMetrics: http.Get failed: %v", err)
204                 return "", err
205         }
206         defer resp.Body.Close()
207
208         metrics, err := ioutil.ReadAll(resp.Body)
209         if err != nil {
210                 Logger.Error("GetLocalMetrics: ioutil.ReadAll failed: %v", err)
211                 return "", err
212         }
213
214         return string(metrics), nil
215 }
216
217 func IsHealthProbeReady() bool {
218         return healthReady
219 }
220
221 func readyHandler(w http.ResponseWriter, r *http.Request) {
222         healthReady = true
223         respondWithJSON(w, http.StatusOK, nil)
224 }
225
226 func aliveHandler(w http.ResponseWriter, r *http.Request) {
227         respondWithJSON(w, http.StatusOK, nil)
228 }
229
230 func 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
253 func 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 }
261
262 func 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 }