2 ==================================================================================
3 Copyright (c) 2019 AT&T Intellectual Property.
4 Copyright (c) 2019 Nokia
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
10 http://www.apache.org/licenses/LICENSE-2.0
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 ==================================================================================
29 "github.com/gorilla/mux"
30 "github.com/spf13/viper"
32 "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/models"
36 ReadyURL = "/ric/v1/health/ready"
37 AliveURL = "/ric/v1/health/alive"
38 ConfigURL = "/ric/v1/cm/{name}"
39 AppConfigURL = "/ric/v1/config"
46 type StatusCb func() bool
53 func NewRouter() *Router {
55 router: mux.NewRouter().StrictSlash(true),
56 cbMap: make([]StatusCb, 0),
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")
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)
74 respondWithJSON(w, http.StatusServiceUnavailable, nil)
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)
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...)
87 func (r *Router) InjectRoutePrefix(prefix string, handler http.HandlerFunc) *mux.Route {
88 return r.router.PathPrefix(prefix).HandlerFunc(r.serviceChecker(handler))
91 func (r *Router) InjectStatusCb(f StatusCb) {
92 r.cbMap = append(r.cbMap, f)
95 func (r *Router) CheckStatus() (status bool) {
96 if len(r.cbMap) == 0 {
100 for _, f := range r.cbMap {
106 func (r *Router) GetSymptomDataParams(w http.ResponseWriter, req *http.Request) SymptomDataParams {
107 params := SymptomDataParams{}
108 queryParams := req.URL.Query()
110 Logger.Info("GetSymptomDataParams: %+v", queryParams)
112 for p := range queryParams {
114 fmt.Sscanf(p, "%d", ¶ms.Timeout)
117 fmt.Sscanf(p, "%d", ¶ms.FromTime)
120 fmt.Sscanf(p, "%d", ¶ms.ToTime)
126 func (r *Router) CollectDefaultSymptomData(fileName string, data interface{}) string {
127 baseDir := Config.GetString("controls.symptomdata.baseDir")
129 baseDir = "/tmp/xapp/"
132 if err := Util.CreateDir(baseDir); err != nil {
133 Logger.Error("CreateDir failed: %v", err)
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)
144 if b, err := json.MarshalIndent(data, "", " "); err == nil {
145 Util.WriteToFile(baseDir+fileName, string(b))
149 rtPath := os.Getenv("RMR_STASH_RT")
154 input, err := ioutil.ReadFile(rtPath)
156 Logger.Error("ioutil.ReadFile failed: %v", err)
160 Util.WriteToFile(baseDir+"rttable.txt", string(input))
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)
169 response, _ := json.MarshalIndent(data, "", " ")
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")
178 r.SendSymptomDataError(w, req, "Failed to create a tmp file: "+err.Error())
181 defer os.Remove(tmpFile.Name())
183 var fileList []string
184 fileList = Util.FetchFiles(baseDir, fileList)
185 err = Util.ZipFiles(tmpFile, baseDir, fileList)
187 r.SendSymptomDataError(w, req, "Failed to zip the files: "+err.Error())
191 w.Header().Set("Content-Disposition", "attachment; filename="+zipFile)
192 http.ServeFile(w, req, tmpFile.Name())
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)
200 func (r *Router) GetLocalMetrics(port int) (string, error) {
201 resp, err := http.Get(fmt.Sprintf("http://localhost:%d/ric/v1/metrics", port))
203 Logger.Error("GetLocalMetrics: http.Get failed: %v", err)
206 defer resp.Body.Close()
208 metrics, err := ioutil.ReadAll(resp.Body)
210 Logger.Error("GetLocalMetrics: ioutil.ReadAll failed: %v", err)
214 return string(metrics), nil
217 func IsHealthProbeReady() bool {
221 func readyHandler(w http.ResponseWriter, r *http.Request) {
223 respondWithJSON(w, http.StatusOK, nil)
226 func aliveHandler(w http.ResponseWriter, r *http.Request) {
227 respondWithJSON(w, http.StatusOK, nil)
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)
238 body, err := ioutil.ReadAll(r.Body)
240 Logger.Error("ioutil.ReadAll failed: %v", err)
241 respondWithJSON(w, http.StatusInternalServerError, nil)
245 if err := PublishConfigChange(xappName, string(body)); err != nil {
246 respondWithJSON(w, http.StatusInternalServerError, nil)
250 respondWithJSON(w, http.StatusOK, nil)
253 func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
254 w.Header().Set("Content-Type", "application/json")
257 response, _ := json.Marshal(payload)
262 func appconfigHandler(w http.ResponseWriter, r *http.Request) {
264 Logger.Info("Inside appconfigHandler")
266 var appconfig models.XappConfigList
267 var metadata models.ConfigMetadata
268 var xappconfig models.XAppConfig
269 name := viper.GetString("name")
271 metadata.XappName = &name
272 metadata.ConfigType = &configtype
274 configFile, err := os.Open("/opt/ric/config/config-file.json")
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")
281 body, err := ioutil.ReadAll(configFile)
283 defer configFile.Close()
285 xappconfig.Metadata = &metadata
286 xappconfig.Config = string(body)
288 appconfig = append(appconfig, &xappconfig)
290 respondWithJSON(w, http.StatusOK, appconfig)
292 //return appconfig,nil