From 080e4a30ba31ef50d769e445567f4ab32b098666 Mon Sep 17 00:00:00 2001 From: Vijaya Tiruveedula Date: Tue, 7 Feb 2023 17:36:45 +0530 Subject: [PATCH] 1. new folder called controller added 2. controller configuration reads from different config file 3. onosService keep polling for onosapp to get RogueIPs 4. finding IMSI based on IP 5. Post call to RoC to disable sim-card(IMSI) --- api/apiserver/api_handler.go | 15 ++ api/apiserver/routes.go | 7 + cmd/controller/config/config.go | 38 ++++ cmd/controller/config/config.yaml | 22 ++ cmd/controller/controller.go | 326 ++++++++++++++++++++++++++++++ cmd/metricfunc/metricfunc.go | 17 ++ go.mod | 4 +- internal/metricdata/subscriber.go | 12 ++ logger/logger.go | 14 +- 9 files changed, 448 insertions(+), 7 deletions(-) create mode 100644 cmd/controller/config/config.go create mode 100644 cmd/controller/config/config.yaml create mode 100644 cmd/controller/controller.go 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"}) }