+++ /dev/null
-/*
- * Copyright (c) 2019 AT&T Intellectual Property.
- * Copyright (c) 2018-2019 Nokia.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * This source code is part of the near-RT RIC (RAN Intelligent Controller)
- * platform project (RICP).
- *
- */
-
-package main
-
-import (
- "errors"
- "fmt"
- "io/ioutil"
- "net"
- "net/http"
- "os"
- "time"
-
- mdcloggo "gerrit.o-ran-sc.org/r/com/golog.git"
-)
-
-var appmgrDomain string
-
-const appmgrXAppConfigPath = "/ric/v1/config"
-const appmgrPort = "8080"
-
-// VesMgr contains runtime information of the vesmgr process
-type VesMgr struct {
- myIPAddress string
- chXAppSubscriptions chan subscriptionNotification
- chXAppNotifications chan []byte
- chSupervision chan chan string
- chVesagent chan error
- vesagent cmdRunner
- httpServer HTTPServer
-}
-
-type subscriptionNotification struct {
- subscribed bool
- err error
- subsID string
-}
-
-var logger *mdcloggo.MdcLogger
-
-// Version information, which is filled during compilation
-// Version tag of vesmgr container
-var Version string
-
-// Hash of the git commit used in building
-var Hash string
-
-const vesmgrXappNotifPort = "8080"
-const vesmgrXappNotifPath = "/vesmgr_xappnotif/"
-const timeoutPostXAppSubscriptions = 5
-const vespaConfigFile = "/etc/ves-agent/ves-agent.yaml"
-
-func init() {
- logger, _ = mdcloggo.InitLogger("vesmgr")
-}
-
-func getMyIP() (myIP string, retErr error) {
- addrs, err := net.InterfaceAddrs()
- if err != nil {
- logger.Error("net.InterfaceAddrs failed: %s", err.Error())
- return "", err
- }
- for _, addr := range addrs {
- // check the address type and if it is not a loopback take it
- if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
- if ipnet.IP.To4() != nil {
- logger.Info("My IP Address: %s", ipnet.IP.String())
- return ipnet.IP.String(), nil
- }
- }
- }
- return "", nil
-}
-
-func createConf(fname string, xappMetrics []byte) {
- f, err := os.Create(fname)
- if err != nil {
- logger.Error("Cannot create vespa conf file: %s", err.Error())
- os.Exit(1)
- }
- defer f.Close()
-
- createVespaConfig(f, xappMetrics)
- logger.Info("Vespa config created")
-}
-
-func (vesmgr *VesMgr) subscribeXAppNotifications() {
- xappNotifURL := "http://" + vesmgr.myIPAddress + ":" + vesmgrXappNotifPort + vesmgrXappNotifPath
- subsURL := "http://" + appmgrDomain + ":" + appmgrPort + appmgrSubsPath
- go subscribexAppNotifications(xappNotifURL, vesmgr.chXAppSubscriptions, timeoutPostXAppSubscriptions, subsURL)
- logger.Info("xApp notifications subscribed from %s", subsURL)
-}
-
-// Init initializes the vesmgr
-func (vesmgr *VesMgr) Init(listenPort string) *VesMgr {
- logger.Info("vesmgrInit")
- logger.Info("version: %s (%s)", Version, Hash)
-
- var err error
- if vesmgr.myIPAddress, err = getMyIP(); err != nil || vesmgr.myIPAddress == "" {
- logger.Error("Cannot get myIPAddress: IP %s", vesmgr.myIPAddress)
- panic("Cannot get my IP address")
- }
-
- var ok bool
- appmgrDomain, ok = os.LookupEnv("VESMGR_APPMGRDOMAIN")
- if ok {
- logger.Info("Using appmgrdomain %s", appmgrDomain)
- } else {
- pltnamespace := os.Getenv("PLT_NAMESPACE")
- if pltnamespace == "" {
- pltnamespace = "ricplt"
- }
- appmgrDomain = fmt.Sprintf("service-%s-appmgr-http.%s.svc.cluster.local", pltnamespace, pltnamespace)
- logger.Info("Using default appmgrdomain %s", appmgrDomain)
- }
- vesmgr.chXAppSubscriptions = make(chan subscriptionNotification)
- // Create notifications as buffered channel so that
- // xappmgr does not block if we are stuck somewhere
- vesmgr.chXAppNotifications = make(chan []byte, 10)
- vesmgr.chSupervision = make(chan chan string)
- vesmgr.chVesagent = make(chan error)
- vesmgr.httpServer = HTTPServer{}
- vesmgr.httpServer.init(vesmgr.myIPAddress + ":" + listenPort)
- vesmgr.vesagent = makeRunner("ves-agent", "-i", os.Getenv("VESMGR_HB_INTERVAL"),
- "-m", os.Getenv("VESMGR_MEAS_INTERVAL"), "--Measurement.Prometheus.Address",
- os.Getenv("VESMGR_PROMETHEUS_ADDR"), "--AlertManager.Bind", os.Getenv("VESMGR_ALERTMANAGER_BIND_ADDR"),
- "--Debug")
- return vesmgr
-}
-
-func (vesmgr *VesMgr) startVesagent() {
- vesmgr.vesagent.run(vesmgr.chVesagent)
-}
-
-func (vesmgr *VesMgr) killVespa() error {
- logger.Info("Killing vespa")
- err := vesmgr.vesagent.kill()
- if err != nil {
- logger.Error("Cannot kill vespa: %s", err.Error())
- return err
- }
- return <-vesmgr.chVesagent // wait vespa exit
-}
-
-func queryXAppsConfig(appmgrURL string, timeout time.Duration) ([]byte, error) {
- emptyConfig := []byte("{}")
- logger.Info("query xAppConfig started, url %s", appmgrURL)
- req, err := http.NewRequest("GET", appmgrURL, nil)
- if err != nil {
- logger.Error("Failed to create a HTTP request: %s", err)
- return emptyConfig, err
- }
- req.Header.Set("Content-Type", "application/json")
- client := &http.Client{}
- client.Timeout = time.Second * timeout
- resp, err := client.Do(req)
- if err != nil {
- logger.Error("Query xApp config failed: %s", err)
- return emptyConfig, err
- }
- defer resp.Body.Close()
- if resp.StatusCode == http.StatusOK {
- body, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- logger.Error("Failed to read xApp config body: %s", err)
- return emptyConfig, err
- }
- logger.Info("query xAppConfig completed")
- return body, nil
- }
- logger.Error("Error from xApp config query: %s", resp.Status)
- return emptyConfig, errors.New(resp.Status)
-}
-
-func queryConf() (appConfig []byte, err error) {
- for i := 0; i < 10; i++ {
- appConfig, err = queryXAppsConfig("http://"+appmgrDomain+":"+appmgrPort+appmgrXAppConfigPath, 10*time.Second)
- if len(appConfig) > 0 {
- break
- }
- time.Sleep(5 * time.Second)
- }
- return appConfig, err
-}
-
-func (vesmgr *VesMgr) emptyNotificationsChannel() {
- for {
- select {
- case <-vesmgr.chXAppNotifications:
- // we don't care the content
- default:
- return
- }
- }
-}
-
-func (vesmgr *VesMgr) servRequest() {
- select {
- case supervision := <-vesmgr.chSupervision:
- logger.Info("vesmgr: supervision")
- supervision <- "OK"
- case xAppNotif := <-vesmgr.chXAppNotifications:
- logger.Info("vesmgr: xApp notification")
- logger.Info(string(xAppNotif))
- vesmgr.emptyNotificationsChannel()
- /*
- * If xapp config query fails then we cannot create
- * a new configuration and kill vespa.
- * In that case we assume that
- * the situation is fixed when the next
- * xapp notif comes
- */
- xappConfig, err := queryConf()
- if err == nil {
- vesmgr.killVespa()
- createConf(vespaConfigFile, xappConfig)
- vesmgr.startVesagent()
- }
- case err := <-vesmgr.chVesagent:
- logger.Error("Vesagent exited: " + err.Error())
- os.Exit(1)
- }
-}
-
-func (vesmgr *VesMgr) waitSubscriptionLoop() {
- for {
- select {
- case supervision := <-vesmgr.chSupervision:
- logger.Info("vesmgr: supervision")
- supervision <- "OK"
- case isSubscribed := <-vesmgr.chXAppSubscriptions:
- if isSubscribed.err != nil {
- logger.Error("Failed to make xApp subscriptions, vesmgr exiting: %s", isSubscribed.err)
- os.Exit(1)
- }
- return
- }
- }
-}
-
-// Run the vesmgr process main loop
-func (vesmgr *VesMgr) Run() {
- logger.Info("vesmgr main loop ready")
-
- vesmgr.httpServer.start(vesmgrXappNotifPath, vesmgr.chXAppNotifications, vesmgr.chSupervision)
-
- vesmgr.subscribeXAppNotifications()
-
- vesmgr.waitSubscriptionLoop()
-
- xappConfig, _ := queryConf()
-
- createConf(vespaConfigFile, xappConfig)
-
- vesmgr.startVesagent()
- for {
- vesmgr.servRequest()
- }
-}