Skip to content
Snippets Groups Projects
Commit 95f8805a authored by Manuel Kieweg's avatar Manuel Kieweg
Browse files

added simple http api

parent f19997ae
Branches
Tags
1 merge request!90Develop
...@@ -32,6 +32,7 @@ package cmd ...@@ -32,6 +32,7 @@ package cmd
import ( import (
"code.fbi.h-da.de/cocsn/gosdn/nucleus" "code.fbi.h-da.de/cocsn/gosdn/nucleus"
"context"
"fmt" "fmt"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
...@@ -46,6 +47,7 @@ var username string ...@@ -46,6 +47,7 @@ var username string
var password string var password string
var address string var address string
var loglevel string var loglevel string
var grpcPort string
// rootCmd represents the base command when called without any subcommands // rootCmd represents the base command when called without any subcommands
...@@ -54,8 +56,9 @@ var rootCmd = &cobra.Command{ ...@@ -54,8 +56,9 @@ var rootCmd = &cobra.Command{
Short: "starts the gosdn controller", Short: "starts the gosdn controller",
Long: `Set GOSDN_DEBUG environment variable to enalbe debug logging.`, Long: `Set GOSDN_DEBUG environment variable to enalbe debug logging.`,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
rc := make(chan bool) ctx, cancel := context.WithCancel(context.Background())
return nucleus.StartAndRun(rc) defer cancel()
return nucleus.Run(ctx)
}, },
} }
...@@ -63,7 +66,9 @@ var rootCmd = &cobra.Command{ ...@@ -63,7 +66,9 @@ var rootCmd = &cobra.Command{
// This is called by main.main(). It only needs to happen once to the rootCmd. // This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() { func Execute() {
if err := rootCmd.Execute(); err != nil { if err := rootCmd.Execute(); err != nil {
fmt.Println(err) log.WithFields(log.Fields{
}).Error(err)
os.Exit(1) os.Exit(1)
} }
} }
...@@ -80,6 +85,8 @@ func init() { ...@@ -80,6 +85,8 @@ func init() {
rootCmd.PersistentFlags().StringVarP(&password, "password", "p", "arista", "password for a gnmi resource") 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(&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.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() { ...@@ -90,6 +97,8 @@ func initConfig() {
viper.SetConfigFile(cfgFile) viper.SetConfigFile(cfgFile)
} else { } else {
viper.AddConfigPath("./configs") viper.AddConfigPath("./configs")
viper.AddConfigPath("/usr/local/etc/gosdn/")
viper.SetConfigType("toml")
viper.SetConfigName("gosdn") viper.SetConfigName("gosdn")
} }
...@@ -100,7 +109,9 @@ func initConfig() { ...@@ -100,7 +109,9 @@ func initConfig() {
fmt.Println("Using config file:", viper.ConfigFileUsed()) fmt.Println("Using config file:", viper.ConfigFileUsed())
} }
loglevel = viper.GetString("GOSDN_DEBUG") viper.SetDefault("socket", ":" + grpcPort)
loglevel = viper.GetString("GOSDN_LOG")
log.SetReportCaller(true) log.SetReportCaller(true)
switch loglevel { switch loglevel {
case "trace": case "trace":
......
package nucleus package nucleus
import ( import (
"github.com/google/uuid"
"os"
"code.fbi.h-da.de/cocsn/gosdn/database" "code.fbi.h-da.de/cocsn/gosdn/database"
"context"
"github.com/google/uuid"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/spf13/viper" "time"
) )
// Core is the representation of the controllers core // Core is the representation of the controllers core
...@@ -14,13 +13,19 @@ type Core struct { ...@@ -14,13 +13,19 @@ type Core struct {
// deprecated // deprecated
database database.Database database database.Database
pndc pndStore pndc pndStore
sbic sbiStore sbic sbiStore
IsRunning chan bool
} }
var c *Core
//Initialize does start-up housekeeping like reading controller config files //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{ c.sbic = sbiStore{
store{}, store{},
} }
...@@ -28,53 +33,48 @@ func (c *Core) Initialize(IsRunningChannel chan bool) error { ...@@ -28,53 +33,48 @@ func (c *Core) Initialize(IsRunningChannel chan bool) error {
store{}, store{},
} }
// Set config defaults if err := createSouthboundInterfaces(); err != nil {
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 {
return err return err
} }
c.AttachDatabase() // TODO: Start grpc listener here
if err := c.CreateSouthboundInterfaces(); err != nil {
if err := httpApi(); err != nil {
return err return err
} }
c.IsRunning = IsRunningChannel attachDatabase()
return nil return nil
} }
// deprecated // deprecated
// AttachDatabase connects to the database and passes the connection to the controller core // AttachDatabase connects to the database and passes the connection to the controller core
func (c *Core) AttachDatabase() { func attachDatabase() {
c.database = database.NewDatabaseClient() c.database = database.NewDatabaseClient()
} }
// CreateSouthboundInterfaces initializes the controller with its supported SBIs // CreateSouthboundInterfaces initializes the controller with its supported SBIs
func (c *Core) CreateSouthboundInterfaces() error { func createSouthboundInterfaces() error {
if err := c.sbic.add(&OpenConfig{id: uuid.New()}); err != nil {
return err
}
if err := c.sbic.add(&OpenConfig{id: uuid.New()}); err != nil { if err := c.sbic.add(&OpenConfig{id: uuid.New()}); err != nil {
return err return err
} }
return nil return nil
} }
// Shutdown waits for the shutdown signal and gracefully shuts down once it arrived func Run(ctx context.Context) error {
func (c *Core) Shutdown() { if err := initialize(); err != nil {
<-c.IsRunning log.WithFields(log.Fields{
log.Info("Received shutdown signal. Shutting down")
err := viper.WriteConfig() }).Error(err)
if err != nil { return err
log.Fatal(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)
} }
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
}
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...")
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment