Add new license claim
[ric-plt/vespamgr.git] / cmd / vesmgr / vesmgr.go
1 /*
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.
16  *
17  *  This source code is part of the near-RT RIC (RAN Intelligent Controller)
18  *  platform project (RICP).
19  *
20  */
21
22 package main
23
24 import (
25         "errors"
26         "io/ioutil"
27         "net"
28         "net/http"
29         "os"
30         "time"
31
32         mdcloggo "gerrit.o-ran-sc.org/r/com/golog.git"
33 )
34
35 var appmgrDomain string
36
37 const appmgrXAppConfigPath = "/ric/v1/config"
38 const appmgrPort = "8080"
39
40 // VesMgr contains runtime information of the vesmgr process
41 type VesMgr struct {
42         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
49 }
50
51 type subscriptionNotification struct {
52         subscribed bool
53         err        error
54         subsID     string
55 }
56
57 var logger *mdcloggo.MdcLogger
58
59 // Version information, which is filled during compilation
60 // Version tag of vesmgr container
61 var Version string
62
63 // Hash of the git commit used in building
64 var Hash string
65
66 const vesmgrXappNotifPort = "8080"
67 const vesmgrXappNotifPath = "/vesmgr_xappnotif/"
68 const timeoutPostXAppSubscriptions = 5
69 const vespaConfigFile = "/etc/ves-agent/ves-agent.yaml"
70
71 func init() {
72         logger, _ = mdcloggo.InitLogger("vesmgr")
73 }
74
75 func 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 }
92
93 func createConf(fname string, xappMetrics []byte) {
94         f, err := os.Create(fname)
95         if err != nil {
96                 logger.Error("Cannot create vespa conf file: %s", err.Error())
97                 os.Exit(1)
98         }
99         defer f.Close()
100
101         createVespaConfig(f, xappMetrics)
102         logger.Info("Vespa config created")
103 }
104
105 func (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)
110 }
111
112 // Init initializes the vesmgr
113 func (vesmgr *VesMgr) Init(listenPort string) *VesMgr {
114         logger.Info("vesmgrInit")
115         logger.Info("version %s (%s)", Version, Hash)
116
117         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())
120                 panic("Cannot get my IP address")
121         }
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         }
131         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
143 }
144
145 func (vesmgr *VesMgr) startVesagent() {
146         vesmgr.vesagent.run(vesmgr.chVesagent)
147 }
148
149 func (vesmgr *VesMgr) killVespa() error {
150         logger.Info("Killing vespa")
151         err := vesmgr.vesagent.kill()
152         if err != nil {
153                 logger.Error("Cannot kill vespa: %s", err.Error())
154                 return err
155         }
156         return <-vesmgr.chVesagent // wait vespa exit
157 }
158
159 func 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)
163         if err != nil {
164                 logger.Error("Failed to create a HTTP request: %s", err)
165                 return emptyConfig, err
166         }
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 {
172                 logger.Error("Query xApp config failed: %s", err)
173                 return emptyConfig, err
174         }
175         defer resp.Body.Close()
176         if resp.StatusCode == http.StatusOK {
177                 body, err := ioutil.ReadAll(resp.Body)
178                 if err != nil {
179                         logger.Error("Failed to read xApp config body: %s", err)
180                         return emptyConfig, err
181                 }
182                 logger.Info("query xAppConfig completed")
183                 return body, nil
184         }
185         logger.Error("Error from xApp config query: %s", resp.Status)
186         return emptyConfig, errors.New(resp.Status)
187 }
188
189 func queryConf() ([]byte, error) {
190         return queryXAppsConfig("http://"+appmgrDomain+":"+appmgrPort+appmgrXAppConfigPath,
191                 10*time.Second)
192 }
193
194 func (vesmgr *VesMgr) emptyNotificationsChannel() {
195         for {
196                 select {
197                 case <-vesmgr.chXAppNotifications:
198                         // we don't care the content
199                 default:
200                         return
201                 }
202         }
203 }
204
205 func (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
233 func (vesmgr *VesMgr) waitSubscriptionLoop() {
234         for {
235                 select {
236                 case supervision := <-vesmgr.chSupervision:
237                         logger.Info("vesmgr: supervision")
238                         supervision <- "OK"
239                 case isSubscribed := <-vesmgr.chXAppSubscriptions:
240                         if isSubscribed.err != nil {
241                                 logger.Error("Failed to make xApp subscriptions, vesmgr exiting: %s", isSubscribed.err)
242                                 os.Exit(1)
243                         }
244                         return
245                 }
246         }
247 }
248
249 // Run the vesmgr process main loop
250 func (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 }