blob: 0e775f86e7e52d37115ae70d2dc63f1e4402ed37 [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"
26 "io/ioutil"
27 "net"
28 "net/http"
Katri Turunen4b74f012019-08-15 10:49:36 +030029 "os"
Katri Turunen412df962019-09-16 08:48:18 +030030 "time"
31
Katri Turunen4b74f012019-08-15 10:49:36 +030032 mdcloggo "gerrit.o-ran-sc.org/r/com/golog.git"
33)
34
Katri Turunen412df962019-09-16 08:48:18 +030035var appmgrDomain string
36
37const appmgrXAppConfigPath = "/ric/v1/config"
38const appmgrPort = "8080"
39
Roni Riskafc77ebb2019-09-26 08:20:44 +030040// VesMgr contains runtime information of the vesmgr process
Katri Turunen412df962019-09-16 08:48:18 +030041type VesMgr struct {
Roni Riskafc77ebb2019-09-26 08:20:44 +030042 myIPAddress string
43 chXAppSubscriptions chan subscriptionNotification
44 chXAppNotifications chan []byte
45 chSupervision chan chan string
46 chVesagent chan error
47 vesagent cmdRunner
48 httpServer HTTPServer
Katri Turunen412df962019-09-16 08:48:18 +030049}
50
Roni Riskafc77ebb2019-09-26 08:20:44 +030051type subscriptionNotification struct {
Katri Turunen412df962019-09-16 08:48:18 +030052 subscribed bool
53 err error
Roni Riskafc77ebb2019-09-26 08:20:44 +030054 subsID string
Katri Turunen4b74f012019-08-15 10:49:36 +030055}
56
Katri Turunen4b74f012019-08-15 10:49:36 +030057var logger *mdcloggo.MdcLogger
Katri Turunen412df962019-09-16 08:48:18 +030058
Roni Riska364295f2019-09-30 09:39:12 +030059// Version information, which is filled during compilation
60// Version tag of vesmgr container
61var Version string
62
63// Hash of the git commit used in building
64var Hash string
65
Katri Turunen412df962019-09-16 08:48:18 +030066const vesmgrXappNotifPort = "8080"
67const vesmgrXappNotifPath = "/vesmgr_xappnotif/"
68const timeoutPostXAppSubscriptions = 5
Roni Riskafc77ebb2019-09-26 08:20:44 +030069const vespaConfigFile = "/etc/ves-agent/ves-agent.yaml"
Katri Turunen4b74f012019-08-15 10:49:36 +030070
71func init() {
72 logger, _ = mdcloggo.InitLogger("vesmgr")
73}
74
Katri Turunen412df962019-09-16 08:48:18 +030075func getMyIP() (myIP string, retErr error) {
76 addrs, err := net.InterfaceAddrs()
77 if err != nil {
78 logger.Error("net.InterfaceAddrs failed: %s", err.Error())
79 return "", err
80 }
81 for _, addr := range addrs {
82 // check the address type and if it is not a loopback take it
83 if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
84 if ipnet.IP.To4() != nil {
85 logger.Info("My IP Address: %s", ipnet.IP.String())
86 return ipnet.IP.String(), nil
87 }
88 }
89 }
90 return "", nil
91}
Katri Turunen4b74f012019-08-15 10:49:36 +030092
Roni Riskafc77ebb2019-09-26 08:20:44 +030093func createConf(fname string, xappMetrics []byte) {
94 f, err := os.Create(fname)
Katri Turunen4b74f012019-08-15 10:49:36 +030095 if err != nil {
96 logger.Error("Cannot create vespa conf file: %s", err.Error())
Katri Turunen412df962019-09-16 08:48:18 +030097 os.Exit(1)
Katri Turunen4b74f012019-08-15 10:49:36 +030098 }
99 defer f.Close()
100
Katri Turunen412df962019-09-16 08:48:18 +0300101 createVespaConfig(f, xappMetrics)
102 logger.Info("Vespa config created")
Katri Turunen4b74f012019-08-15 10:49:36 +0300103}
104
Roni Riskafc77ebb2019-09-26 08:20:44 +0300105func (vesmgr *VesMgr) subscribeXAppNotifications() {
106 xappNotifURL := "http://" + vesmgr.myIPAddress + ":" + vesmgrXappNotifPort + vesmgrXappNotifPath
107 subsURL := "http://" + appmgrDomain + ":" + appmgrPort + appmgrSubsPath
108 go subscribexAppNotifications(xappNotifURL, vesmgr.chXAppSubscriptions, timeoutPostXAppSubscriptions, subsURL)
109 logger.Info("xApp notifications subscribed from %s", subsURL)
Katri Turunen412df962019-09-16 08:48:18 +0300110}
111
Roni Riskafc77ebb2019-09-26 08:20:44 +0300112// Init initializes the vesmgr
113func (vesmgr *VesMgr) Init(listenPort string) *VesMgr {
Katri Turunen412df962019-09-16 08:48:18 +0300114 logger.Info("vesmgrInit")
Abukar Mohamed267b9092020-03-17 14:00:18 +0000115 logger.Info("version: %s (%s)", Version, Hash)
Roni Riska364295f2019-09-30 09:39:12 +0300116
Katri Turunen412df962019-09-16 08:48:18 +0300117 var err error
118 if vesmgr.myIPAddress, err = getMyIP(); err != nil || vesmgr.myIPAddress == "" {
119 logger.Error("Cannot get myIPAddress: IP %s, err %s", vesmgr.myIPAddress, err.Error())
Roni Riskafc77ebb2019-09-26 08:20:44 +0300120 panic("Cannot get my IP address")
Katri Turunen412df962019-09-16 08:48:18 +0300121 }
122
123 var ok bool
124 appmgrDomain, ok = os.LookupEnv("VESMGR_APPMGRDOMAIN")
125 if ok {
126 logger.Info("Using appmgrdomain %s", appmgrDomain)
127 } else {
128 appmgrDomain = "service-ricplt-appmgr-http.ricplt.svc.cluster.local"
129 logger.Info("Using default appmgrdomain %s", appmgrDomain)
130 }
Roni Riskafc77ebb2019-09-26 08:20:44 +0300131 vesmgr.chXAppSubscriptions = make(chan subscriptionNotification)
132 // Create notifications as buffered channel so that
133 // xappmgr does not block if we are stuck somewhere
134 vesmgr.chXAppNotifications = make(chan []byte, 10)
135 vesmgr.chSupervision = make(chan chan string)
136 vesmgr.chVesagent = make(chan error)
137 vesmgr.httpServer = HTTPServer{}
138 vesmgr.httpServer.init(vesmgr.myIPAddress + ":" + listenPort)
139 vesmgr.vesagent = makeRunner("ves-agent", "-i", os.Getenv("VESMGR_HB_INTERVAL"),
140 "-m", os.Getenv("VESMGR_MEAS_INTERVAL"), "--Measurement.Prometheus.Address",
Abukar Mohamed267b9092020-03-17 14:00:18 +0000141 os.Getenv("VESMGR_PROMETHEUS_ADDR"), "--AlertManager.Bind", os.Getenv("VESMGR_ALERTMANAGER_BIND_ADDR"),
142 "--Debug")
Roni Riskafc77ebb2019-09-26 08:20:44 +0300143 return vesmgr
Katri Turunen412df962019-09-16 08:48:18 +0300144}
145
Roni Riskafc77ebb2019-09-26 08:20:44 +0300146func (vesmgr *VesMgr) startVesagent() {
147 vesmgr.vesagent.run(vesmgr.chVesagent)
Katri Turunen4b74f012019-08-15 10:49:36 +0300148}
149
Roni Riskafc77ebb2019-09-26 08:20:44 +0300150func (vesmgr *VesMgr) killVespa() error {
Katri Turunen412df962019-09-16 08:48:18 +0300151 logger.Info("Killing vespa")
Roni Riskafc77ebb2019-09-26 08:20:44 +0300152 err := vesmgr.vesagent.kill()
Katri Turunen412df962019-09-16 08:48:18 +0300153 if err != nil {
154 logger.Error("Cannot kill vespa: %s", err.Error())
Roni Riskafc77ebb2019-09-26 08:20:44 +0300155 return err
Katri Turunen412df962019-09-16 08:48:18 +0300156 }
Roni Riskafc77ebb2019-09-26 08:20:44 +0300157 return <-vesmgr.chVesagent // wait vespa exit
Katri Turunen412df962019-09-16 08:48:18 +0300158}
159
Roni Riskafc77ebb2019-09-26 08:20:44 +0300160func queryXAppsConfig(appmgrURL string, timeout time.Duration) ([]byte, error) {
161 emptyConfig := []byte("{}")
162 logger.Info("query xAppConfig started, url %s", appmgrURL)
163 req, err := http.NewRequest("GET", appmgrURL, nil)
Katri Turunen412df962019-09-16 08:48:18 +0300164 if err != nil {
165 logger.Error("Failed to create a HTTP request: %s", err)
Roni Riskafc77ebb2019-09-26 08:20:44 +0300166 return emptyConfig, err
Katri Turunen412df962019-09-16 08:48:18 +0300167 }
168 req.Header.Set("Content-Type", "application/json")
169 client := &http.Client{}
170 client.Timeout = time.Second * timeout
171 resp, err := client.Do(req)
172 if err != nil {
Roni Riskafc77ebb2019-09-26 08:20:44 +0300173 logger.Error("Query xApp config failed: %s", err)
174 return emptyConfig, err
Katri Turunen412df962019-09-16 08:48:18 +0300175 }
176 defer resp.Body.Close()
177 if resp.StatusCode == http.StatusOK {
178 body, err := ioutil.ReadAll(resp.Body)
179 if err != nil {
Roni Riskafc77ebb2019-09-26 08:20:44 +0300180 logger.Error("Failed to read xApp config body: %s", err)
181 return emptyConfig, err
Katri Turunen412df962019-09-16 08:48:18 +0300182 }
Roni Riskafc77ebb2019-09-26 08:20:44 +0300183 logger.Info("query xAppConfig completed")
Katri Turunen412df962019-09-16 08:48:18 +0300184 return body, nil
Katri Turunen412df962019-09-16 08:48:18 +0300185 }
Roni Riskafc77ebb2019-09-26 08:20:44 +0300186 logger.Error("Error from xApp config query: %s", resp.Status)
187 return emptyConfig, errors.New(resp.Status)
Katri Turunen412df962019-09-16 08:48:18 +0300188}
189
Katri Turunen412df962019-09-16 08:48:18 +0300190func queryConf() ([]byte, error) {
Roni Riskafc77ebb2019-09-26 08:20:44 +0300191 return queryXAppsConfig("http://"+appmgrDomain+":"+appmgrPort+appmgrXAppConfigPath,
Katri Turunen412df962019-09-16 08:48:18 +0300192 10*time.Second)
193}
194
Roni Riskafc77ebb2019-09-26 08:20:44 +0300195func (vesmgr *VesMgr) emptyNotificationsChannel() {
Katri Turunen4b74f012019-08-15 10:49:36 +0300196 for {
Katri Turunen412df962019-09-16 08:48:18 +0300197 select {
Roni Riskafc77ebb2019-09-26 08:20:44 +0300198 case <-vesmgr.chXAppNotifications:
199 // we don't care the content
200 default:
201 return
202 }
203 }
204}
205
206func (vesmgr *VesMgr) servRequest() {
207 select {
208 case supervision := <-vesmgr.chSupervision:
209 logger.Info("vesmgr: supervision")
210 supervision <- "OK"
211 case xAppNotif := <-vesmgr.chXAppNotifications:
212 logger.Info("vesmgr: xApp notification")
213 logger.Info(string(xAppNotif))
214 vesmgr.emptyNotificationsChannel()
215 /*
216 * If xapp config query fails then we cannot create
217 * a new configuration and kill vespa.
218 * In that case we assume that
219 * the situation is fixed when the next
220 * xapp notif comes
221 */
222 xappConfig, err := queryConf()
223 if err == nil {
224 vesmgr.killVespa()
225 createConf(vespaConfigFile, xappConfig)
226 vesmgr.startVesagent()
227 }
228 case err := <-vesmgr.chVesagent:
229 logger.Error("Vesagent exited: " + err.Error())
230 os.Exit(1)
231 }
232}
233
234func (vesmgr *VesMgr) waitSubscriptionLoop() {
235 for {
236 select {
237 case supervision := <-vesmgr.chSupervision:
Katri Turunen412df962019-09-16 08:48:18 +0300238 logger.Info("vesmgr: supervision")
239 supervision <- "OK"
Roni Riskafc77ebb2019-09-26 08:20:44 +0300240 case isSubscribed := <-vesmgr.chXAppSubscriptions:
Katri Turunen412df962019-09-16 08:48:18 +0300241 if isSubscribed.err != nil {
242 logger.Error("Failed to make xApp subscriptions, vesmgr exiting: %s", isSubscribed.err)
243 os.Exit(1)
244 }
Roni Riskafc77ebb2019-09-26 08:20:44 +0300245 return
Katri Turunen412df962019-09-16 08:48:18 +0300246 }
Katri Turunen4b74f012019-08-15 10:49:36 +0300247 }
248}
Roni Riskafc77ebb2019-09-26 08:20:44 +0300249
250// Run the vesmgr process main loop
251func (vesmgr *VesMgr) Run() {
252 logger.Info("vesmgr main loop ready")
253 vesmgr.httpServer.start(vesmgrXappNotifPath, vesmgr.chXAppNotifications, vesmgr.chSupervision)
254 vesmgr.subscribeXAppNotifications()
255 vesmgr.waitSubscriptionLoop()
256 xappConfig, _ := queryConf()
257 createConf(vespaConfigFile, xappConfig)
258 vesmgr.startVesagent()
259 for {
260 vesmgr.servRequest()
261 }
262}