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 ==================================================================================
36 "github.com/spf13/viper"
39 // For testing purpose go version 1.13 ->
46 type ReadyCB func(interface{})
47 type ShutdownCB func()
50 // XApp is an application instance
53 SdlStorage *SDLStorage
59 Subscription *Subscriber
63 readyCbParams interface{}
69 var startTime time.Time
71 func XappUpTime() time.Duration {
72 return time.Since(startTime)
76 startTime = time.Now()
80 return Rmr != nil && Rmr.IsReady() && SdlStorage != nil && SdlStorage.IsReady()
83 func SetReadyCB(cb ReadyCB, params interface{}) {
85 readyCbParams = params
88 func XappReadyCb(params interface{}) {
89 Alarm = NewAlarmClient(viper.GetString("moId"), viper.GetString("name"))
91 readyCb(readyCbParams)
95 func SetShutdownCB(cb ShutdownCB) {
99 func XappShutdownCb() {
100 if err := doDeregister(); err != nil {
101 Logger.Info("xApp deregistration failed: %v, terminating ungracefully!", err)
103 Logger.Info("xApp deregistration successfull!")
106 if shutdownCb != nil {
111 func registerXapp() {
113 time.Sleep(5 * time.Second)
114 if !IsHealthProbeReady() {
115 Logger.Debug("Application='%s' is not ready yet, waiting ...", viper.GetString("name"))
119 Logger.Debug("Application='%s' is now up and ready, continue with registration ...", viper.GetString("name"))
120 if err := doRegister(); err == nil {
121 Logger.Info("Registration done, proceeding with startup ...")
127 func getService(host, service string) string {
128 appnamespace := os.Getenv("APP_NAMESPACE")
129 if appnamespace == "" {
130 appnamespace = DEFAULT_XAPP_NS
133 svc := fmt.Sprintf(service, strings.ToUpper(appnamespace), strings.ToUpper(host))
134 url := strings.Split(os.Getenv(strings.Replace(svc, "-", "_", -1)), "//")
136 Logger.Info("getService: %+v %+v", svc, url)
143 func getPltNamespace(envName, defVal string) string {
144 pltnamespace := os.Getenv("PLT_NAMESPACE")
145 if pltnamespace == "" {
146 pltnamespace = defVal
152 func doPost(pltNs, url string, msg []byte, status int) error {
153 resp, err := http.Post(fmt.Sprintf(url, pltNs, pltNs), "application/json", bytes.NewBuffer(msg))
154 if err != nil || resp == nil || resp.StatusCode != status {
155 Logger.Info("http.Post to '%s' failed with error: %v", fmt.Sprintf(url, pltNs, pltNs), err)
158 Logger.Info("Post to '%s' done, status:%v", fmt.Sprintf(url, pltNs, pltNs), resp.Status)
163 func doRegister() error {
164 host, _ := os.Hostname()
165 xappname := viper.GetString("name")
166 xappversion := viper.GetString("version")
167 pltNs := getPltNamespace("PLT_NAMESPACE", DEFAULT_PLT_NS)
169 //httpEp, rmrEp := getService(xappname, SERVICE_HTTP), getService(xappname, SERVICE_RMR)
170 httpEp, rmrEp := getService(host, SERVICE_HTTP), getService(host, SERVICE_RMR)
171 if httpEp == "" || rmrEp == "" {
172 Logger.Warn("Couldn't resolve service endpoints: httpEp=%s rmrEp=%s", httpEp, rmrEp)
176 requestBody, err := json.Marshal(map[string]string{
178 "httpEndpoint": httpEp,
179 "rmrEndpoint": rmrEp,
180 "appInstanceName": xappname,
181 "appVersion": xappversion,
182 "configPath": CONFIG_PATH,
186 Logger.Error("json.Marshal failed with error: %v", err)
190 return doPost(pltNs, REGISTER_PATH, requestBody, http.StatusCreated)
193 func doDeregister() error {
194 if !IsHealthProbeReady() {
198 name, _ := os.Hostname()
199 xappname := viper.GetString("name")
200 pltNs := getPltNamespace("PLT_NAMESPACE", DEFAULT_PLT_NS)
202 requestBody, err := json.Marshal(map[string]string{
204 "appInstanceName": xappname,
208 Logger.Error("json.Marshal failed with error: %v", err)
212 return doPost(pltNs, DEREGISTER_PATH, requestBody, http.StatusNoContent)
215 func InstallSignalHandler() {
217 // Signal handlers to really exit program.
218 // shutdownCb can hang until application has
219 // made all needed gracefull shutdown actions
220 // hardcoded limit for shutdown is 20 seconds
222 interrupt := make(chan os.Signal, 1)
223 signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
224 //signal handler function
226 for range interrupt {
227 if atomic.CompareAndSwapInt32(&shutdownFlag, 0, 1) {
231 sentry := make(chan struct{})
240 case <-time.After(time.Duration(timeout) * time.Second):
241 Logger.Info("xapp-frame shutdown callback took more than %d seconds", timeout)
243 Logger.Info("xapp-frame shutdown callback handled within %d seconds", timeout)
248 newCnt := atomic.AddInt32(&shutdownCnt, 1)
249 Logger.Info("xapp-frame shutdown already ongoing. Forced exit counter %d/%d ", newCnt, 5)
251 Logger.Info("xapp-frame shutdown forced exit")
262 // Load xapp configuration
263 Logger = LoadConfig()
265 if viper.IsSet("controls.logger.level") {
266 Logger.SetLevel(viper.GetInt("controls.logger.level"))
268 Logger.SetLevel(viper.GetInt("logger.level"))
271 if !viper.IsSet("controls.logger.noFormat") || !viper.GetBool("controls.logger.noFormat") {
275 Resource = NewRouter()
276 Config = Configurator{}
277 Metric = NewMetrics(viper.GetString("metrics.url"), viper.GetString("metrics.namespace"), Resource.router)
278 Subscription = NewSubscriber(viper.GetString("controls.subscription.host"), viper.GetInt("controls.subscription.timeout"))
279 SdlStorage = NewSdlStorage()
280 Sdl = NewSDLClient(viper.GetString("controls.db.namespace"))
281 Rnib = GetNewRnibClient(SdlStorage.db)
284 InstallSignalHandler()
287 func GetIpAddress() (string, error) {
288 ifname := os.Getenv("INTERFACE_NAME")
289 itf, err := net.InterfaceByName(ifname)
291 return "<nil>", fmt.Errorf("Interface (%s) %w", ifname, err)
293 item, err := itf.Addrs()
295 return "<nil>", fmt.Errorf("Interface (%s) %w", ifname, err)
297 for _, addr := range item {
298 switch v := addr.(type) {
300 if !v.IP.IsLinkLocalUnicast() {
301 return v.IP.String(), nil
305 return "<nil>", fmt.Errorf("Interface (%s) couldn't find ip", ifname)
308 func RunWithParams(c MessageConsumer, sdlcheck bool) {
311 Rmr.SetReadyCB(XappReadyCb, nil)
312 ipString, err := GetIpAddress()
314 Logger.Info("IP address is not able to resolve " + err.Error())
317 if ipString == "<nil>" {
318 host = fmt.Sprintf(":%d", GetPortData("http").Port)
320 host = fmt.Sprintf("[%s]:%d", ipString, GetPortData("http").Port)
322 go http.ListenAndServe(host, Resource.router)
323 Logger.Info(fmt.Sprintf("Xapp started, listening on: %s", host))
326 SdlStorage.TestConnection(viper.GetString("controls.db.namespace"))
333 func Run(c MessageConsumer) {
334 RunWithParams(c, true)