blob: 508609849afb822e9ccf16166b1fca8448c5748b [file] [log] [blame]
Katri Turunen4b74f012019-08-15 10:49:36 +03001/*
2 * Copyright (c) 2019 AT&T Intellectual Property.
3 * Copyright (c) 2018-2019 Nokia.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
Roni Riska6ffba082019-11-27 10:59:54 +020016 *
17 * This source code is part of the near-RT RIC (RAN Intelligent Controller)
18 * platform project (RICP).
19 *
Katri Turunen4b74f012019-08-15 10:49:36 +030020 */
21
22package main
23
24import (
Katri Turunen412df962019-09-16 08:48:18 +030025 "errors"
Mohamed Abukar67a790a2020-11-05 09:54:19 +020026 "fmt"
Katri Turunen412df962019-09-16 08:48:18 +030027 "io/ioutil"
28 "net"
29 "net/http"
Katri Turunen4b74f012019-08-15 10:49:36 +030030 "os"
Katri Turunen412df962019-09-16 08:48:18 +030031 "time"
32
Katri Turunen4b74f012019-08-15 10:49:36 +030033 mdcloggo "gerrit.o-ran-sc.org/r/com/golog.git"
34)
35
Katri Turunen412df962019-09-16 08:48:18 +030036var appmgrDomain string
37
38const appmgrXAppConfigPath = "/ric/v1/config"
39const appmgrPort = "8080"
40
Roni Riskafc77ebb2019-09-26 08:20:44 +030041// VesMgr contains runtime information of the vesmgr process
Katri Turunen412df962019-09-16 08:48:18 +030042type VesMgr struct {
Roni Riskafc77ebb2019-09-26 08:20:44 +030043 myIPAddress string
44 chXAppSubscriptions chan subscriptionNotification
45 chXAppNotifications chan []byte
46 chSupervision chan chan string
47 chVesagent chan error
48 vesagent cmdRunner
49 httpServer HTTPServer
Katri Turunen412df962019-09-16 08:48:18 +030050}
51
Roni Riskafc77ebb2019-09-26 08:20:44 +030052type subscriptionNotification struct {
Katri Turunen412df962019-09-16 08:48:18 +030053 subscribed bool
54 err error
Roni Riskafc77ebb2019-09-26 08:20:44 +030055 subsID string
Katri Turunen4b74f012019-08-15 10:49:36 +030056}
57
Katri Turunen4b74f012019-08-15 10:49:36 +030058var logger *mdcloggo.MdcLogger
Katri Turunen412df962019-09-16 08:48:18 +030059
Roni Riska364295f2019-09-30 09:39:12 +030060// Version information, which is filled during compilation
61// Version tag of vesmgr container
62var Version string
63
64// Hash of the git commit used in building
65var Hash string
66
Katri Turunen412df962019-09-16 08:48:18 +030067const vesmgrXappNotifPort = "8080"
68const vesmgrXappNotifPath = "/vesmgr_xappnotif/"
69const timeoutPostXAppSubscriptions = 5
Roni Riskafc77ebb2019-09-26 08:20:44 +030070const vespaConfigFile = "/etc/ves-agent/ves-agent.yaml"
Katri Turunen4b74f012019-08-15 10:49:36 +030071
72func init() {
73 logger, _ = mdcloggo.InitLogger("vesmgr")
74}
75
Katri Turunen412df962019-09-16 08:48:18 +030076func getMyIP() (myIP string, retErr error) {
77 addrs, err := net.InterfaceAddrs()
78 if err != nil {
79 logger.Error("net.InterfaceAddrs failed: %s", err.Error())
80 return "", err
81 }
82 for _, addr := range addrs {
83 // check the address type and if it is not a loopback take it
84 if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
85 if ipnet.IP.To4() != nil {
86 logger.Info("My IP Address: %s", ipnet.IP.String())
87 return ipnet.IP.String(), nil
88 }
89 }
90 }
91 return "", nil
92}
Katri Turunen4b74f012019-08-15 10:49:36 +030093
Roni Riskafc77ebb2019-09-26 08:20:44 +030094func createConf(fname string, xappMetrics []byte) {
95 f, err := os.Create(fname)
Katri Turunen4b74f012019-08-15 10:49:36 +030096 if err != nil {
97 logger.Error("Cannot create vespa conf file: %s", err.Error())
Katri Turunen412df962019-09-16 08:48:18 +030098 os.Exit(1)
Katri Turunen4b74f012019-08-15 10:49:36 +030099 }
100 defer f.Close()
101
Katri Turunen412df962019-09-16 08:48:18 +0300102 createVespaConfig(f, xappMetrics)
103 logger.Info("Vespa config created")
Katri Turunen4b74f012019-08-15 10:49:36 +0300104}
105
Roni Riskafc77ebb2019-09-26 08:20:44 +0300106func (vesmgr *VesMgr) subscribeXAppNotifications() {
107 xappNotifURL := "http://" + vesmgr.myIPAddress + ":" + vesmgrXappNotifPort + vesmgrXappNotifPath
108 subsURL := "http://" + appmgrDomain + ":" + appmgrPort + appmgrSubsPath
109 go subscribexAppNotifications(xappNotifURL, vesmgr.chXAppSubscriptions, timeoutPostXAppSubscriptions, subsURL)
110 logger.Info("xApp notifications subscribed from %s", subsURL)
Katri Turunen412df962019-09-16 08:48:18 +0300111}
112
Roni Riskafc77ebb2019-09-26 08:20:44 +0300113// Init initializes the vesmgr
114func (vesmgr *VesMgr) Init(listenPort string) *VesMgr {
Katri Turunen412df962019-09-16 08:48:18 +0300115 logger.Info("vesmgrInit")
Abukar Mohamed267b9092020-03-17 14:00:18 +0000116 logger.Info("version: %s (%s)", Version, Hash)
Roni Riska364295f2019-09-30 09:39:12 +0300117
Katri Turunen412df962019-09-16 08:48:18 +0300118 var err error
119 if vesmgr.myIPAddress, err = getMyIP(); err != nil || vesmgr.myIPAddress == "" {
Mohamed Abukarcbfed3c2020-10-08 09:37:21 +0300120 logger.Error("Cannot get myIPAddress: IP %s", vesmgr.myIPAddress)
Roni Riskafc77ebb2019-09-26 08:20:44 +0300121 panic("Cannot get my IP address")
Katri Turunen412df962019-09-16 08:48:18 +0300122 }
123
124 var ok bool
125 appmgrDomain, ok = os.LookupEnv("VESMGR_APPMGRDOMAIN")
126 if ok {
127 logger.Info("Using appmgrdomain %s", appmgrDomain)
128 } else {
Mohamed Abukar67a790a2020-11-05 09:54:19 +0200129 pltnamespace := os.Getenv("PLT_NAMESPACE")
130 if pltnamespace == "" {
131 pltnamespace = "ricplt"
132 }
133 appmgrDomain = fmt.Sprintf("service-%s-appmgr-http.%s.svc.cluster.local", pltnamespace, pltnamespace)
Katri Turunen412df962019-09-16 08:48:18 +0300134 logger.Info("Using default appmgrdomain %s", appmgrDomain)
135 }
Roni Riskafc77ebb2019-09-26 08:20:44 +0300136 vesmgr.chXAppSubscriptions = make(chan subscriptionNotification)
137 // Create notifications as buffered channel so that
138 // xappmgr does not block if we are stuck somewhere
139 vesmgr.chXAppNotifications = make(chan []byte, 10)
140 vesmgr.chSupervision = make(chan chan string)
141 vesmgr.chVesagent = make(chan error)
142 vesmgr.httpServer = HTTPServer{}
143 vesmgr.httpServer.init(vesmgr.myIPAddress + ":" + listenPort)
144 vesmgr.vesagent = makeRunner("ves-agent", "-i", os.Getenv("VESMGR_HB_INTERVAL"),
145 "-m", os.Getenv("VESMGR_MEAS_INTERVAL"), "--Measurement.Prometheus.Address",
Abukar Mohamed267b9092020-03-17 14:00:18 +0000146 os.Getenv("VESMGR_PROMETHEUS_ADDR"), "--AlertManager.Bind", os.Getenv("VESMGR_ALERTMANAGER_BIND_ADDR"),
147 "--Debug")
Roni Riskafc77ebb2019-09-26 08:20:44 +0300148 return vesmgr
Katri Turunen412df962019-09-16 08:48:18 +0300149}
150
Roni Riskafc77ebb2019-09-26 08:20:44 +0300151func (vesmgr *VesMgr) startVesagent() {
152 vesmgr.vesagent.run(vesmgr.chVesagent)
Katri Turunen4b74f012019-08-15 10:49:36 +0300153}
154
Roni Riskafc77ebb2019-09-26 08:20:44 +0300155func (vesmgr *VesMgr) killVespa() error {
Katri Turunen412df962019-09-16 08:48:18 +0300156 logger.Info("Killing vespa")
Roni Riskafc77ebb2019-09-26 08:20:44 +0300157 err := vesmgr.vesagent.kill()
Katri Turunen412df962019-09-16 08:48:18 +0300158 if err != nil {
159 logger.Error("Cannot kill vespa: %s", err.Error())
Roni Riskafc77ebb2019-09-26 08:20:44 +0300160 return err
Katri Turunen412df962019-09-16 08:48:18 +0300161 }
Roni Riskafc77ebb2019-09-26 08:20:44 +0300162 return <-vesmgr.chVesagent // wait vespa exit
Katri Turunen412df962019-09-16 08:48:18 +0300163}
164
Roni Riskafc77ebb2019-09-26 08:20:44 +0300165func queryXAppsConfig(appmgrURL string, timeout time.Duration) ([]byte, error) {
166 emptyConfig := []byte("{}")
167 logger.Info("query xAppConfig started, url %s", appmgrURL)
168 req, err := http.NewRequest("GET", appmgrURL, nil)
Katri Turunen412df962019-09-16 08:48:18 +0300169 if err != nil {
170 logger.Error("Failed to create a HTTP request: %s", err)
Roni Riskafc77ebb2019-09-26 08:20:44 +0300171 return emptyConfig, err
Katri Turunen412df962019-09-16 08:48:18 +0300172 }
173 req.Header.Set("Content-Type", "application/json")
174 client := &http.Client{}
175 client.Timeout = time.Second * timeout
176 resp, err := client.Do(req)
177 if err != nil {
Roni Riskafc77ebb2019-09-26 08:20:44 +0300178 logger.Error("Query xApp config failed: %s", err)
179 return emptyConfig, err
Katri Turunen412df962019-09-16 08:48:18 +0300180 }
181 defer resp.Body.Close()
182 if resp.StatusCode == http.StatusOK {
183 body, err := ioutil.ReadAll(resp.Body)
184 if err != nil {
Roni Riskafc77ebb2019-09-26 08:20:44 +0300185 logger.Error("Failed to read xApp config body: %s", err)
186 return emptyConfig, err
Katri Turunen412df962019-09-16 08:48:18 +0300187 }
Roni Riskafc77ebb2019-09-26 08:20:44 +0300188 logger.Info("query xAppConfig completed")
Katri Turunen412df962019-09-16 08:48:18 +0300189 return body, nil
Katri Turunen412df962019-09-16 08:48:18 +0300190 }
Roni Riskafc77ebb2019-09-26 08:20:44 +0300191 logger.Error("Error from xApp config query: %s", resp.Status)
192 return emptyConfig, errors.New(resp.Status)
Katri Turunen412df962019-09-16 08:48:18 +0300193}
194
Mohamed Abukar070b3652020-10-22 19:31:01 +0300195func queryConf() (appConfig []byte, err error) {
196 for i := 0; i < 10; i++ {
197 appConfig, err = queryXAppsConfig("http://"+appmgrDomain+":"+appmgrPort+appmgrXAppConfigPath, 10*time.Second)
198 if len(appConfig) > 0 {
199 break
200 }
201 time.Sleep(5 * time.Second)
202 }
203 return appConfig, err
Katri Turunen412df962019-09-16 08:48:18 +0300204}
205
Roni Riskafc77ebb2019-09-26 08:20:44 +0300206func (vesmgr *VesMgr) emptyNotificationsChannel() {
Katri Turunen4b74f012019-08-15 10:49:36 +0300207 for {
Katri Turunen412df962019-09-16 08:48:18 +0300208 select {
Roni Riskafc77ebb2019-09-26 08:20:44 +0300209 case <-vesmgr.chXAppNotifications:
210 // we don't care the content
211 default:
212 return
213 }
214 }
215}
216
217func (vesmgr *VesMgr) servRequest() {
218 select {
219 case supervision := <-vesmgr.chSupervision:
220 logger.Info("vesmgr: supervision")
221 supervision <- "OK"
222 case xAppNotif := <-vesmgr.chXAppNotifications:
223 logger.Info("vesmgr: xApp notification")
224 logger.Info(string(xAppNotif))
225 vesmgr.emptyNotificationsChannel()
226 /*
227 * If xapp config query fails then we cannot create
228 * a new configuration and kill vespa.
229 * In that case we assume that
230 * the situation is fixed when the next
231 * xapp notif comes
232 */
233 xappConfig, err := queryConf()
234 if err == nil {
235 vesmgr.killVespa()
236 createConf(vespaConfigFile, xappConfig)
237 vesmgr.startVesagent()
238 }
239 case err := <-vesmgr.chVesagent:
240 logger.Error("Vesagent exited: " + err.Error())
241 os.Exit(1)
242 }
243}
244
245func (vesmgr *VesMgr) waitSubscriptionLoop() {
246 for {
247 select {
248 case supervision := <-vesmgr.chSupervision:
Katri Turunen412df962019-09-16 08:48:18 +0300249 logger.Info("vesmgr: supervision")
250 supervision <- "OK"
Roni Riskafc77ebb2019-09-26 08:20:44 +0300251 case isSubscribed := <-vesmgr.chXAppSubscriptions:
Katri Turunen412df962019-09-16 08:48:18 +0300252 if isSubscribed.err != nil {
253 logger.Error("Failed to make xApp subscriptions, vesmgr exiting: %s", isSubscribed.err)
254 os.Exit(1)
255 }
Roni Riskafc77ebb2019-09-26 08:20:44 +0300256 return
Katri Turunen412df962019-09-16 08:48:18 +0300257 }
Katri Turunen4b74f012019-08-15 10:49:36 +0300258 }
259}
Roni Riskafc77ebb2019-09-26 08:20:44 +0300260
261// Run the vesmgr process main loop
262func (vesmgr *VesMgr) Run() {
263 logger.Info("vesmgr main loop ready")
Mohamed Abukar070b3652020-10-22 19:31:01 +0300264
Roni Riskafc77ebb2019-09-26 08:20:44 +0300265 vesmgr.httpServer.start(vesmgrXappNotifPath, vesmgr.chXAppNotifications, vesmgr.chSupervision)
Mohamed Abukar070b3652020-10-22 19:31:01 +0300266
Roni Riskafc77ebb2019-09-26 08:20:44 +0300267 vesmgr.subscribeXAppNotifications()
Mohamed Abukar070b3652020-10-22 19:31:01 +0300268
Roni Riskafc77ebb2019-09-26 08:20:44 +0300269 vesmgr.waitSubscriptionLoop()
Mohamed Abukar070b3652020-10-22 19:31:01 +0300270
Roni Riskafc77ebb2019-09-26 08:20:44 +0300271 xappConfig, _ := queryConf()
Mohamed Abukar070b3652020-10-22 19:31:01 +0300272
Roni Riskafc77ebb2019-09-26 08:20:44 +0300273 createConf(vespaConfigFile, xappConfig)
Mohamed Abukar070b3652020-10-22 19:31:01 +0300274
Roni Riskafc77ebb2019-09-26 08:20:44 +0300275 vesmgr.startVesagent()
276 for {
277 vesmgr.servRequest()
278 }
279}