diff --git a/api/apiserver/api_handler.go b/api/apiserver/api_handler.go index 2bf4e3d..8c0dfa1 100644 --- a/api/apiserver/api_handler.go +++ b/api/apiserver/api_handler.go @@ -5,9 +5,11 @@ package apiserver import ( + "encoding/json" "net/http" "github.com/gin-gonic/gin" + "github.com/omec-project/metricfunc/cmd/controller" "github.com/omec-project/metricfunc/internal/metricdata" "github.com/omec-project/metricfunc/logger" "github.com/omec-project/openapi" @@ -113,3 +115,16 @@ func GetNfServiceStatsDetail(c *gin.Context) { //Gives summary of all services func GetNfServiceStatsAll(c *gin.Context) { } + +func PushTestIPs(c *gin.Context) { + requestBody, err := c.GetRawData() + if err != nil { + logger.ApiSrvLog.Errorf("get requestbody error %s", err.Error()) + return + } + var rogueIPs controller.RogueIPs + json.Unmarshal(requestBody, &rogueIPs) + + logger.ApiSrvLog.Infoln("Test RogueIPs: ", rogueIPs) + controller.RogueChannel <- rogueIPs +} diff --git a/api/apiserver/routes.go b/api/apiserver/routes.go index 1a02faf..8985910 100644 --- a/api/apiserver/routes.go +++ b/api/apiserver/routes.go @@ -112,6 +112,13 @@ var routes = Routes{ "/nfServiceStats/all", GetNfServiceStatsAll, }, + + { + "TestIPs", + strings.ToUpper("Post"), + "/testIPs", + PushTestIPs, + }, } /* APIs diff --git a/cmd/controller/config/config.go b/cmd/controller/config/config.go new file mode 100644 index 0000000..034e5a3 --- /dev/null +++ b/cmd/controller/config/config.go @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: 2022-present Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 + +package Config + +type Config struct { + Info *Info `yaml:"info"` + Logger *Logger `yaml:"logger"` + Configuration *Configuration `yaml:"configuration"` +} + +type Info struct { + Version string `yaml:"version,omitempty"` + Description string `yaml:"description,omitempty"` + HttpVersion int `yaml:"http-version,omitempty"` +} + +type Logger struct { + LogLevel string `yaml:"level,omitempty"` +} + +type Configuration struct { + OnosApiServer ServerAddr `yaml:"onosApiServer,omitempty"` + RocEndPoint ServerAddr `yaml:"rocEndPoint,omitempty"` + MetricFuncEndPoint ServerAddr `yaml:"metricFuncEndPoint,omitempty"` +} + +type ServerAddr struct { + Addr string `yaml:"addr,omitempty"` // IP used to run the server in the node. + Port int `yaml:"port,omitempty"` + PollInterval int `yaml:"pollInterval,omitempty"` +} + +type Urls struct { + Uri string `yaml:"uri,omitempty"` + Port int `yaml:"port,omitempty"` +} diff --git a/cmd/controller/config/config.yaml b/cmd/controller/config/config.yaml new file mode 100644 index 0000000..420cd15 --- /dev/null +++ b/cmd/controller/config/config.yaml @@ -0,0 +1,22 @@ +# SPDX-FileCopyrightText: 2022-present Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +info: + version: 1.0.0 + description: Controller Pod initial local configuration + +logger: + level: debug + +configuration: + onosApiServer: + addr: "onosapp" + port: 9301 + rocEndPoint: + addr: "aether-roc-umbrella-aether-roc-gui-v2-1-external.aether-roc.svc" + port: 31194 + metricFuncEndPoint: + addr: "metricfunc.omec.svc" + port: 5001 + diff --git a/cmd/controller/controller.go b/cmd/controller/controller.go new file mode 100644 index 0000000..ccc59e9 --- /dev/null +++ b/cmd/controller/controller.go @@ -0,0 +1,326 @@ +// SPDX-FileCopyrightText: 2022-present Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 + +package controller + +import ( + "bytes" + "context" + "crypto/tls" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net" + "net/http" + "strconv" + "strings" + "time" + + CConfig "github.com/omec-project/metricfunc/cmd/controller/config" + "github.com/omec-project/metricfunc/internal/metricdata" + "github.com/omec-project/metricfunc/logger" + "golang.org/x/net/http2" + "gopkg.in/yaml.v2" +) + +var ControllerConfig CConfig.Config +var client *http.Client + +//creating for testing +var RogueChannel chan RogueIPs + +type Targets struct { + EnterpriseId string `yaml:"name,omitempty" json:"name,omitempty"` +} +type RogueIPs struct { + IpAddresses []string `yaml:"ipaddresses,omitempty" json:"ipaddresses,omitempty"` +} +type OnosService struct { + OnosServiceUrl string `yaml:"onosServiceUrl,omitempty" json:"onosServiceUrl,omitempty"` + PollInterval int `yaml:"pollInterval,omitempty" json:"pollInterval,omitempty"` + RogueIPs RogueIPs `yaml:"rogueips,omitempty" json:"rogueips,omitempty"` +} + +type RocService struct { + RocServiceUrl string `yaml:"rocServiceUrl,omitempty" json:"rocServiceUrl,omitempty"` + SiteInfo []SiteInfo `yaml:"site-info,omitempty" json:"site-info,omitempty"` +} + +type SimCard struct { + SimId string `yaml:"sim-id,omitempty" json:"sim-id,omitempty"` + Imsi string `yaml:"imsi,omitempty" json:"imsi,omitempty"` + DisplayName string `yaml:"display-name,omitempty" json:"display-name,omitempty"` + Enable *bool `yaml:"enable,omitempty" json:"enable,omitempty"` +} +type SiteInfo struct { + SiteId string `yaml:"site-id,omitempty" json:"site-id,omitempty"` + SimCardDetails []SimCard `yaml:"sim-card,omitempty" json:"sim-card,omitempty"` +} + +func InitConfigFactory(f string) error { + //Read provided config + fmt.Printf("Controller has started with configuration file [%v]", f) + + if content, err := ioutil.ReadFile(f); err != nil { + logger.ControllerLog.Errorln("Readfile failed called ", err) + return err + } else { + ControllerConfig = CConfig.Config{} + + if yamlErr := yaml.Unmarshal(content, &ControllerConfig); yamlErr != nil { + logger.ControllerLog.Errorln("yaml parsing failed ", yamlErr) + return yamlErr + } + } + if ControllerConfig.Configuration == nil { + logger.ControllerLog.Errorln("Configuration Parsing Failed ", ControllerConfig.Configuration) + return nil + } + + //set http client + if ControllerConfig.Info.HttpVersion == 2 { + client = &http.Client{ + Transport: &http2.Transport{ + AllowHTTP: true, + DialTLS: func(network, addr string, _ *tls.Config) (net.Conn, error) { + return net.Dial(network, addr) + }, + }, + Timeout: 5 * time.Second, + } + } else { + client = &http.Client{ + Timeout: 5 * time.Second, + } + } + + if ControllerConfig.Configuration.OnosApiServer.PollInterval == 0 { + ControllerConfig.Configuration.OnosApiServer.PollInterval = 30 + } + + logger.ControllerLog.Infoln("Ons Api Server Endpoint:") + ControllerConfig.Configuration.OnosApiServer.Addr = strings.TrimSpace(ControllerConfig.Configuration.OnosApiServer.Addr) + logger.ControllerLog.Infoln("Address ", ControllerConfig.Configuration.OnosApiServer.Addr) + logger.ControllerLog.Infoln("Port ", ControllerConfig.Configuration.OnosApiServer.Port) + logger.ControllerLog.Infoln("PollInterval ", ControllerConfig.Configuration.OnosApiServer.PollInterval) + + logger.ControllerLog.Infoln("Roc Endpoint:") + ControllerConfig.Configuration.RocEndPoint.Addr = strings.TrimSpace(ControllerConfig.Configuration.RocEndPoint.Addr) + logger.ControllerLog.Infoln("Address ", ControllerConfig.Configuration.RocEndPoint.Addr) + logger.ControllerLog.Infoln("Port ", ControllerConfig.Configuration.RocEndPoint.Port) + + /*logger.ControllerLog.Infoln("Metric Func Endpoint:") + ControllerConfig.Configuration.MetricFuncEndPoint.Addr = strings.TrimSpace(ControllerConfig.Configuration.MetricFuncEndPoint.Addr) + logger.ControllerLog.Infoln("Address ", ControllerConfig.Configuration.MetricFuncEndPoint.Addr) + logger.ControllerLog.Infoln("Port ", ControllerConfig.Configuration.MetricFuncEndPoint.Port)*/ + return nil +} + +func getNextBackoffInterval(retry, interval uint) uint { + mFactor := 1.5 + nextInterval := float64(retry*interval) * mFactor + + if nextInterval > 10 { + return 10 + } + + return uint(nextInterval) +} + +func sendHttpReqMsg(req *http.Request) (*http.Response, error) { + //Keep sending request to Http server until response is success + var retries uint = 0 + var body []byte + if req.Body != nil { + body, _ = ioutil.ReadAll(req.Body) + } + for { + cloneReq := req.Clone(context.Background()) + req.Body = ioutil.NopCloser(bytes.NewReader(body)) + cloneReq.Body = ioutil.NopCloser(bytes.NewReader(body)) + rsp, err := client.Do(cloneReq) + retries += 1 + if err != nil { + nextInterval := getNextBackoffInterval(retries, 2) + logger.ControllerLog.Warningf("http req send error [%v], retrying after %v sec...", err.Error(), nextInterval) + time.Sleep(time.Second * time.Duration(nextInterval)) + continue + } + + if rsp.StatusCode == http.StatusAccepted || + rsp.StatusCode == http.StatusOK || rsp.StatusCode == http.StatusNoContent || + rsp.StatusCode == http.StatusCreated { + logger.ControllerLog.Infoln("Get config from peer success") + req.Body.Close() + return rsp, nil + } else { + nextInterval := getNextBackoffInterval(retries, 2) + logger.ControllerLog.Warningf("http rsp error [%v], retrying after [%v] sec...", http.StatusText(rsp.StatusCode), nextInterval) + rsp.Body.Close() + time.Sleep(time.Second * time.Duration(nextInterval)) + } + } +} + +func (onosClient *OnosService) GetRogueIPs(rogueIPChannel chan RogueIPs) { + + onosServerApi := onosClient.OnosServiceUrl + "/api/v1" + req, err := http.NewRequest(http.MethodGet, onosServerApi, nil) + if err != nil { + logger.ControllerLog.Errorln("An Error Occured ", err) + return + } + + for { + req.Header.Set("Content-Type", "application/json; charset=utf-8") + + rsp, httpErr := sendHttpReqMsg(req) + if httpErr != nil { + logger.ControllerLog.Errorf("Get Message [%v] returned error [%v] ", onosServerApi, err.Error()) + time.Sleep(10 * time.Second) + continue + } + + var rogueIPs RogueIPs + if rsp != nil && rsp.Body != nil { + json.NewDecoder(rsp.Body).Decode(&rogueIPs) + logger.ControllerLog.Infoln("received rogueIPs from Onos App: ", rogueIPs) + //writing rogueIPs into channel + rogueIPChannel <- rogueIPs + } + time.Sleep(time.Duration(onosClient.PollInterval) * time.Second) + } +} + +/*func (metricClient *MetricFuncService) GetTargets(ipaddress string) (names []Targets) { + metricApi := metricClient.MetricServiceUrl + "/nmetric-func/v1/subscriber/" + req, err := http.NewRequest(http.MethodGet, rocTargetsApi, nil) + if err != nil { + fmt.Printf("An Error Occured %v", err) + return + } + rsp, httpErr := sendHttpReqMsg(req) + if httpErr != nil { + log.Printf("Get Message [%v] returned error [%v] ", rocTargetsApi, err.Error()) + } + + if rsp != nil && rsp.Body != nil { + json.NewDecoder(rsp.Body).Decode(&names) + log.Printf("Targets received from RoC: %v", names) + } + return +}*/ + +func (rocClient *RocService) GetTargets() (names []Targets) { + rocTargetsApi := rocClient.RocServiceUrl + "/aether-roc-api/targets" + req, err := http.NewRequest(http.MethodGet, rocTargetsApi, nil) + if err != nil { + logger.ControllerLog.Errorf("An Error Occured %v", err) + return + } + rsp, httpErr := sendHttpReqMsg(req) + if httpErr != nil { + logger.ControllerLog.Errorf("Get Message [%v] returned error [%v] ", rocTargetsApi, err.Error()) + } + + if rsp != nil && rsp.Body != nil { + json.NewDecoder(rsp.Body).Decode(&names) + logger.ControllerLog.Infoln("Targets received from RoC: ", names) + } + return +} + +func (rocClient *RocService) DisableSimcard(targets []Targets, imsi string) { + for _, target := range targets { + rocSiteApi := rocClient.RocServiceUrl + "/aether-roc-api/aether/v2.1.x/" + target.EnterpriseId + "/site" + req, err := http.NewRequest(http.MethodGet, rocSiteApi, nil) + if err != nil { + fmt.Printf("An Error Occured %v", err) + return + } + rsp, httpErr := sendHttpReqMsg(req) + if httpErr != nil { + logger.ControllerLog.Errorf("Get Message [%v] returned error [%v] ", rocSiteApi, err.Error()) + } + var siteInfo []SiteInfo + if rsp != nil && rsp.Body != nil { + json.NewDecoder(rsp.Body).Decode(&siteInfo) + b, _ := io.ReadAll(rsp.Body) + logger.ControllerLog.Infof("SimDetails Received from RoC: %s\n", string(b)) + } + + var rocDisableSimCard *SimCard + for _, siteInfo := range siteInfo { + for _, simCard := range siteInfo.SimCardDetails { + if strings.HasPrefix(imsi, "imsi-") { + imsi = imsi[5:] + } + if simCard.Imsi == imsi { + logger.ControllerLog.Infof("SimCard %v Details Found in site [%v]\n", imsi, siteInfo.SiteId) + rocDisableSimCard = &simCard + break + } + } + if rocDisableSimCard != nil { + rocDisableImsiApi := rocSiteApi + "/" + siteInfo.SiteId + "/sim-card/" + rocDisableSimCard.SimId + var val bool + rocDisableSimCard.Enable = &val + b, err := json.Marshal(&rocDisableSimCard) + reqMsgBody := bytes.NewBuffer(b) + fmt.Println("Rest API to disable IMSI: ", rocDisableImsiApi) + fmt.Println("Post Msg Body:", reqMsgBody) + + req, err := http.NewRequest(http.MethodPost, rocDisableImsiApi, reqMsgBody) + req.Header.Set("Content-Type", "application/json; charset=utf-8") + _, httpErr := sendHttpReqMsg(req) + if httpErr != nil { + logger.ControllerLog.Errorf("Post Message [%v] returned error [%v] ", rocDisableImsiApi, err.Error()) + } + break + } + } + } +} + +func RogueIPHandler(rogueIPChannel chan RogueIPs) { + rocClient := RocService{ + RocServiceUrl: "http://" + ControllerConfig.Configuration.RocEndPoint.Addr + ":" + strconv.Itoa(ControllerConfig.Configuration.RocEndPoint.Port), + } + /*metricFuncClient := MetricService{ + MetricServiceUrl: "http://" + ControllerConfig.Configuration.MetricFuncEndPoint.Addr + ":" + strconv.Itoa(ControllerConfig.Configuration.MetricFuncEndPoint.Port), + }*/ + + for rogueIPs := range rogueIPChannel { + + for _, ipaddr := range rogueIPs.IpAddresses { + // get IP to imsi mapping from metricfunc + subscriberInfo, err := metricdata.GetSubscriberImsiFromIpAddr(ipaddr) + if err != nil { + logger.ControllerLog.Infoln("Subscriber Details doesn not exist with imsi ", err) + continue + } + logger.ControllerLog.Infoln("Subscriber Imsi [%v] of the IP: [%v]", subscriberInfo.Imsi, ipaddr) + //get enterprises or targets from ROC + targets := rocClient.GetTargets() + + // get siteinfo from ROC + //rocClient.DisableSimcard(targets, "208930100007490") + rocClient.DisableSimcard(targets, subscriberInfo.Imsi) + } + } +} + +/*func main() { + rogueIpChan := make(chan RogueIPs, 100) + InitConfigFactory() + onosClient := OnosService{ + OnosServiceUrl: "http://" + ControllerConfig.Configuration.OnosApiServer.Addr + ":" + + strconv.Itoa(ControllerConfig.Configuration.OnosApiServer.Port), + } + go onosClient.GetRogueIPs(rogueIpChan) + go RogueIPHandler(rogueIpChan) + + select {} +}*/ diff --git a/cmd/metricfunc/metricfunc.go b/cmd/metricfunc/metricfunc.go index dd1f195..d18a133 100644 --- a/cmd/metricfunc/metricfunc.go +++ b/cmd/metricfunc/metricfunc.go @@ -10,6 +10,7 @@ import ( "io/ioutil" "net" "os" + "strconv" "net/http" _ "net/http/pprof" @@ -18,6 +19,7 @@ import ( "gopkg.in/yaml.v2" "github.com/omec-project/metricfunc/api/apiserver" + "github.com/omec-project/metricfunc/cmd/controller" "github.com/omec-project/metricfunc/config" "github.com/omec-project/metricfunc/internal/promclient" "github.com/omec-project/metricfunc/internal/reader" @@ -36,6 +38,7 @@ func main() { //Read provided config cfgFilePtr := flag.String("metrics", "../../config/config.yaml", "is a config file") + controllerCfgFilePtr := flag.String("cfg", "./config/config.yaml", "is a config file") flag.Parse() logger.AppLog.Infof("Metricfunction has started with configuration file [%v]", *cfgFilePtr) @@ -70,6 +73,20 @@ func main() { //Start Prometheus client go promclient.StartPrometheusClient(&cfg.Configuration.PrometheusServer) + //controller + rogueIpChan := make(chan controller.RogueIPs, 100) + controller.InitConfigFactory(*controllerCfgFilePtr) + onosClient := controller.OnosService{ + OnosServiceUrl: "http://" + controller.ControllerConfig.Configuration.OnosApiServer.Addr + ":" + + strconv.Itoa(controller.ControllerConfig.Configuration.OnosApiServer.Port), + PollInterval: controller.ControllerConfig.Configuration.OnosApiServer.PollInterval, + } + + controller.RogueChannel = rogueIpChan + + go onosClient.GetRogueIPs(rogueIpChan) + go controller.RogueIPHandler(rogueIpChan) + //Go Pprofiling debugProfPort := cfg.Configuration.DebugProfile.Port if debugProfPort != 0 { diff --git a/go.mod b/go.mod index 06ceaa5..537487d 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,8 @@ module github.com/omec-project/metricfunc go 1.18 +replace github.com/omec-project/metricfunc => ./ + require ( github.com/antonfisher/nested-logrus-formatter v1.3.1 github.com/gin-gonic/gin v1.8.1 @@ -11,6 +13,7 @@ require ( github.com/prometheus/client_golang v1.13.0 github.com/segmentio/kafka-go v0.4.35 github.com/sirupsen/logrus v1.7.0 + golang.org/x/net v0.0.0-20220706163947-c90051bbdb60 gopkg.in/yaml.v2 v2.4.0 ) @@ -42,7 +45,6 @@ require ( github.com/prometheus/procfs v0.8.0 // indirect github.com/ugorji/go/codec v1.2.7 // indirect golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect - golang.org/x/net v0.0.0-20220706163947-c90051bbdb60 // indirect golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect golang.org/x/text v0.3.7 // indirect diff --git a/internal/metricdata/subscriber.go b/internal/metricdata/subscriber.go index 396661d..d32a117 100644 --- a/internal/metricdata/subscriber.go +++ b/internal/metricdata/subscriber.go @@ -112,6 +112,18 @@ func GetSubscriber(key string) (*metricinfo.CoreSubscriber, error) { return nil, fmt.Errorf("subscriber with key [%v] not found ", key) } +func GetSubscriberImsiFromIpAddr(ipaddr string) (*metricinfo.CoreSubscriber, error) { + metricData.SubLock.RLock() + defer metricData.SubLock.RUnlock() + for imsi, sub := range metricData.Subscribers { + if sub.IPAddress == ipaddr { + logger.CacheLog.Infof("found subscriber with ip-addr [%s], imsi [%s]", ipaddr, imsi) + return sub, nil + } + } + return nil, fmt.Errorf("subscriber with ip-addr [%v] not found ", ipaddr) +} + func GetSubscriberAll() []string { imsis := []string{} metricData.SubLock.RLock() diff --git a/logger/logger.go b/logger/logger.go index 76929ff..82206f2 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -12,12 +12,13 @@ import ( ) var ( - log *logrus.Logger - ApiSrvLog *logrus.Entry - GinLog *logrus.Entry - CacheLog *logrus.Entry - PromLog *logrus.Entry - AppLog *logrus.Entry + log *logrus.Logger + ApiSrvLog *logrus.Entry + GinLog *logrus.Entry + CacheLog *logrus.Entry + PromLog *logrus.Entry + AppLog *logrus.Entry + ControllerLog *logrus.Entry ) func init() { @@ -37,6 +38,7 @@ func init() { CacheLog = log.WithFields(logrus.Fields{"component": "MetricFunc", "category": "Cache"}) PromLog = log.WithFields(logrus.Fields{"component": "MetricFunc", "category": "Prometheus"}) AppLog = log.WithFields(logrus.Fields{"component": "MetricFunc", "category": "App"}) + ControllerLog = log.WithFields(logrus.Fields{"component": "Controller", "category": "App"}) }