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 ==================================================================================
33 "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/models"
34 "github.com/gorilla/mux"
35 "github.com/prometheus/client_golang/prometheus"
36 "github.com/prometheus/common/expfmt"
37 "github.com/spf13/viper"
41 ReadyURL = "/ric/v1/health/ready"
42 AliveURL = "/ric/v1/health/alive"
43 ConfigURL = "/ric/v1/cm/{name}"
44 AppConfigURL = "/ric/v1/config"
51 type StatusCb func() bool
58 func NewRouter() *Router {
60 router: mux.NewRouter().StrictSlash(true),
61 cbMap: make([]StatusCb, 0),
64 // Inject default routes for health probes
65 r.InjectRoute(ReadyURL, readyHandler, "GET")
66 r.InjectRoute(AliveURL, aliveHandler, "GET")
67 r.InjectRoute(ConfigURL, configHandler, "POST")
68 r.InjectRoute(AppConfigURL, appconfigHandler, "GET")
73 func (r *Router) serviceChecker(inner http.HandlerFunc) http.HandlerFunc {
74 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
75 Logger.Debug("restapi: method=%s url=%s", req.Method, req.URL.RequestURI())
76 if req.URL.RequestURI() == AliveURL || r.CheckStatus() {
77 inner.ServeHTTP(w, req)
79 respondWithJSON(w, http.StatusServiceUnavailable, nil)
84 func (r *Router) InjectRoute(url string, handler http.HandlerFunc, method string) *mux.Route {
85 return r.router.Path(url).HandlerFunc(r.serviceChecker(handler)).Methods(method)
88 func (r *Router) InjectQueryRoute(url string, h http.HandlerFunc, m string, q ...string) *mux.Route {
89 return r.router.Path(url).HandlerFunc(r.serviceChecker(h)).Methods(m).Queries(q...)
92 func (r *Router) InjectRoutePrefix(prefix string, handler http.HandlerFunc) *mux.Route {
93 return r.router.PathPrefix(prefix).HandlerFunc(r.serviceChecker(handler))
96 func (r *Router) InjectStatusCb(f StatusCb) {
97 r.cbMap = append(r.cbMap, f)
100 func (r *Router) CheckStatus() (status bool) {
101 if len(r.cbMap) == 0 {
105 for _, f := range r.cbMap {
111 func (r *Router) GetSymptomDataParams(w http.ResponseWriter, req *http.Request) SymptomDataParams {
112 params := SymptomDataParams{}
113 queryParams := req.URL.Query()
115 Logger.Info("GetSymptomDataParams: %+v", queryParams)
117 for p := range queryParams {
119 fmt.Sscanf(p, "%d", ¶ms.Timeout)
122 fmt.Sscanf(p, "%d", ¶ms.FromTime)
125 fmt.Sscanf(p, "%d", ¶ms.ToTime)
131 func (r *Router) CollectDefaultSymptomData(fileName string, data interface{}) string {
132 baseDir := Config.GetString("controls.symptomdata.baseDir")
134 baseDir = "/tmp/xapp/"
137 if err := Util.CreateDir(baseDir); err != nil {
138 Logger.Error("CreateDir failed: %v", err)
143 // Collect some general information into one file
154 lines = append(lines, fmt.Sprintf("uptime: %02d:%02d:%02d", h, m, s))
156 Util.WriteToFile(baseDir+"information.txt", strings.Join(lines, "\n"))
161 if metrics, err := r.GetLocalMetrics(GetPortData("http").Port); err == nil {
162 if err := Util.WriteToFile(baseDir+"metrics.json", metrics); err != nil {
163 Logger.Error("writeToFile failed for metrics.json: %v", err)
168 // Collect currently used config file
170 cfile := viper.ConfigFileUsed()
171 input, err := ioutil.ReadFile(cfile)
173 Util.WriteToFile(baseDir+path.Base(cfile), string(input))
175 Logger.Error("ioutil.ReadFile failed: %v", err)
179 // Collect environment
181 Util.WriteToFile(baseDir+"environment.txt", strings.Join(os.Environ(), "\n"))
184 // Collect RMR rt table
186 rtPath := os.Getenv("RMR_STASH_RT")
188 input, err = ioutil.ReadFile(rtPath)
190 Util.WriteToFile(baseDir+"rttable.txt", string(input))
192 Logger.Error("ioutil.ReadFile failed: %v", err)
197 // Put data that was provided as argument
200 if b, err := json.MarshalIndent(data, "", " "); err == nil {
201 Util.WriteToFile(baseDir+fileName, string(b))
208 func (r *Router) SendSymptomDataJson(w http.ResponseWriter, req *http.Request, data interface{}, n string) {
209 w.Header().Set("Content-Type", "application/json")
210 w.Header().Set("Content-Disposition", "attachment; filename="+n)
211 w.WriteHeader(http.StatusOK)
213 response, _ := json.MarshalIndent(data, "", " ")
218 func (r *Router) SendSymptomDataFile(w http.ResponseWriter, req *http.Request, baseDir, zipFile string) {
220 var fileList []string
221 fileList = Util.FetchFiles(baseDir, fileList)
222 tmpFileName, err := Util.ZipFilesToTmpFile(baseDir, "symptom", fileList)
224 r.SendSymptomDataError(w, req, err.Error())
227 defer os.Remove(tmpFileName)
229 w.Header().Set("Content-Disposition", "attachment; filename="+zipFile)
230 http.ServeFile(w, req, tmpFileName)
233 func (r *Router) SendSymptomDataError(w http.ResponseWriter, req *http.Request, message string) {
234 w.Header().Set("Content-Disposition", "attachment; filename=error_status.txt")
235 http.Error(w, message, http.StatusInternalServerError)
238 func (r *Router) GetLocalMetrics(port int) (string, error) {
239 buf := &bytes.Buffer{}
240 enc := expfmt.NewEncoder(buf, expfmt.FmtText)
241 vals, err := prometheus.DefaultGatherer.Gather()
243 return fmt.Sprintf("#metrics get error: %s\n", err.Error()), fmt.Errorf("Could get local metrics %w", err)
245 for _, val := range vals {
246 err = enc.Encode(val)
248 buf.WriteString(fmt.Sprintf("#metrics enc err:%s\n", err.Error()))
251 return string(buf.Bytes()), nil
254 func IsHealthProbeReady() bool {
258 func readyHandler(w http.ResponseWriter, r *http.Request) {
260 respondWithJSON(w, http.StatusOK, nil)
263 func aliveHandler(w http.ResponseWriter, r *http.Request) {
264 respondWithJSON(w, http.StatusOK, nil)
267 func configHandler(w http.ResponseWriter, r *http.Request) {
268 xappName := mux.Vars(r)["name"]
269 if xappName == "" || r.Body == nil {
270 respondWithJSON(w, http.StatusBadRequest, nil)
275 body, err := ioutil.ReadAll(r.Body)
277 Logger.Error("ioutil.ReadAll failed: %v", err)
278 respondWithJSON(w, http.StatusInternalServerError, nil)
282 if err := PublishConfigChange(xappName, string(body)); err != nil {
283 respondWithJSON(w, http.StatusInternalServerError, nil)
287 respondWithJSON(w, http.StatusOK, nil)
290 func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
291 w.Header().Set("Content-Type", "application/json")
294 response, _ := json.Marshal(payload)
299 func appconfigHandler(w http.ResponseWriter, r *http.Request) {
301 Logger.Info("Inside appconfigHandler")
303 var appconfig models.XappConfigList
304 var metadata models.ConfigMetadata
305 var xappconfig models.XAppConfig
306 name := viper.GetString("name")
308 metadata.XappName = &name
309 metadata.ConfigType = &configtype
311 configFile, err := os.Open("/opt/ric/config/config-file.json")
313 Logger.Error("Cannot open config file: %v", err)
314 respondWithJSON(w, http.StatusInternalServerError, nil)
315 // return nil,errors.New("Could Not parse the config file")
318 body, err := ioutil.ReadAll(configFile)
320 defer configFile.Close()
322 xappconfig.Metadata = &metadata
323 xappconfig.Config = string(body)
325 appconfig = append(appconfig, &xappconfig)
327 respondWithJSON(w, http.StatusOK, appconfig)
329 //return appconfig,nil