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{}
67 disableAlarmClient bool
70 var startTime time.Time
72 func XappUpTime() time.Duration {
73 return time.Since(startTime)
77 startTime = time.Now()
81 return Rmr != nil && Rmr.IsReady() && SdlStorage != nil && SdlStorage.IsReady()
84 func SetReadyCB(cb ReadyCB, params interface{}) {
86 readyCbParams = params
89 func XappReadyCb(params interface{}) {
90 if disableAlarmClient == false {
91 Alarm = NewAlarmClient(viper.GetString("moId"), viper.GetString("name"))
94 readyCb(readyCbParams)
98 func SetShutdownCB(cb ShutdownCB) {
102 func XappShutdownCb() {
103 if err := doDeregister(); err != nil {
104 Logger.Info("xApp deregistration failed: %v, terminating ungracefully!", err)
106 Logger.Info("xApp deregistration successfull!")
109 if shutdownCb != nil {
114 func registerXapp() {
116 time.Sleep(5 * time.Second)
117 if !IsHealthProbeReady() {
118 Logger.Debug("Application='%s' is not ready yet, waiting ...", viper.GetString("name"))
122 Logger.Debug("Application='%s' is now up and ready, continue with registration ...", viper.GetString("name"))
123 if err := doRegister(); err == nil {
124 Logger.Info("Registration done, proceeding with startup ...")
130 func getService(host, service string) string {
131 appnamespace := os.Getenv("APP_NAMESPACE")
132 if appnamespace == "" {
133 appnamespace = DEFAULT_XAPP_NS
136 svc := fmt.Sprintf(service, strings.ToUpper(appnamespace), strings.ToUpper(host))
137 url := strings.Split(os.Getenv(strings.Replace(svc, "-", "_", -1)), "//")
139 Logger.Info("getService: %+v %+v", svc, url)
146 func getPltNamespace(envName, defVal string) string {
147 pltnamespace := os.Getenv("PLT_NAMESPACE")
148 if pltnamespace == "" {
149 pltnamespace = defVal
155 func doPost(pltNs, url string, msg []byte, status int) error {
156 resp, err := http.Post(fmt.Sprintf(url, pltNs, pltNs), "application/json", bytes.NewBuffer(msg))
157 if err != nil || resp == nil || resp.StatusCode != status {
158 logdesc := fmt.Sprintf("http.Post to '%s' failed with", fmt.Sprintf(url, pltNs, pltNs))
160 logdesc += fmt.Sprintf(" status: %d != %d", resp.StatusCode, status)
162 logdesc += fmt.Sprintf(" resp: nil")
165 logdesc += fmt.Sprintf(" err: %s", err.Error())
167 logdesc += fmt.Sprintf(" err: nil")
170 return fmt.Errorf(logdesc)
173 Logger.Info("Post to '%s' done, status:%v", fmt.Sprintf(url, pltNs, pltNs), resp.Status)
178 func doRegister() error {
179 host, _ := os.Hostname()
180 xappname := viper.GetString("name")
181 xappversion := viper.GetString("version")
182 pltNs := getPltNamespace("PLT_NAMESPACE", DEFAULT_PLT_NS)
184 //httpEp, rmrEp := getService(xappname, SERVICE_HTTP), getService(xappname, SERVICE_RMR)
185 httpEp, rmrEp := getService(host, SERVICE_HTTP), getService(host, SERVICE_RMR)
186 if httpEp == "" || rmrEp == "" {
187 Logger.Warn("Couldn't resolve service endpoints: httpEp=%s rmrEp=%s", httpEp, rmrEp)
191 requestBody, err := json.Marshal(map[string]string{
193 "httpEndpoint": httpEp,
194 "rmrEndpoint": rmrEp,
195 "appInstanceName": xappname,
196 "appVersion": xappversion,
197 "configPath": CONFIG_PATH,
201 Logger.Error("json.Marshal failed with error: %v", err)
205 return doPost(pltNs, REGISTER_PATH, requestBody, http.StatusCreated)
208 func doDeregister() error {
209 if !IsHealthProbeReady() {
213 name, _ := os.Hostname()
214 xappname := viper.GetString("name")
215 pltNs := getPltNamespace("PLT_NAMESPACE", DEFAULT_PLT_NS)
217 requestBody, err := json.Marshal(map[string]string{
219 "appInstanceName": xappname,
223 Logger.Error("json.Marshal failed with error: %v", err)
227 return doPost(pltNs, DEREGISTER_PATH, requestBody, http.StatusNoContent)
230 func InstallSignalHandler() {
232 // Signal handlers to really exit program.
233 // shutdownCb can hang until application has
234 // made all needed gracefull shutdown actions
235 // hardcoded limit for shutdown is 20 seconds
237 interrupt := make(chan os.Signal, 1)
238 signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
239 //signal handler function
241 for range interrupt {
242 if atomic.CompareAndSwapInt32(&shutdownFlag, 0, 1) {
246 sentry := make(chan struct{})
255 case <-time.After(time.Duration(timeout) * time.Second):
256 Logger.Info("xapp-frame shutdown callback took more than %d seconds", timeout)
258 Logger.Info("xapp-frame shutdown callback handled within %d seconds", timeout)
263 newCnt := atomic.AddInt32(&shutdownCnt, 1)
264 Logger.Info("xapp-frame shutdown already ongoing. Forced exit counter %d/%d ", newCnt, 5)
266 Logger.Info("xapp-frame shutdown forced exit")
277 // Load xapp configuration
278 Logger = LoadConfig()
280 if viper.IsSet("controls.logger.level") {
281 Logger.SetLevel(viper.GetInt("controls.logger.level"))
283 Logger.SetLevel(viper.GetInt("logger.level"))
286 if !viper.IsSet("controls.logger.noFormat") || !viper.GetBool("controls.logger.noFormat") {
290 Resource = NewRouter()
291 Config = Configurator{}
292 Metric = NewMetrics(viper.GetString("metrics.url"), viper.GetString("metrics.namespace"), Resource.router)
293 Subscription = NewSubscriber(viper.GetString("controls.subscription.host"), viper.GetInt("controls.subscription.timeout"))
294 SdlStorage = NewSdlStorage()
295 Sdl = NewSDLClient(viper.GetString("controls.db.namespace"))
296 Rnib = GetNewRnibClient(SdlStorage.db)
299 InstallSignalHandler()
302 func GetIpAddress() (string, error) {
303 ifname := os.Getenv("INTERFACE_NAME")
304 itf, err := net.InterfaceByName(ifname)
306 return "<nil>", fmt.Errorf("Interface (%s) %w", ifname, err)
308 item, err := itf.Addrs()
310 return "<nil>", fmt.Errorf("Interface (%s) %w", ifname, err)
312 for _, addr := range item {
313 switch v := addr.(type) {
315 if !v.IP.IsLinkLocalUnicast() {
316 return v.IP.String(), nil
320 return "<nil>", fmt.Errorf("Interface (%s) couldn't find ip", ifname)
323 type RunParams struct {
325 DisableAlarmClient bool
328 func RunWithRunParams(c MessageConsumer, params RunParams) {
330 if params.DisableAlarmClient {
331 disableAlarmClient = true
333 disableAlarmClient = false
338 Rmr.SetReadyCB(XappReadyCb, nil)
339 ipString, err := GetIpAddress()
341 Logger.Info("IP address is not able to resolve " + err.Error())
344 if ipString == "<nil>" {
345 host = fmt.Sprintf(":%d", GetPortData("http").Port)
347 host = fmt.Sprintf("[%s]:%d", ipString, GetPortData("http").Port)
349 go http.ListenAndServe(host, Resource.router)
350 Logger.Info(fmt.Sprintf("Xapp started, listening on: %s", host))
353 SdlStorage.TestConnection(viper.GetString("controls.db.namespace"))
360 func RunWithParams(c MessageConsumer, sdlcheck bool) {
361 RunWithRunParams(c, RunParams{SdlCheck: sdlcheck, DisableAlarmClient: false})
364 func Run(c MessageConsumer) {
365 RunWithParams(c, true)