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
71 var startTime time.Time
73 func XappUpTime() time.Duration {
74 return time.Since(startTime)
78 startTime = time.Now()
82 return Rmr != nil && Rmr.IsReady() && SdlStorage != nil && SdlStorage.IsReady()
85 func IsRegistered() bool {
89 func SetReadyCB(cb ReadyCB, params interface{}) {
91 readyCbParams = params
94 func XappReadyCb(params interface{}) {
95 if disableAlarmClient == false {
96 Alarm = NewAlarmClient(viper.GetString("moId"), viper.GetString("name"))
99 readyCb(readyCbParams)
103 func SetShutdownCB(cb ShutdownCB) {
107 func XappShutdownCb() {
108 if err := doDeregister(); err != nil {
109 Logger.Info("xApp deregistration failed: %v, terminating ungracefully!", err)
111 Logger.Info("xApp deregistration successfull!")
114 if shutdownCb != nil {
121 func registerXapp() {
123 time.Sleep(5 * time.Second)
124 if !IsHealthProbeReady() {
125 Logger.Debug("Application='%s' is not ready yet, waiting ...", viper.GetString("name"))
129 Logger.Debug("Application='%s' is now up and ready, continue with registration ...", viper.GetString("name"))
130 if err := doRegister(); err == nil {
132 Logger.Info("Registration done, proceeding with startup ...")
138 func getService(host, service string) string {
139 appnamespace := os.Getenv("APP_NAMESPACE")
140 if appnamespace == "" {
141 appnamespace = DEFAULT_XAPP_NS
144 svc := fmt.Sprintf(service, strings.ToUpper(appnamespace), strings.ToUpper(host))
145 url := strings.Split(os.Getenv(strings.Replace(svc, "-", "_", -1)), "//")
147 Logger.Info("getService: %+v %+v", svc, url)
154 func getPltNamespace(envName, defVal string) string {
155 pltnamespace := os.Getenv("PLT_NAMESPACE")
156 if pltnamespace == "" {
157 pltnamespace = defVal
163 func doPost(pltNs, url string, msg []byte, status int) error {
164 resp, err := http.Post(fmt.Sprintf(url, pltNs, pltNs), "application/json", bytes.NewBuffer(msg))
165 if err != nil || resp == nil || resp.StatusCode != status {
166 logdesc := fmt.Sprintf("http.Post to '%s' failed with", fmt.Sprintf(url, pltNs, pltNs))
168 logdesc += fmt.Sprintf(" status: %d != %d", resp.StatusCode, status)
170 logdesc += fmt.Sprintf(" resp: nil")
173 logdesc += fmt.Sprintf(" err: %s", err.Error())
175 logdesc += fmt.Sprintf(" err: nil")
178 return fmt.Errorf(logdesc)
181 Logger.Info("Post to '%s' done, status:%v", fmt.Sprintf(url, pltNs, pltNs), resp.Status)
186 func doRegister() error {
187 host, _ := os.Hostname()
188 xappname := viper.GetString("name")
189 xappversion := viper.GetString("version")
190 pltNs := getPltNamespace("PLT_NAMESPACE", DEFAULT_PLT_NS)
192 //httpEp, rmrEp := getService(xappname, SERVICE_HTTP), getService(xappname, SERVICE_RMR)
193 httpEp, rmrEp := getService(host, SERVICE_HTTP), getService(host, SERVICE_RMR)
194 if httpEp == "" || rmrEp == "" {
195 Logger.Warn("Couldn't resolve service endpoints: httpEp=%s rmrEp=%s", httpEp, rmrEp)
199 requestBody, err := json.Marshal(map[string]string{
201 "httpEndpoint": httpEp,
202 "rmrEndpoint": rmrEp,
203 "appInstanceName": xappname,
204 "appVersion": xappversion,
205 "configPath": CONFIG_PATH,
209 Logger.Error("json.Marshal failed with error: %v", err)
213 return doPost(pltNs, REGISTER_PATH, requestBody, http.StatusCreated)
216 func doDeregister() error {
217 if !IsHealthProbeReady() {
221 name, _ := os.Hostname()
222 xappname := viper.GetString("name")
223 pltNs := getPltNamespace("PLT_NAMESPACE", DEFAULT_PLT_NS)
225 requestBody, err := json.Marshal(map[string]string{
227 "appInstanceName": xappname,
231 Logger.Error("json.Marshal failed with error: %v", err)
235 return doPost(pltNs, DEREGISTER_PATH, requestBody, http.StatusNoContent)
238 func InstallSignalHandler() {
240 // Signal handlers to really exit program.
241 // shutdownCb can hang until application has
242 // made all needed gracefull shutdown actions
243 // hardcoded limit for shutdown is 20 seconds
245 interrupt := make(chan os.Signal, 1)
246 signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
247 //signal handler function
249 for range interrupt {
250 if atomic.CompareAndSwapInt32(&shutdownFlag, 0, 1) {
254 sentry := make(chan struct{})
263 case <-time.After(time.Duration(timeout) * time.Second):
264 Logger.Info("xapp-frame shutdown callback took more than %d seconds", timeout)
266 Logger.Info("xapp-frame shutdown callback handled within %d seconds", timeout)
271 newCnt := atomic.AddInt32(&shutdownCnt, 1)
272 Logger.Info("xapp-frame shutdown already ongoing. Forced exit counter %d/%d ", newCnt, 5)
274 Logger.Info("xapp-frame shutdown forced exit")
285 // Load xapp configuration
286 Logger = LoadConfig()
288 if viper.IsSet("controls.logger.level") {
289 Logger.SetLevel(viper.GetInt("controls.logger.level"))
291 Logger.SetLevel(viper.GetInt("logger.level"))
294 if !viper.IsSet("controls.logger.noFormat") || !viper.GetBool("controls.logger.noFormat") {
298 Resource = NewRouter()
299 Config = Configurator{}
300 Metric = NewMetrics(viper.GetString("metrics.url"), viper.GetString("metrics.namespace"), Resource.router)
301 Subscription = NewSubscriber(viper.GetString("controls.subscription.host"), viper.GetInt("controls.subscription.timeout"))
302 SdlStorage = NewSdlStorage()
303 Sdl = NewSDLClient(viper.GetString("controls.db.namespace"))
304 Rnib = GetNewRnibClient(SdlStorage.db)
307 InstallSignalHandler()
310 func GetIpAddress() (string, error) {
311 ifname := os.Getenv("INTERFACE_NAME")
312 itf, err := net.InterfaceByName(ifname)
314 return "<nil>", fmt.Errorf("Interface (%s) %w", ifname, err)
316 item, err := itf.Addrs()
318 return "<nil>", fmt.Errorf("Interface (%s) %w", ifname, err)
320 for _, addr := range item {
321 switch v := addr.(type) {
323 if !v.IP.IsLinkLocalUnicast() {
324 return v.IP.String(), nil
328 return "<nil>", fmt.Errorf("Interface (%s) couldn't find ip", ifname)
331 type RunParams struct {
333 DisableAlarmClient bool
336 func RunWithRunParams(c MessageConsumer, params RunParams) {
338 if params.DisableAlarmClient {
339 disableAlarmClient = true
341 disableAlarmClient = false
346 Rmr.SetReadyCB(XappReadyCb, nil)
347 ipString, err := GetIpAddress()
349 Logger.Info("IP address is not able to resolve " + err.Error())
352 if ipString == "<nil>" {
353 host = fmt.Sprintf(":%d", GetPortData("http").Port)
355 host = fmt.Sprintf("[%s]:%d", ipString, GetPortData("http").Port)
357 go http.ListenAndServe(host, Resource.router)
358 Logger.Info(fmt.Sprintf("Xapp started, listening on: %s", host))
361 SdlStorage.TestConnection(viper.GetString("controls.db.namespace"))
368 func RunWithParams(c MessageConsumer, sdlcheck bool) {
369 RunWithRunParams(c, RunParams{SdlCheck: sdlcheck, DisableAlarmClient: false})
372 func Run(c MessageConsumer) {
373 RunWithParams(c, true)