diff --git a/cmd/root.go b/cmd/root.go index 24a5fed67463871d5a6360faa32b02f5b77cecf2..66cdc3210192b7d22bfd981685f68c3d5df235f3 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -32,6 +32,7 @@ package cmd import ( "code.fbi.h-da.de/cocsn/gosdn/nucleus" + "context" "fmt" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -46,6 +47,7 @@ var username string var password string var address string var loglevel string +var grpcPort string // rootCmd represents the base command when called without any subcommands @@ -54,8 +56,9 @@ var rootCmd = &cobra.Command{ Short: "starts the gosdn controller", Long: `Set GOSDN_DEBUG environment variable to enalbe debug logging.`, RunE: func(cmd *cobra.Command, args []string) error { - rc := make(chan bool) - return nucleus.StartAndRun(rc) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + return nucleus.Run(ctx) }, } @@ -63,7 +66,9 @@ var rootCmd = &cobra.Command{ // This is called by main.main(). It only needs to happen once to the rootCmd. func Execute() { if err := rootCmd.Execute(); err != nil { - fmt.Println(err) + log.WithFields(log.Fields{ + + }).Error(err) os.Exit(1) } } @@ -80,6 +85,8 @@ func init() { rootCmd.PersistentFlags().StringVarP(&password, "password", "p", "arista", "password for a gnmi resource") rootCmd.PersistentFlags().StringVarP(&username, "address", "a", "ceos-cocsn.apps.ocp.fbi.h-da.de:6030", "address to a gnmi resource") rootCmd.PersistentFlags().StringVarP(&loglevel, "log-level", "l", "", "log level 'debug' or 'trace'") + + rootCmd.Flags().StringVar(&grpcPort, "grpc-port", "55055", "port for gRPC NBI") } @@ -90,6 +97,8 @@ func initConfig() { viper.SetConfigFile(cfgFile) } else { viper.AddConfigPath("./configs") + viper.AddConfigPath("/usr/local/etc/gosdn/") + viper.SetConfigType("toml") viper.SetConfigName("gosdn") } @@ -100,7 +109,9 @@ func initConfig() { fmt.Println("Using config file:", viper.ConfigFileUsed()) } - loglevel = viper.GetString("GOSDN_DEBUG") + viper.SetDefault("socket", ":" + grpcPort) + + loglevel = viper.GetString("GOSDN_LOG") log.SetReportCaller(true) switch loglevel { case "trace": diff --git a/nucleus/controller.go b/nucleus/controller.go index e503fb60e5f8fa45f60b5d557b7c84a7af530f71..f7477d6baf0dd5514442c1424d176a1ce0676179 100644 --- a/nucleus/controller.go +++ b/nucleus/controller.go @@ -1,12 +1,11 @@ package nucleus import ( - "github.com/google/uuid" - "os" - "code.fbi.h-da.de/cocsn/gosdn/database" + "context" + "github.com/google/uuid" log "github.com/sirupsen/logrus" - "github.com/spf13/viper" + "time" ) // Core is the representation of the controllers core @@ -14,13 +13,19 @@ type Core struct { // deprecated database database.Database - pndc pndStore - sbic sbiStore - IsRunning chan bool + pndc pndStore + sbic sbiStore } +var c *Core + //Initialize does start-up housekeeping like reading controller config files -func (c *Core) Initialize(IsRunningChannel chan bool) error { +func initialize() error { + c = &Core{ + database: database.Database{}, + pndc: pndStore{}, + sbic: sbiStore{}, + } c.sbic = sbiStore{ store{}, } @@ -28,53 +33,48 @@ func (c *Core) Initialize(IsRunningChannel chan bool) error { store{}, } - // Set config defaults - viper.SetDefault("socket", ":55055") - - // Set config path and read config - viper.SetConfigName("gosdn") - viper.SetConfigType("toml") - viper.AddConfigPath("/usr/local/etc/gosdn/") - viper.AddConfigPath("./configs/") - err := viper.ReadInConfig() - if err != nil { + if err := createSouthboundInterfaces(); err != nil { return err } - c.AttachDatabase() - if err := c.CreateSouthboundInterfaces(); err != nil { + // TODO: Start grpc listener here + + if err := httpApi(); err != nil { return err } - c.IsRunning = IsRunningChannel + attachDatabase() + return nil } // deprecated // AttachDatabase connects to the database and passes the connection to the controller core -func (c *Core) AttachDatabase() { +func attachDatabase() { c.database = database.NewDatabaseClient() } // CreateSouthboundInterfaces initializes the controller with its supported SBIs -func (c *Core) CreateSouthboundInterfaces() error { - if err := c.sbic.add(&OpenConfig{id: uuid.New()}); err != nil { - return err - } +func createSouthboundInterfaces() error { if err := c.sbic.add(&OpenConfig{id: uuid.New()}); err != nil { return err } return nil } -// Shutdown waits for the shutdown signal and gracefully shuts down once it arrived -func (c *Core) Shutdown() { - <-c.IsRunning - log.Info("Received shutdown signal. Shutting down") +func Run(ctx context.Context) error { + if err := initialize(); err != nil { + log.WithFields(log.Fields{ - err := viper.WriteConfig() - if err != nil { - log.Fatal(err) + }).Error(err) + return err + } + log.WithFields(log.Fields{}).Info("initialisation finished") + for { + select { + case <-ctx.Done(): + return nil + case <-time.Tick(time.Minute): + log.Debug("up and running") + } } - log.Info("Shutdown complete") - os.Exit(0) } diff --git a/nucleus/http.go b/nucleus/http.go new file mode 100644 index 0000000000000000000000000000000000000000..390c123ed25e47ba8bd2e5981691fd36970754fa --- /dev/null +++ b/nucleus/http.go @@ -0,0 +1,124 @@ +package nucleus + +import ( + "code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi" + "fmt" + "github.com/google/uuid" + gpb "github.com/openconfig/gnmi/proto/gnmi" + log "github.com/sirupsen/logrus" + "net/http" + "net/url" +) + +const basePath = "/api" + +func httpApi() (err error) { + id := c.sbic.UUIDs()[0] + sbi, err := c.sbic.get(id) + if err != nil { + return + } + pnd, err := NewPND("http", "http base pnd", uuid.New(), sbi) + if err != nil { + return + } + err = c.pndc.add(pnd) + if err != nil { + return + } + + httpHandler := func(writer http.ResponseWriter, request *http.Request) { + log.WithFields(log.Fields{ + "request": request, + }).Debug("incoming request") + + query, err := url.ParseQuery(request.URL.RawQuery) + if err != nil { + log.Error(err) + return + } + + id, err := uuid.Parse(query.Get("uuid")) + if err != nil { + log.Error(err) + } + + switch query.Get("q") { + case "addDevice": + d, err := NewDevice(sbi, &GnmiTransportOptions{ + Config: gnmi.Config{ + Addr: query.Get("address"), + Password: query.Get("password"), + Username: query.Get("username"), + Encoding: gpb.Encoding_JSON_IETF, + }, + SetNode: sbi.SetNode(), + Unmarshal: sbi.(*OpenConfig).Unmarshal(), + RespChan: make(chan *gpb.SubscribeResponse), + }) + err = pnd.AddDevice(d) + if err != nil { + writer.WriteHeader(http.StatusBadRequest) + log.Error(err) + return + } + writer.WriteHeader(http.StatusCreated) + case "request": + err = pnd.Request(id, query.Get("path")) + if err != nil { + switch err.(type) { + case *ErrNotFound: + writer.WriteHeader(http.StatusNotFound) + default: + writer.WriteHeader(http.StatusInternalServerError) + } + log.Error(err) + return + } + writer.WriteHeader(http.StatusOK) + case "requestAll": + err = pnd.RequestAll(query.Get("path")) + if err != nil { + switch err.(type) { + case *ErrNotFound: + writer.WriteHeader(http.StatusNotFound) + default: + writer.WriteHeader(http.StatusInternalServerError) + } + log.Error(err) + return + } + writer.WriteHeader(http.StatusOK) + case "getDevice": + device, err := pnd.MarshalDevice(id) + if err != nil { + switch err.(type) { + case *ErrNotFound: + writer.WriteHeader(http.StatusNotFound) + default: + writer.WriteHeader(http.StatusInternalServerError) + } + log.Error(err) + return + } + writer.Header().Set("Content-Type", "application/json") + fmt.Fprintf(writer, "%v", device) + case "getIDs": + ids := pnd.(*pndImplementation).devices.UUIDs() + for i, id := range ids { + fmt.Fprintf(writer, "%v: %v\n", i+1, id) + } + default: + writer.WriteHeader(http.StatusBadRequest) + } + } + http.HandleFunc(basePath, httpHandler) + + go func() { + err = http.ListenAndServe(":8080", nil) + if err != nil { + return + } + }() + return nil +} diff --git a/nucleus/nucleus-core.go b/nucleus/nucleus-core.go deleted file mode 100644 index 1256182d1ab456247945d7af5c5242ad3dd8a04d..0000000000000000000000000000000000000000 --- a/nucleus/nucleus-core.go +++ /dev/null @@ -1,28 +0,0 @@ -package nucleus - -import ( - "time" - - log "github.com/sirupsen/logrus" -) - -//StartAndRun starts the gosdn core and auxiliary services. -func StartAndRun(IsRunningChannel chan bool) error { - log.Info("This is the network superintendent...") - log.Info("Starting my ducks") - - // Initialize the Core - core := Core{} - if err := core.Initialize(IsRunningChannel); err != nil { - return err - } - go core.Shutdown() - - log.Info("and ready for take off") - - //Just to produce some signs of vitality... - for { - time.Sleep(10 * time.Second) - log.Debug("Still alive...") - } -}