blob: dfe9864cd1c3d8588c7dc6245487f0b7d35fcab7 [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")
Roni Riska364295f2019-09-30 09:39:12 +0300115 logger.Info("version %s (%s)", Version, Hash)
116
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",
141 os.Getenv("VESMGR_PROMETHEUS_ADDR"))
142 return vesmgr
Katri Turunen412df962019-09-16 08:48:18 +0300143}
144
Roni Riskafc77ebb2019-09-26 08:20:44 +0300145func (vesmgr *VesMgr) startVesagent() {
146 vesmgr.vesagent.run(vesmgr.chVesagent)
Katri Turunen4b74f012019-08-15 10:49:36 +0300147}
148
Roni Riskafc77ebb2019-09-26 08:20:44 +0300149func (vesmgr *VesMgr) killVespa() error {
Katri Turunen412df962019-09-16 08:48:18 +0300150 logger.Info("Killing vespa")
Roni Riskafc77ebb2019-09-26 08:20:44 +0300151 err := vesmgr.vesagent.kill()
Katri Turunen412df962019-09-16 08:48:18 +0300152 if err != nil {
153 logger.Error("Cannot kill vespa: %s", err.Error())
Roni Riskafc77ebb2019-09-26 08:20:44 +0300154 return err
Katri Turunen412df962019-09-16 08:48:18 +0300155 }
Roni Riskafc77ebb2019-09-26 08:20:44 +0300156 return <-vesmgr.chVesagent // wait vespa exit
Katri Turunen412df962019-09-16 08:48:18 +0300157}
158
Roni Riskafc77ebb2019-09-26 08:20:44 +0300159func queryXAppsConfig(appmgrURL string, timeout time.Duration) ([]byte, error) {
160 emptyConfig := []byte("{}")
161 logger.Info("query xAppConfig started, url %s", appmgrURL)
162 req, err := http.NewRequest("GET", appmgrURL, nil)
Katri Turunen412df962019-09-16 08:48:18 +0300163 if err != nil {
164 logger.Error("Failed to create a HTTP request: %s", err)
Roni Riskafc77ebb2019-09-26 08:20:44 +0300165 return emptyConfig, err
Katri Turunen412df962019-09-16 08:48:18 +0300166 }
167 req.Header.Set("Content-Type", "application/json")
168 client := &http.Client{}
169 client.Timeout = time.Second * timeout
170 resp, err := client.Do(req)
171 if err != nil {
Roni Riskafc77ebb2019-09-26 08:20:44 +0300172 logger.Error("Query xApp config failed: %s", err)
173 return emptyConfig, err
Katri Turunen412df962019-09-16 08:48:18 +0300174 }
175 defer resp.Body.Close()
176 if resp.StatusCode == http.StatusOK {
177 body, err := ioutil.ReadAll(resp.Body)
178 if err != nil {
Roni Riskafc77ebb2019-09-26 08:20:44 +0300179 logger.Error("Failed to read xApp config body: %s", err)
180 return emptyConfig, err
Katri Turunen412df962019-09-16 08:48:18 +0300181 }
Roni Riskafc77ebb2019-09-26 08:20:44 +0300182 logger.Info("query xAppConfig completed")
Katri Turunen412df962019-09-16 08:48:18 +0300183 return body, nil
Katri Turunen412df962019-09-16 08:48:18 +0300184 }
Roni Riskafc77ebb2019-09-26 08:20:44 +0300185 logger.Error("Error from xApp config query: %s", resp.Status)
186 return emptyConfig, errors.New(resp.Status)
Katri Turunen412df962019-09-16 08:48:18 +0300187}
188
Katri Turunen412df962019-09-16 08:48:18 +0300189func queryConf() ([]byte, error) {
Roni Riskafc77ebb2019-09-26 08:20:44 +0300190 return queryXAppsConfig("http://"+appmgrDomain+":"+appmgrPort+appmgrXAppConfigPath,
Katri Turunen412df962019-09-16 08:48:18 +0300191 10*time.Second)
192}
193
Roni Riskafc77ebb2019-09-26 08:20:44 +0300194func (vesmgr *VesMgr) emptyNotificationsChannel() {
Katri Turunen4b74f012019-08-15 10:49:36 +0300195 for {
Katri Turunen412df962019-09-16 08:48:18 +0300196 select {
Roni Riskafc77ebb2019-09-26 08:20:44 +0300197 case <-vesmgr.chXAppNotifications:
198 // we don't care the content
199 default:
200 return
201 }
202 }
203}
204
205func (vesmgr *VesMgr) servRequest() {
206 select {
207 case supervision := <-vesmgr.chSupervision:
208 logger.Info("vesmgr: supervision")
209 supervision <- "OK"
210 case xAppNotif := <-vesmgr.chXAppNotifications:
211 logger.Info("vesmgr: xApp notification")
212 logger.Info(string(xAppNotif))
213 vesmgr.emptyNotificationsChannel()
214 /*
215 * If xapp config query fails then we cannot create
216 * a new configuration and kill vespa.
217 * In that case we assume that
218 * the situation is fixed when the next
219 * xapp notif comes
220 */
221 xappConfig, err := queryConf()
222 if err == nil {
223 vesmgr.killVespa()
224 createConf(vespaConfigFile, xappConfig)
225 vesmgr.startVesagent()
226 }
227 case err := <-vesmgr.chVesagent:
228 logger.Error("Vesagent exited: " + err.Error())
229 os.Exit(1)
230 }
231}
232
233func (vesmgr *VesMgr) waitSubscriptionLoop() {
234 for {
235 select {
236 case supervision := <-vesmgr.chSupervision:
Katri Turunen412df962019-09-16 08:48:18 +0300237 logger.Info("vesmgr: supervision")
238 supervision <- "OK"
Roni Riskafc77ebb2019-09-26 08:20:44 +0300239 case isSubscribed := <-vesmgr.chXAppSubscriptions:
Katri Turunen412df962019-09-16 08:48:18 +0300240 if isSubscribed.err != nil {
241 logger.Error("Failed to make xApp subscriptions, vesmgr exiting: %s", isSubscribed.err)
242 os.Exit(1)
243 }
Roni Riskafc77ebb2019-09-26 08:20:44 +0300244 return
Katri Turunen412df962019-09-16 08:48:18 +0300245 }
Katri Turunen4b74f012019-08-15 10:49:36 +0300246 }
247}
Roni Riskafc77ebb2019-09-26 08:20:44 +0300248
249// Run the vesmgr process main loop
250func (vesmgr *VesMgr) Run() {
251 logger.Info("vesmgr main loop ready")
252 vesmgr.httpServer.start(vesmgrXappNotifPath, vesmgr.chXAppNotifications, vesmgr.chSupervision)
253 vesmgr.subscribeXAppNotifications()
254 vesmgr.waitSubscriptionLoop()
255 xappConfig, _ := queryConf()
256 createConf(vespaConfigFile, xappConfig)
257 vesmgr.startVesagent()
258 for {
259 vesmgr.servRequest()
260 }
261}