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.
28 mdcloggo "gerrit.o-ran-sc.org/r/com/golog.git"
31 var appmgrDomain string
33 const appmgrXAppConfigPath = "/ric/v1/config"
34 const appmgrPort = "8080"
36 // VesMgr contains runtime information of the vesmgr process
39 chXAppSubscriptions chan subscriptionNotification
40 chXAppNotifications chan []byte
41 chSupervision chan chan string
47 type subscriptionNotification struct {
53 var logger *mdcloggo.MdcLogger
55 // Version information, which is filled during compilation
56 // Version tag of vesmgr container
59 // Hash of the git commit used in building
62 const vesmgrXappNotifPort = "8080"
63 const vesmgrXappNotifPath = "/vesmgr_xappnotif/"
64 const timeoutPostXAppSubscriptions = 5
65 const vespaConfigFile = "/etc/ves-agent/ves-agent.yaml"
68 logger, _ = mdcloggo.InitLogger("vesmgr")
71 func getMyIP() (myIP string, retErr error) {
72 addrs, err := net.InterfaceAddrs()
74 logger.Error("net.InterfaceAddrs failed: %s", err.Error())
77 for _, addr := range addrs {
78 // check the address type and if it is not a loopback take it
79 if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
80 if ipnet.IP.To4() != nil {
81 logger.Info("My IP Address: %s", ipnet.IP.String())
82 return ipnet.IP.String(), nil
89 func createConf(fname string, xappMetrics []byte) {
90 f, err := os.Create(fname)
92 logger.Error("Cannot create vespa conf file: %s", err.Error())
97 createVespaConfig(f, xappMetrics)
98 logger.Info("Vespa config created")
101 func (vesmgr *VesMgr) subscribeXAppNotifications() {
102 xappNotifURL := "http://" + vesmgr.myIPAddress + ":" + vesmgrXappNotifPort + vesmgrXappNotifPath
103 subsURL := "http://" + appmgrDomain + ":" + appmgrPort + appmgrSubsPath
104 go subscribexAppNotifications(xappNotifURL, vesmgr.chXAppSubscriptions, timeoutPostXAppSubscriptions, subsURL)
105 logger.Info("xApp notifications subscribed from %s", subsURL)
108 // Init initializes the vesmgr
109 func (vesmgr *VesMgr) Init(listenPort string) *VesMgr {
110 logger.Info("vesmgrInit")
111 logger.Info("version %s (%s)", Version, Hash)
114 if vesmgr.myIPAddress, err = getMyIP(); err != nil || vesmgr.myIPAddress == "" {
115 logger.Error("Cannot get myIPAddress: IP %s, err %s", vesmgr.myIPAddress, err.Error())
116 panic("Cannot get my IP address")
120 appmgrDomain, ok = os.LookupEnv("VESMGR_APPMGRDOMAIN")
122 logger.Info("Using appmgrdomain %s", appmgrDomain)
124 appmgrDomain = "service-ricplt-appmgr-http.ricplt.svc.cluster.local"
125 logger.Info("Using default appmgrdomain %s", appmgrDomain)
127 vesmgr.chXAppSubscriptions = make(chan subscriptionNotification)
128 // Create notifications as buffered channel so that
129 // xappmgr does not block if we are stuck somewhere
130 vesmgr.chXAppNotifications = make(chan []byte, 10)
131 vesmgr.chSupervision = make(chan chan string)
132 vesmgr.chVesagent = make(chan error)
133 vesmgr.httpServer = HTTPServer{}
134 vesmgr.httpServer.init(vesmgr.myIPAddress + ":" + listenPort)
135 vesmgr.vesagent = makeRunner("ves-agent", "-i", os.Getenv("VESMGR_HB_INTERVAL"),
136 "-m", os.Getenv("VESMGR_MEAS_INTERVAL"), "--Measurement.Prometheus.Address",
137 os.Getenv("VESMGR_PROMETHEUS_ADDR"))
141 func (vesmgr *VesMgr) startVesagent() {
142 vesmgr.vesagent.run(vesmgr.chVesagent)
145 func (vesmgr *VesMgr) killVespa() error {
146 logger.Info("Killing vespa")
147 err := vesmgr.vesagent.kill()
149 logger.Error("Cannot kill vespa: %s", err.Error())
152 return <-vesmgr.chVesagent // wait vespa exit
155 func queryXAppsConfig(appmgrURL string, timeout time.Duration) ([]byte, error) {
156 emptyConfig := []byte("{}")
157 logger.Info("query xAppConfig started, url %s", appmgrURL)
158 req, err := http.NewRequest("GET", appmgrURL, nil)
160 logger.Error("Failed to create a HTTP request: %s", err)
161 return emptyConfig, err
163 req.Header.Set("Content-Type", "application/json")
164 client := &http.Client{}
165 client.Timeout = time.Second * timeout
166 resp, err := client.Do(req)
168 logger.Error("Query xApp config failed: %s", err)
169 return emptyConfig, err
171 defer resp.Body.Close()
172 if resp.StatusCode == http.StatusOK {
173 body, err := ioutil.ReadAll(resp.Body)
175 logger.Error("Failed to read xApp config body: %s", err)
176 return emptyConfig, err
178 logger.Info("query xAppConfig completed")
181 logger.Error("Error from xApp config query: %s", resp.Status)
182 return emptyConfig, errors.New(resp.Status)
185 func queryConf() ([]byte, error) {
186 return queryXAppsConfig("http://"+appmgrDomain+":"+appmgrPort+appmgrXAppConfigPath,
190 func (vesmgr *VesMgr) emptyNotificationsChannel() {
193 case <-vesmgr.chXAppNotifications:
194 // we don't care the content
201 func (vesmgr *VesMgr) servRequest() {
203 case supervision := <-vesmgr.chSupervision:
204 logger.Info("vesmgr: supervision")
206 case xAppNotif := <-vesmgr.chXAppNotifications:
207 logger.Info("vesmgr: xApp notification")
208 logger.Info(string(xAppNotif))
209 vesmgr.emptyNotificationsChannel()
211 * If xapp config query fails then we cannot create
212 * a new configuration and kill vespa.
213 * In that case we assume that
214 * the situation is fixed when the next
217 xappConfig, err := queryConf()
220 createConf(vespaConfigFile, xappConfig)
221 vesmgr.startVesagent()
223 case err := <-vesmgr.chVesagent:
224 logger.Error("Vesagent exited: " + err.Error())
229 func (vesmgr *VesMgr) waitSubscriptionLoop() {
232 case supervision := <-vesmgr.chSupervision:
233 logger.Info("vesmgr: supervision")
235 case isSubscribed := <-vesmgr.chXAppSubscriptions:
236 if isSubscribed.err != nil {
237 logger.Error("Failed to make xApp subscriptions, vesmgr exiting: %s", isSubscribed.err)
245 // Run the vesmgr process main loop
246 func (vesmgr *VesMgr) Run() {
247 logger.Info("vesmgr main loop ready")
248 vesmgr.httpServer.start(vesmgrXappNotifPath, vesmgr.chXAppNotifications, vesmgr.chSupervision)
249 vesmgr.subscribeXAppNotifications()
250 vesmgr.waitSubscriptionLoop()
251 xappConfig, _ := queryConf()
252 createConf(vespaConfigFile, xappConfig)
253 vesmgr.startVesagent()