blob: 06e3bfc2f7aab4ac45163d2ecd4fe2e352f52f3d [file] [log] [blame]
Mohamed Abukar2e78e422019-06-02 11:45:52 +03001/*
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
20package xapp
21
22import (
wahidw413abf52020-12-15 12:17:09 +000023 "bytes"
24 "encoding/json"
Mohamed Abukar2e78e422019-06-02 11:45:52 +030025 "fmt"
26 "github.com/spf13/viper"
27 "net/http"
Juha Hyttinen54066262020-09-16 10:37:28 +030028 "os"
29 "os/signal"
wahidw413abf52020-12-15 12:17:09 +000030 "strings"
Juha Hyttinen54066262020-09-16 10:37:28 +030031 "sync/atomic"
32 "syscall"
Mohamed Abukar0d451c42021-03-16 16:35:40 +020033 "testing"
Juha Hyttinen54066262020-09-16 10:37:28 +030034 "time"
Mohamed Abukar2e78e422019-06-02 11:45:52 +030035)
36
Mohamed Abukar0d451c42021-03-16 16:35:40 +020037// For testing purpose
38var _ = func() bool {
39 testing.Init()
40 return true
41}()
42
Mohamed Abukar192518d2019-06-11 18:06:50 +030043type ReadyCB func(interface{})
Juha Hyttinen54066262020-09-16 10:37:28 +030044type ShutdownCB func()
Mohamed Abukar349a0982019-06-08 18:15:42 +030045
Mohamed Abukar2e78e422019-06-02 11:45:52 +030046var (
47 // XApp is an application instance
Juha Hyttinenf49009a2019-11-26 10:28:14 +020048 Rmr *RMRClient
49 Sdl *SDLClient
50 Rnib *RNIBClient
51 Resource *Router
52 Metric *Metrics
53 Logger *Log
54 Config Configurator
Mohamed Abukar5120ec12020-02-04 11:01:24 +020055 Subscription *Subscriber
Mohamed Abukar8dcedb42020-03-10 13:20:56 +020056 Alarm *AlarmClient
Mohamed Abukar0ce03382021-02-24 10:52:25 +020057 Util *Utils
Juha Hyttinenf49009a2019-11-26 10:28:14 +020058 readyCb ReadyCB
59 readyCbParams interface{}
Juha Hyttinen54066262020-09-16 10:37:28 +030060 shutdownCb ShutdownCB
61 shutdownFlag int32
62 shutdownCnt int32
Mohamed Abukar2e78e422019-06-02 11:45:52 +030063)
64
Mohamed Abukar775722c2019-06-10 16:41:57 +030065func IsReady() bool {
Juha Hyttinenf49009a2019-11-26 10:28:14 +020066 return Rmr != nil && Rmr.IsReady() && Sdl != nil && Sdl.IsReady()
Mohamed Abukar775722c2019-06-10 16:41:57 +030067}
68
Mohamed Abukar192518d2019-06-11 18:06:50 +030069func SetReadyCB(cb ReadyCB, params interface{}) {
Juha Hyttinenf49009a2019-11-26 10:28:14 +020070 readyCb = cb
71 readyCbParams = params
72}
73
Mohamed Abukarb8b191f2020-11-07 11:22:56 +020074func XappReadyCb(params interface{}) {
75 Alarm = NewAlarmClient(viper.GetString("moId"), viper.GetString("name"))
Juha Hyttinenf49009a2019-11-26 10:28:14 +020076 if readyCb != nil {
77 readyCb(readyCbParams)
78 }
Mohamed Abukar775722c2019-06-10 16:41:57 +030079}
80
Juha Hyttinen54066262020-09-16 10:37:28 +030081func SetShutdownCB(cb ShutdownCB) {
82 shutdownCb = cb
83}
84
Mohamed Abukar256c3042020-12-30 17:48:12 +020085func XappShutdownCb() {
86 if err := doDeregister(); err != nil {
87 Logger.Info("xApp deregistration failed: %v, terminating ungracefully!", err)
88 } else {
89 Logger.Info("xApp deregistration successfull!")
90 }
91
92 if shutdownCb != nil {
93 shutdownCb()
94 }
95}
96
97func registerXapp() {
98 for {
99 time.Sleep(5 * time.Second)
100 if !IsHealthProbeReady() {
Mohamed Abukar3602bf82021-04-04 18:07:52 +0300101 Logger.Info("Application='%s' is not ready yet, waiting ...", viper.GetString("name"))
Mohamed Abukar256c3042020-12-30 17:48:12 +0200102 continue
103 }
104
Mohamed Abukar3602bf82021-04-04 18:07:52 +0300105 Logger.Debug("Application='%s' is now up and ready, continue with registration ...", viper.GetString("name"))
Mohamed Abukar256c3042020-12-30 17:48:12 +0200106 if err := doRegister(); err == nil {
Mohamed Abukar3602bf82021-04-04 18:07:52 +0300107 Logger.Info("Registration done, proceeding with startup ...")
Mohamed Abukar256c3042020-12-30 17:48:12 +0200108 break
109 }
110 }
111}
112
113func getService(host, service string) string {
114 appnamespace := os.Getenv("APP_NAMESPACE")
115 if appnamespace == "" {
116 appnamespace = DEFAULT_XAPP_NS
117 }
118
119 svc := fmt.Sprintf(service, strings.ToUpper(appnamespace), strings.ToUpper(host))
120 url := strings.Split(os.Getenv(strings.Replace(svc, "-", "_", -1)), "//")
121 if len(url) > 1 {
122 return url[1]
123 }
124 return ""
125}
126
127func getPltNamespace(envName, defVal string) string {
128 pltnamespace := os.Getenv("PLT_NAMESPACE")
129 if pltnamespace == "" {
130 pltnamespace = defVal
131 }
132
133 return pltnamespace
134}
135
136func doPost(pltNs, url string, msg []byte, status int) error {
137 resp, err := http.Post(fmt.Sprintf(url, pltNs, pltNs), "application/json", bytes.NewBuffer(msg))
138 if err != nil || resp == nil || resp.StatusCode != status {
139 Logger.Info("http.Post to '%s' failed with error: %v", fmt.Sprintf(url, pltNs, pltNs), err)
140 return err
141 }
142 Logger.Info("Post to '%s' done, status:%v", fmt.Sprintf(url, pltNs, pltNs), resp.Status)
143
144 return err
145}
146
147func doRegister() error {
148 host, _ := os.Hostname()
149 xappname := viper.GetString("name")
150 xappversion := viper.GetString("version")
151 pltNs := getPltNamespace("PLT_NAMESPACE", DEFAULT_PLT_NS)
152
153 httpEp, rmrEp := getService(host, SERVICE_HTTP), getService(host, SERVICE_RMR)
154 if httpEp == "" || rmrEp == "" {
155 Logger.Warn("Couldn't resolve service endpoints: httpEp=%s rmrEp=%s", httpEp, rmrEp)
156 return nil
157 }
158
159 requestBody, err := json.Marshal(map[string]string{
160 "appName": host,
161 "httpEndpoint": httpEp,
162 "rmrEndpoint": rmrEp,
163 "appInstanceName": xappname,
164 "appVersion": xappversion,
165 "configPath": CONFIG_PATH,
166 })
167
168 if err != nil {
169 Logger.Error("json.Marshal failed with error: %v", err)
170 return err
171 }
172
173 return doPost(pltNs, REGISTER_PATH, requestBody, http.StatusCreated)
174}
175
176func doDeregister() error {
177 if !IsHealthProbeReady() {
178 return nil
179 }
180
181 name, _ := os.Hostname()
182 xappname := viper.GetString("name")
183 pltNs := getPltNamespace("PLT_NAMESPACE", DEFAULT_PLT_NS)
184
185 requestBody, err := json.Marshal(map[string]string{
186 "appName": name,
187 "appInstanceName": xappname,
188 })
189
190 if err != nil {
191 Logger.Error("json.Marshal failed with error: %v", err)
192 return err
193 }
194
195 return doPost(pltNs, DEREGISTER_PATH, requestBody, http.StatusNoContent)
196}
197
Mohamed Abukarb8b191f2020-11-07 11:22:56 +0200198func InstallSignalHandler() {
Juha Hyttinen54066262020-09-16 10:37:28 +0300199 //
200 // Signal handlers to really exit program.
201 // shutdownCb can hang until application has
202 // made all needed gracefull shutdown actions
203 // hardcoded limit for shutdown is 20 seconds
204 //
205 interrupt := make(chan os.Signal, 1)
206 signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
207 //signal handler function
208 go func() {
Mohamed Abukarb8b191f2020-11-07 11:22:56 +0200209 for range interrupt {
Juha Hyttinen54066262020-09-16 10:37:28 +0300210 if atomic.CompareAndSwapInt32(&shutdownFlag, 0, 1) {
211 // close function
212 go func() {
213 timeout := int(20)
214 sentry := make(chan struct{})
215 defer close(sentry)
216
217 // close callback
218 go func() {
Mohamed Abukar256c3042020-12-30 17:48:12 +0200219 XappShutdownCb()
Juha Hyttinen54066262020-09-16 10:37:28 +0300220 sentry <- struct{}{}
221 }()
222 select {
223 case <-time.After(time.Duration(timeout) * time.Second):
224 Logger.Info("xapp-frame shutdown callback took more than %d seconds", timeout)
225 case <-sentry:
226 Logger.Info("xapp-frame shutdown callback handled within %d seconds", timeout)
227 }
228 os.Exit(0)
229 }()
230 } else {
231 newCnt := atomic.AddInt32(&shutdownCnt, 1)
232 Logger.Info("xapp-frame shutdown already ongoing. Forced exit counter %d/%d ", newCnt, 5)
233 if newCnt >= 5 {
234 Logger.Info("xapp-frame shutdown forced exit")
235 os.Exit(0)
236 }
237 continue
238 }
239
240 }
241 }()
Mohamed Abukar2e78e422019-06-02 11:45:52 +0300242}
243
Mohamed Abukarb8b191f2020-11-07 11:22:56 +0200244func init() {
245 // Load xapp configuration
246 Logger = LoadConfig()
247
Mohamed Abukar827a6412020-11-12 10:02:41 +0200248 if viper.IsSet("controls.logger.level") {
249 Logger.SetLevel(viper.GetInt("controls.logger.level"))
250 } else {
251 Logger.SetLevel(viper.GetInt("logger.level"))
252 }
Mohamed Abukar3602bf82021-04-04 18:07:52 +0300253
254 if !viper.IsSet("controls.logger.noFormat") || !viper.GetBool("controls.logger.noFormat") {
255 Logger.SetFormat(0)
256 }
Mohamed Abukar522d7362020-11-25 09:41:01 +0200257
Mohamed Abukarb8b191f2020-11-07 11:22:56 +0200258 Resource = NewRouter()
259 Config = Configurator{}
260 Metric = NewMetrics(viper.GetString("metrics.url"), viper.GetString("metrics.namespace"), Resource.router)
Mohamed Abukar9ea6c782021-04-07 21:18:55 +0300261 Subscription = NewSubscriber(viper.GetString("controls.subscription.host"), viper.GetInt("controls.subscription.timeout"))
Mohamed Abukar827a6412020-11-12 10:02:41 +0200262 Sdl = NewSDLClient(viper.GetString("controls.db.namespace"))
Mohamed Abukarb8b191f2020-11-07 11:22:56 +0200263 Rnib = NewRNIBClient()
Mohamed Abukar0ce03382021-02-24 10:52:25 +0200264 Util = NewUtils()
Mohamed Abukarb8b191f2020-11-07 11:22:56 +0200265
266 InstallSignalHandler()
267}
268
Juha Hyttinenf49009a2019-11-26 10:28:14 +0200269func RunWithParams(c MessageConsumer, sdlcheck bool) {
270 Rmr = NewRMRClient()
Mohamed Abukar256c3042020-12-30 17:48:12 +0200271
Mohamed Abukarb8b191f2020-11-07 11:22:56 +0200272 Rmr.SetReadyCB(XappReadyCb, nil)
Mohamed Abukar256c3042020-12-30 17:48:12 +0200273
Mohamed Abukarb8b191f2020-11-07 11:22:56 +0200274 host := fmt.Sprintf(":%d", GetPortData("http").Port)
275 go http.ListenAndServe(host, Resource.router)
276 Logger.Info(fmt.Sprintf("Xapp started, listening on: %s", host))
Mohamed Abukar256c3042020-12-30 17:48:12 +0200277
Juha Hyttinenf49009a2019-11-26 10:28:14 +0200278 if sdlcheck {
279 Sdl.TestConnection()
280 }
Mohamed Abukar256c3042020-12-30 17:48:12 +0200281 go registerXapp()
282
Mohamed Abukar2e78e422019-06-02 11:45:52 +0300283 Rmr.Start(c)
284}
Juha Hyttinenf49009a2019-11-26 10:28:14 +0200285
286func Run(c MessageConsumer) {
287 RunWithParams(c, true)
288}