Add utility module
[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 IsHealthProbeReady() bool {
160         return healthReady
161 }
162
163 func readyHandler(w http.ResponseWriter, r *http.Request) {
164         healthReady = true
165         respondWithJSON(w, http.StatusOK, nil)
166 }
167
168 func aliveHandler(w http.ResponseWriter, r *http.Request) {
169         respondWithJSON(w, http.StatusOK, nil)
170 }
171
172 func configHandler(w http.ResponseWriter, r *http.Request) {
173         xappName := mux.Vars(r)["name"]
174         if xappName == "" || r.Body == nil {
175                 respondWithJSON(w, http.StatusBadRequest, nil)
176                 return
177         }
178         defer r.Body.Close()
179
180         body, err := ioutil.ReadAll(r.Body)
181         if err != nil {
182                 Logger.Error("ioutil.ReadAll failed: %v", err)
183                 respondWithJSON(w, http.StatusInternalServerError, nil)
184                 return
185         }
186
187         if err := PublishConfigChange(xappName, string(body)); err != nil {
188                 respondWithJSON(w, http.StatusInternalServerError, nil)
189                 return
190         }
191
192         respondWithJSON(w, http.StatusOK, nil)
193 }
194
195 func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
196         w.Header().Set("Content-Type", "application/json")
197         w.WriteHeader(code)
198         if payload != nil {
199                 response, _ := json.Marshal(payload)
200                 w.Write(response)
201         }
202 }
203
204 func appconfigHandler(w http.ResponseWriter, r *http.Request) {
205
206         Logger.Info("Inside appconfigHandler")
207
208         var appconfig models.XappConfigList
209         var metadata models.ConfigMetadata
210         var xappconfig models.XAppConfig
211         name := viper.GetString("name")
212         configtype := "json"
213         metadata.XappName = &name
214         metadata.ConfigType = &configtype
215
216         configFile, err := os.Open("/opt/ric/config/config-file.json")
217         if err != nil {
218                 Logger.Error("Cannot open config file: %v", err)
219                 respondWithJSON(w, http.StatusInternalServerError, nil)
220                 // return nil,errors.New("Could Not parse the config file")
221         }
222
223         body, err := ioutil.ReadAll(configFile)
224
225         defer configFile.Close()
226
227         xappconfig.Metadata = &metadata
228         xappconfig.Config = string(body)
229
230         appconfig = append(appconfig, &xappconfig)
231
232         respondWithJSON(w, http.StatusOK, appconfig)
233
234         //return appconfig,nil
235 }