205f5f6f8a6c50120ba29d90bdf2a5548b4f8cdb
[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.Info("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         Logger.Info("GetSymptomDataParams ...")
108
109         params := SymptomDataParams{}
110         queryParams := req.URL.Query()
111
112         Logger.Info("GetSymptomDataParams: %+v", queryParams)
113
114         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
128 func (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
166 func (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 {
171                 response, _ := json.MarshalIndent(data, "", " ")
172                 w.Write(response)
173         }
174 }
175
176 func (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
197 func (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
202 func (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
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 }