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).
32 mdcloggo "gerrit.o-ran-sc.org/r/com/golog.git"
35 var appmgrDomain string
37 const appmgrXAppConfigPath = "/ric/v1/config"
38 const appmgrPort = "8080"
40 // VesMgr contains runtime information of the vesmgr process
43 chXAppSubscriptions chan subscriptionNotification
44 chXAppNotifications chan []byte
45 chSupervision chan chan string
51 type subscriptionNotification struct {
57 var logger *mdcloggo.MdcLogger
59 // Version information, which is filled during compilation
60 // Version tag of vesmgr container
63 // Hash of the git commit used in building
66 const vesmgrXappNotifPort = "8080"
67 const vesmgrXappNotifPath = "/vesmgr_xappnotif/"
68 const timeoutPostXAppSubscriptions = 5
69 const vespaConfigFile = "/etc/ves-agent/ves-agent.yaml"
72 logger, _ = mdcloggo.InitLogger("vesmgr")
75 func getMyIP() (myIP string, retErr error) {
76 addrs, err := net.InterfaceAddrs()
78 logger.Error("net.InterfaceAddrs failed: %s", err.Error())
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
93 func createConf(fname string, xappMetrics []byte) {
94 f, err := os.Create(fname)
96 logger.Error("Cannot create vespa conf file: %s", err.Error())
101 createVespaConfig(f, xappMetrics)
102 logger.Info("Vespa config created")
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)
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)
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")
124 appmgrDomain, ok = os.LookupEnv("VESMGR_APPMGRDOMAIN")
126 logger.Info("Using appmgrdomain %s", appmgrDomain)
128 appmgrDomain = "service-ricplt-appmgr-http.ricplt.svc.cluster.local"
129 logger.Info("Using default appmgrdomain %s", appmgrDomain)
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"), "--AlertManager.Bind", os.Getenv("VESMGR_ALERTMANAGER_BIND_ADDR"),
146 func (vesmgr *VesMgr) startVesagent() {
147 vesmgr.vesagent.run(vesmgr.chVesagent)
150 func (vesmgr *VesMgr) killVespa() error {
151 logger.Info("Killing vespa")
152 err := vesmgr.vesagent.kill()
154 logger.Error("Cannot kill vespa: %s", err.Error())
157 return <-vesmgr.chVesagent // wait vespa exit
160 func 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)
165 logger.Error("Failed to create a HTTP request: %s", err)
166 return emptyConfig, err
168 req.Header.Set("Content-Type", "application/json")
169 client := &http.Client{}
170 client.Timeout = time.Second * timeout
171 resp, err := client.Do(req)
173 logger.Error("Query xApp config failed: %s", err)
174 return emptyConfig, err
176 defer resp.Body.Close()
177 if resp.StatusCode == http.StatusOK {
178 body, err := ioutil.ReadAll(resp.Body)
180 logger.Error("Failed to read xApp config body: %s", err)
181 return emptyConfig, err
183 logger.Info("query xAppConfig completed")
186 logger.Error("Error from xApp config query: %s", resp.Status)
187 return emptyConfig, errors.New(resp.Status)
190 func queryConf() ([]byte, error) {
191 return queryXAppsConfig("http://"+appmgrDomain+":"+appmgrPort+appmgrXAppConfigPath,
195 func (vesmgr *VesMgr) emptyNotificationsChannel() {
198 case <-vesmgr.chXAppNotifications:
199 // we don't care the content
206 func (vesmgr *VesMgr) servRequest() {
208 case supervision := <-vesmgr.chSupervision:
209 logger.Info("vesmgr: supervision")
211 case xAppNotif := <-vesmgr.chXAppNotifications:
212 logger.Info("vesmgr: xApp notification")
213 logger.Info(string(xAppNotif))
214 vesmgr.emptyNotificationsChannel()
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
222 xappConfig, err := queryConf()
225 createConf(vespaConfigFile, xappConfig)
226 vesmgr.startVesagent()
228 case err := <-vesmgr.chVesagent:
229 logger.Error("Vesagent exited: " + err.Error())
234 func (vesmgr *VesMgr) waitSubscriptionLoop() {
237 case supervision := <-vesmgr.chSupervision:
238 logger.Info("vesmgr: supervision")
240 case isSubscribed := <-vesmgr.chXAppSubscriptions:
241 if isSubscribed.err != nil {
242 logger.Error("Failed to make xApp subscriptions, vesmgr exiting: %s", isSubscribed.err)
250 // Run the vesmgr process main loop
251 func (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()