Add interface to read metrics
[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         "github.com/gorilla/mux"
26         "github.com/spf13/viper"
27         "io/ioutil"
28         "net/http"
29         "os"
30
31         "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/models"
32 )
33
34 const (
35         ReadyURL     = "/ric/v1/health/ready"
36         AliveURL     = "/ric/v1/health/alive"
37         ConfigURL    = "/ric/v1/cm/{name}"
38         AppConfigURL = "/ric/v1/config"
39 )
40
41 var (
42         healthReady bool
43 )
44
45 type StatusCb func() bool
46
47 type Router struct {
48         router *mux.Router
49         cbMap  []StatusCb
50 }
51
52 func NewRouter() *Router {
53         r := &Router{
54                 router: mux.NewRouter().StrictSlash(true),
55                 cbMap:  make([]StatusCb, 0),
56         }
57
58         // Inject default routes for health probes
59         r.InjectRoute(ReadyURL, readyHandler, "GET")
60         r.InjectRoute(AliveURL, aliveHandler, "GET")
61         r.InjectRoute(ConfigURL, configHandler, "POST")
62         r.InjectRoute(AppConfigURL, appconfigHandler, "GET")
63
64         return r
65 }
66
67 func (r *Router) serviceChecker(inner http.HandlerFunc) http.HandlerFunc {
68         return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
69                 Logger.Info("restapi: method=%s url=%s", req.Method, req.URL.RequestURI())
70                 if req.URL.RequestURI() == AliveURL || r.CheckStatus() {
71                         inner.ServeHTTP(w, req)
72                 } else {
73                         respondWithJSON(w, http.StatusServiceUnavailable, nil)
74                 }
75         })
76 }
77
78 func (r *Router) InjectRoute(url string, handler http.HandlerFunc, method string) *mux.Route {
79         return r.router.Path(url).HandlerFunc(r.serviceChecker(handler)).Methods(method)
80 }
81
82 func (r *Router) InjectQueryRoute(url string, h http.HandlerFunc, m string, q ...string) *mux.Route {
83         return r.router.Path(url).HandlerFunc(r.serviceChecker(h)).Methods(m).Queries(q...)
84 }
85
86 func (r *Router) InjectRoutePrefix(prefix string, handler http.HandlerFunc) *mux.Route {
87         return r.router.PathPrefix(prefix).HandlerFunc(r.serviceChecker(handler))
88 }
89
90 func (r *Router) InjectStatusCb(f StatusCb) {
91         r.cbMap = append(r.cbMap, f)
92 }
93
94 func (r *Router) CheckStatus() (status bool) {
95         if len(r.cbMap) == 0 {
96                 return true
97         }
98
99         for _, f := range r.cbMap {
100                 status = f()
101         }
102         return
103 }
104
105 func (r *Router) GetSymptomDataParams(w http.ResponseWriter, req *http.Request) SymptomDataParams {
106         params := SymptomDataParams{}
107         queryParams := req.URL.Query()
108
109         for p := range queryParams {
110                 if p == "timeout" {
111                         fmt.Sscanf(p, "%d", &params.Timeout)
112                 }
113                 if p == "fromtime" {
114                         fmt.Sscanf(p, "%d", &params.FromTime)
115                 }
116                 if p == "totime" {
117                         fmt.Sscanf(p, "%d", &params.ToTime)
118                 }
119         }
120         return params
121 }
122
123 func (r *Router) SendSymptomDataJson(w http.ResponseWriter, req *http.Request, data interface{}, n string) {
124         w.Header().Set("Content-Type", "application/json")
125         w.Header().Set("Content-Disposition", "attachment; filename="+n)
126         w.WriteHeader(http.StatusOK)
127         if data != nil {
128                 response, _ := json.Marshal(data)
129                 w.Write(response)
130         }
131 }
132
133 func (r *Router) SendSymptomDataFile(w http.ResponseWriter, req *http.Request, baseDir, zipFile string) {
134         // Compress and reply with attachment
135         tmpFile, err := ioutil.TempFile("", "symptom")
136         if err != nil {
137                 r.SendSymptomDataError(w, req, "Failed to create a tmp file: "+err.Error())
138                 return
139         }
140         defer os.Remove(tmpFile.Name())
141
142         var fileList []string
143         fileList = Util.FetchFiles(baseDir, fileList)
144         err = Util.ZipFiles(tmpFile, baseDir, fileList)
145         if err != nil {
146                 r.SendSymptomDataError(w, req, "Failed to zip the files: "+err.Error())
147                 return
148         }
149
150         w.Header().Set("Content-Disposition", "attachment; filename="+zipFile)
151         http.ServeFile(w, req, tmpFile.Name())
152 }
153
154 func (r *Router) SendSymptomDataError(w http.ResponseWriter, req *http.Request, message string) {
155         w.Header().Set("Content-Disposition", "attachment; filename=error_status.txt")
156         http.Error(w, message, http.StatusInternalServerError)
157 }
158
159 func (r *Router) GetLocalMetrics(port int) (string, error) {
160         resp, err := http.Get(fmt.Sprintf("http://localhost:%d/ric/v1/metrics", port))
161         if err != nil {
162                 return "", err
163         }
164         defer resp.Body.Close()
165
166         metrics, err := ioutil.ReadAll(resp.Body)
167         if err != nil {
168                 return "", err
169         }
170
171         return string(metrics), nil
172 }
173
174 func IsHealthProbeReady() bool {
175         return healthReady
176 }
177
178 func readyHandler(w http.ResponseWriter, r *http.Request) {
179         healthReady = true
180         respondWithJSON(w, http.StatusOK, nil)
181 }
182
183 func aliveHandler(w http.ResponseWriter, r *http.Request) {
184         respondWithJSON(w, http.StatusOK, nil)
185 }
186
187 func configHandler(w http.ResponseWriter, r *http.Request) {
188         xappName := mux.Vars(r)["name"]
189         if xappName == "" || r.Body == nil {
190                 respondWithJSON(w, http.StatusBadRequest, nil)
191                 return
192         }
193         defer r.Body.Close()
194
195         body, err := ioutil.ReadAll(r.Body)
196         if err != nil {
197                 Logger.Error("ioutil.ReadAll failed: %v", err)
198                 respondWithJSON(w, http.StatusInternalServerError, nil)
199                 return
200         }
201
202         if err := PublishConfigChange(xappName, string(body)); err != nil {
203                 respondWithJSON(w, http.StatusInternalServerError, nil)
204                 return
205         }
206
207         respondWithJSON(w, http.StatusOK, nil)
208 }
209
210 func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
211         w.Header().Set("Content-Type", "application/json")
212         w.WriteHeader(code)
213         if payload != nil {
214                 response, _ := json.Marshal(payload)
215                 w.Write(response)
216         }
217 }
218
219 func appconfigHandler(w http.ResponseWriter, r *http.Request) {
220
221         Logger.Info("Inside appconfigHandler")
222
223         var appconfig models.XappConfigList
224         var metadata models.ConfigMetadata
225         var xappconfig models.XAppConfig
226         name := viper.GetString("name")
227         configtype := "json"
228         metadata.XappName = &name
229         metadata.ConfigType = &configtype
230
231         configFile, err := os.Open("/opt/ric/config/config-file.json")
232         if err != nil {
233                 Logger.Error("Cannot open config file: %v", err)
234                 respondWithJSON(w, http.StatusInternalServerError, nil)
235                 // return nil,errors.New("Could Not parse the config file")
236         }
237
238         body, err := ioutil.ReadAll(configFile)
239
240         defer configFile.Close()
241
242         xappconfig.Metadata = &metadata
243         xappconfig.Config = string(body)
244
245         appconfig = append(appconfig, &xappconfig)
246
247         respondWithJSON(w, http.StatusOK, appconfig)
248
249         //return appconfig,nil
250 }