2 * Copyright (c) 2019 AT&T Intellectual Property.
3 * Copyright (c) 2018-2019 Nokia.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 * This source code is part of the near-RT RIC (RAN Intelligent Controller)
18 * platform project (RICP).
33 mdcloggo "gerrit.o-ran-sc.org/r/com/golog.git"
36 var appmgrDomain string
38 const appmgrXAppConfigPath = "/ric/v1/config"
39 const appmgrPort = "8080"
41 // VesMgr contains runtime information of the vesmgr process
44 chXAppSubscriptions chan subscriptionNotification
45 chXAppNotifications chan []byte
46 chSupervision chan chan string
52 type subscriptionNotification struct {
58 var logger *mdcloggo.MdcLogger
60 // Version information, which is filled during compilation
61 // Version tag of vesmgr container
64 // Hash of the git commit used in building
67 const vesmgrXappNotifPort = "8080"
68 const vesmgrXappNotifPath = "/vesmgr_xappnotif/"
69 const timeoutPostXAppSubscriptions = 5
70 const vespaConfigFile = "/etc/ves-agent/ves-agent.yaml"
73 logger, _ = mdcloggo.InitLogger("vesmgr")
76 func getMyIP() (myIP string, retErr error) {
77 addrs, err := net.InterfaceAddrs()
79 logger.Error("net.InterfaceAddrs failed: %s", err.Error())
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
94 func createConf(fname string, xappMetrics []byte) {
95 f, err := os.Create(fname)
97 logger.Error("Cannot create vespa conf file: %s", err.Error())
102 createVespaConfig(f, xappMetrics)
103 logger.Info("Vespa config created")
106 func (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)
113 // Init initializes the vesmgr
114 func (vesmgr *VesMgr) Init(listenPort string) *VesMgr {
115 logger.Info("vesmgrInit")
116 logger.Info("version: %s (%s)", Version, Hash)
119 if vesmgr.myIPAddress, err = getMyIP(); err != nil || vesmgr.myIPAddress == "" {
120 logger.Error("Cannot get myIPAddress: IP %s", vesmgr.myIPAddress)
121 panic("Cannot get my IP address")
125 appmgrDomain, ok = os.LookupEnv("VESMGR_APPMGRDOMAIN")
127 logger.Info("Using appmgrdomain %s", appmgrDomain)
129 pltnamespace := os.Getenv("PLT_NAMESPACE")
130 if pltnamespace == "" {
131 pltnamespace = "ricplt"
133 appmgrDomain = fmt.Sprintf("service-%s-appmgr-http.%s.svc.cluster.local", pltnamespace, pltnamespace)
134 logger.Info("Using default appmgrdomain %s", appmgrDomain)
136 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",
146 os.Getenv("VESMGR_PROMETHEUS_ADDR"), "--AlertManager.Bind", os.Getenv("VESMGR_ALERTMANAGER_BIND_ADDR"),
151 func (vesmgr *VesMgr) startVesagent() {
152 vesmgr.vesagent.run(vesmgr.chVesagent)
155 func (vesmgr *VesMgr) killVespa() error {
156 logger.Info("Killing vespa")
157 err := vesmgr.vesagent.kill()
159 logger.Error("Cannot kill vespa: %s", err.Error())
162 return <-vesmgr.chVesagent // wait vespa exit
165 func 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)
170 logger.Error("Failed to create a HTTP request: %s", err)
171 return emptyConfig, err
173 req.Header.Set("Content-Type", "application/json")
174 client := &http.Client{}
175 client.Timeout = time.Second * timeout
176 resp, err := client.Do(req)
178 logger.Error("Query xApp config failed: %s", err)
179 return emptyConfig, err
181 defer resp.Body.Close()
182 if resp.StatusCode == http.StatusOK {
183 body, err := ioutil.ReadAll(resp.Body)
185 logger.Error("Failed to read xApp config body: %s", err)
186 return emptyConfig, err
188 logger.Info("query xAppConfig completed")
191 logger.Error("Error from xApp config query: %s", resp.Status)
192 return emptyConfig, errors.New(resp.Status)
195 func 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 {
201 time.Sleep(5 * time.Second)
203 return appConfig, err
206 func (vesmgr *VesMgr) emptyNotificationsChannel() {
209 case <-vesmgr.chXAppNotifications:
210 // we don't care the content
217 func (vesmgr *VesMgr) servRequest() {
219 case supervision := <-vesmgr.chSupervision:
220 logger.Info("vesmgr: supervision")
222 case xAppNotif := <-vesmgr.chXAppNotifications:
223 logger.Info("vesmgr: xApp notification")
224 logger.Info(string(xAppNotif))
225 vesmgr.emptyNotificationsChannel()
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
233 xappConfig, err := queryConf()
236 createConf(vespaConfigFile, xappConfig)
237 vesmgr.startVesagent()
239 case err := <-vesmgr.chVesagent:
240 logger.Error("Vesagent exited: " + err.Error())
245 func (vesmgr *VesMgr) waitSubscriptionLoop() {
248 case supervision := <-vesmgr.chSupervision:
249 logger.Info("vesmgr: supervision")
251 case isSubscribed := <-vesmgr.chXAppSubscriptions:
252 if isSubscribed.err != nil {
253 logger.Error("Failed to make xApp subscriptions, vesmgr exiting: %s", isSubscribed.err)
261 // Run the vesmgr process main loop
262 func (vesmgr *VesMgr) Run() {
263 logger.Info("vesmgr main loop ready")
265 vesmgr.httpServer.start(vesmgrXappNotifPath, vesmgr.chXAppNotifications, vesmgr.chSupervision)
267 vesmgr.subscribeXAppNotifications()
269 vesmgr.waitSubscriptionLoop()
271 xappConfig, _ := queryConf()
273 createConf(vespaConfigFile, xappConfig)
275 vesmgr.startVesagent()