Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 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. |
Roni Riska | 6ffba08 | 2019-11-27 10:59:54 +0200 | [diff] [blame] | 16 | * |
| 17 | * This source code is part of the near-RT RIC (RAN Intelligent Controller) |
| 18 | * platform project (RICP). |
| 19 | * |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 20 | */ |
| 21 | |
| 22 | package main |
| 23 | |
| 24 | import ( |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 25 | "errors" |
Mohamed Abukar | 67a790a | 2020-11-05 09:54:19 +0200 | [diff] [blame^] | 26 | "fmt" |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 27 | "io/ioutil" |
| 28 | "net" |
| 29 | "net/http" |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 30 | "os" |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 31 | "time" |
| 32 | |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 33 | mdcloggo "gerrit.o-ran-sc.org/r/com/golog.git" |
| 34 | ) |
| 35 | |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 36 | var appmgrDomain string |
| 37 | |
| 38 | const appmgrXAppConfigPath = "/ric/v1/config" |
| 39 | const appmgrPort = "8080" |
| 40 | |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 41 | // VesMgr contains runtime information of the vesmgr process |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 42 | type VesMgr struct { |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 43 | myIPAddress string |
| 44 | chXAppSubscriptions chan subscriptionNotification |
| 45 | chXAppNotifications chan []byte |
| 46 | chSupervision chan chan string |
| 47 | chVesagent chan error |
| 48 | vesagent cmdRunner |
| 49 | httpServer HTTPServer |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 50 | } |
| 51 | |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 52 | type subscriptionNotification struct { |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 53 | subscribed bool |
| 54 | err error |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 55 | subsID string |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 56 | } |
| 57 | |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 58 | var logger *mdcloggo.MdcLogger |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 59 | |
Roni Riska | 364295f | 2019-09-30 09:39:12 +0300 | [diff] [blame] | 60 | // Version information, which is filled during compilation |
| 61 | // Version tag of vesmgr container |
| 62 | var Version string |
| 63 | |
| 64 | // Hash of the git commit used in building |
| 65 | var Hash string |
| 66 | |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 67 | const vesmgrXappNotifPort = "8080" |
| 68 | const vesmgrXappNotifPath = "/vesmgr_xappnotif/" |
| 69 | const timeoutPostXAppSubscriptions = 5 |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 70 | const vespaConfigFile = "/etc/ves-agent/ves-agent.yaml" |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 71 | |
| 72 | func init() { |
| 73 | logger, _ = mdcloggo.InitLogger("vesmgr") |
| 74 | } |
| 75 | |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 76 | func getMyIP() (myIP string, retErr error) { |
| 77 | addrs, err := net.InterfaceAddrs() |
| 78 | if err != nil { |
| 79 | logger.Error("net.InterfaceAddrs failed: %s", err.Error()) |
| 80 | return "", err |
| 81 | } |
| 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 |
| 88 | } |
| 89 | } |
| 90 | } |
| 91 | return "", nil |
| 92 | } |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 93 | |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 94 | func createConf(fname string, xappMetrics []byte) { |
| 95 | f, err := os.Create(fname) |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 96 | if err != nil { |
| 97 | logger.Error("Cannot create vespa conf file: %s", err.Error()) |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 98 | os.Exit(1) |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 99 | } |
| 100 | defer f.Close() |
| 101 | |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 102 | createVespaConfig(f, xappMetrics) |
| 103 | logger.Info("Vespa config created") |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 104 | } |
| 105 | |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 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) |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 111 | } |
| 112 | |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 113 | // Init initializes the vesmgr |
| 114 | func (vesmgr *VesMgr) Init(listenPort string) *VesMgr { |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 115 | logger.Info("vesmgrInit") |
Abukar Mohamed | 267b909 | 2020-03-17 14:00:18 +0000 | [diff] [blame] | 116 | logger.Info("version: %s (%s)", Version, Hash) |
Roni Riska | 364295f | 2019-09-30 09:39:12 +0300 | [diff] [blame] | 117 | |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 118 | var err error |
| 119 | if vesmgr.myIPAddress, err = getMyIP(); err != nil || vesmgr.myIPAddress == "" { |
Mohamed Abukar | cbfed3c | 2020-10-08 09:37:21 +0300 | [diff] [blame] | 120 | logger.Error("Cannot get myIPAddress: IP %s", vesmgr.myIPAddress) |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 121 | panic("Cannot get my IP address") |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 122 | } |
| 123 | |
| 124 | var ok bool |
| 125 | appmgrDomain, ok = os.LookupEnv("VESMGR_APPMGRDOMAIN") |
| 126 | if ok { |
| 127 | logger.Info("Using appmgrdomain %s", appmgrDomain) |
| 128 | } else { |
Mohamed Abukar | 67a790a | 2020-11-05 09:54:19 +0200 | [diff] [blame^] | 129 | pltnamespace := os.Getenv("PLT_NAMESPACE") |
| 130 | if pltnamespace == "" { |
| 131 | pltnamespace = "ricplt" |
| 132 | } |
| 133 | appmgrDomain = fmt.Sprintf("service-%s-appmgr-http.%s.svc.cluster.local", pltnamespace, pltnamespace) |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 134 | logger.Info("Using default appmgrdomain %s", appmgrDomain) |
| 135 | } |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 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", |
Abukar Mohamed | 267b909 | 2020-03-17 14:00:18 +0000 | [diff] [blame] | 146 | os.Getenv("VESMGR_PROMETHEUS_ADDR"), "--AlertManager.Bind", os.Getenv("VESMGR_ALERTMANAGER_BIND_ADDR"), |
| 147 | "--Debug") |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 148 | return vesmgr |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 149 | } |
| 150 | |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 151 | func (vesmgr *VesMgr) startVesagent() { |
| 152 | vesmgr.vesagent.run(vesmgr.chVesagent) |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 153 | } |
| 154 | |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 155 | func (vesmgr *VesMgr) killVespa() error { |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 156 | logger.Info("Killing vespa") |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 157 | err := vesmgr.vesagent.kill() |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 158 | if err != nil { |
| 159 | logger.Error("Cannot kill vespa: %s", err.Error()) |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 160 | return err |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 161 | } |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 162 | return <-vesmgr.chVesagent // wait vespa exit |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 163 | } |
| 164 | |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 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) |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 169 | if err != nil { |
| 170 | logger.Error("Failed to create a HTTP request: %s", err) |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 171 | return emptyConfig, err |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 172 | } |
| 173 | req.Header.Set("Content-Type", "application/json") |
| 174 | client := &http.Client{} |
| 175 | client.Timeout = time.Second * timeout |
| 176 | resp, err := client.Do(req) |
| 177 | if err != nil { |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 178 | logger.Error("Query xApp config failed: %s", err) |
| 179 | return emptyConfig, err |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 180 | } |
| 181 | defer resp.Body.Close() |
| 182 | if resp.StatusCode == http.StatusOK { |
| 183 | body, err := ioutil.ReadAll(resp.Body) |
| 184 | if err != nil { |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 185 | logger.Error("Failed to read xApp config body: %s", err) |
| 186 | return emptyConfig, err |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 187 | } |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 188 | logger.Info("query xAppConfig completed") |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 189 | return body, nil |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 190 | } |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 191 | logger.Error("Error from xApp config query: %s", resp.Status) |
| 192 | return emptyConfig, errors.New(resp.Status) |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 193 | } |
| 194 | |
Mohamed Abukar | 070b365 | 2020-10-22 19:31:01 +0300 | [diff] [blame] | 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 { |
| 199 | break |
| 200 | } |
| 201 | time.Sleep(5 * time.Second) |
| 202 | } |
| 203 | return appConfig, err |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 204 | } |
| 205 | |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 206 | func (vesmgr *VesMgr) emptyNotificationsChannel() { |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 207 | for { |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 208 | select { |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 209 | case <-vesmgr.chXAppNotifications: |
| 210 | // we don't care the content |
| 211 | default: |
| 212 | return |
| 213 | } |
| 214 | } |
| 215 | } |
| 216 | |
| 217 | func (vesmgr *VesMgr) servRequest() { |
| 218 | select { |
| 219 | case supervision := <-vesmgr.chSupervision: |
| 220 | logger.Info("vesmgr: supervision") |
| 221 | supervision <- "OK" |
| 222 | case xAppNotif := <-vesmgr.chXAppNotifications: |
| 223 | logger.Info("vesmgr: xApp notification") |
| 224 | logger.Info(string(xAppNotif)) |
| 225 | vesmgr.emptyNotificationsChannel() |
| 226 | /* |
| 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 |
| 231 | * xapp notif comes |
| 232 | */ |
| 233 | xappConfig, err := queryConf() |
| 234 | if err == nil { |
| 235 | vesmgr.killVespa() |
| 236 | createConf(vespaConfigFile, xappConfig) |
| 237 | vesmgr.startVesagent() |
| 238 | } |
| 239 | case err := <-vesmgr.chVesagent: |
| 240 | logger.Error("Vesagent exited: " + err.Error()) |
| 241 | os.Exit(1) |
| 242 | } |
| 243 | } |
| 244 | |
| 245 | func (vesmgr *VesMgr) waitSubscriptionLoop() { |
| 246 | for { |
| 247 | select { |
| 248 | case supervision := <-vesmgr.chSupervision: |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 249 | logger.Info("vesmgr: supervision") |
| 250 | supervision <- "OK" |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 251 | case isSubscribed := <-vesmgr.chXAppSubscriptions: |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 252 | if isSubscribed.err != nil { |
| 253 | logger.Error("Failed to make xApp subscriptions, vesmgr exiting: %s", isSubscribed.err) |
| 254 | os.Exit(1) |
| 255 | } |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 256 | return |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 257 | } |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 258 | } |
| 259 | } |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 260 | |
| 261 | // Run the vesmgr process main loop |
| 262 | func (vesmgr *VesMgr) Run() { |
| 263 | logger.Info("vesmgr main loop ready") |
Mohamed Abukar | 070b365 | 2020-10-22 19:31:01 +0300 | [diff] [blame] | 264 | |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 265 | vesmgr.httpServer.start(vesmgrXappNotifPath, vesmgr.chXAppNotifications, vesmgr.chSupervision) |
Mohamed Abukar | 070b365 | 2020-10-22 19:31:01 +0300 | [diff] [blame] | 266 | |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 267 | vesmgr.subscribeXAppNotifications() |
Mohamed Abukar | 070b365 | 2020-10-22 19:31:01 +0300 | [diff] [blame] | 268 | |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 269 | vesmgr.waitSubscriptionLoop() |
Mohamed Abukar | 070b365 | 2020-10-22 19:31:01 +0300 | [diff] [blame] | 270 | |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 271 | xappConfig, _ := queryConf() |
Mohamed Abukar | 070b365 | 2020-10-22 19:31:01 +0300 | [diff] [blame] | 272 | |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 273 | createConf(vespaConfigFile, xappConfig) |
Mohamed Abukar | 070b365 | 2020-10-22 19:31:01 +0300 | [diff] [blame] | 274 | |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 275 | vesmgr.startVesagent() |
| 276 | for { |
| 277 | vesmgr.servRequest() |
| 278 | } |
| 279 | } |