Skip to content
Snippets Groups Projects
Commit d50f431f authored by S.H.'s avatar S.H.
Browse files

Restructure rtdt-manager creation process, adjust event system (subscribe to...

Restructure rtdt-manager creation process, adjust event system (subscribe to user events), proper error handling in many places
parent 4d6d0354
Branches
No related tags found
No related merge requests found
Pipeline #252608 failed
...@@ -7,22 +7,30 @@ import ( ...@@ -7,22 +7,30 @@ import (
"syscall" "syscall"
"code.fbi.h-da.de/danet/gosdn/application-framework/event" "code.fbi.h-da.de/danet/gosdn/application-framework/event"
"code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/clab-config"
"code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/rtdt-auth" "code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/rtdt-auth"
"code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/rtdt-manager" "code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/rtdt-manager"
"code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/util"
"google.golang.org/grpc" "google.golang.org/grpc"
) )
// An App manages one realnet instance of gosdn and one (or potentially more) twin // An App manages one realnet instance of gosdn and one (or potentially more) twin
type App struct { type App struct {
conn *grpc.ClientConn // connection to realnet gosdn gosdnPath string // Absolute root of gosdn repo on system
stopChan chan os.Signal conn *grpc.ClientConn // connection to realnet gosdn
managers []*rtdtmanager.RtdtManager // future consideration: launch multiple siblings stopChan chan os.Signal
managers []*rtdtmanager.RtdtManager // future consideration: launch multiple siblings
} }
func NewApp(conn *grpc.ClientConn, auth *rtdt_auth.RtdtAuth) *App { func NewApp(conn *grpc.ClientConn, auth *rtdt_auth.RtdtAuth) *App {
gosdnPath, err := util.GenerateGosdnPath()
if err != nil {
return nil
}
return &App{ return &App{
conn: conn, gosdnPath: gosdnPath,
stopChan: make(chan os.Signal), // TODO: Should this be shared globally? conn: conn,
stopChan: make(chan os.Signal), // TODO: Should this be shared globally?
} }
} }
...@@ -30,6 +38,10 @@ func (app *App) AddManager(rMan *rtdtmanager.RtdtManager) { ...@@ -30,6 +38,10 @@ func (app *App) AddManager(rMan *rtdtmanager.RtdtManager) {
app.managers = append(app.managers, rMan) app.managers = append(app.managers, rMan)
} }
func (app App) GetGosdnPath() string {
return app.gosdnPath
}
// Run the app with manager, catch events and react to them // Run the app with manager, catch events and react to them
func (app *App) Run() error { func (app *App) Run() error {
if len(app.managers) == 0 { if len(app.managers) == 0 {
...@@ -39,15 +51,21 @@ func (app *App) Run() error { ...@@ -39,15 +51,21 @@ func (app *App) Run() error {
signal.Notify(app.stopChan, os.Interrupt, syscall.SIGSTOP) signal.Notify(app.stopChan, os.Interrupt, syscall.SIGSTOP)
// Run until SIGINT, SIGTERM is received // Run until SIGINT, SIGTERM is received
// Based on "code.fbi.h-da.de/danet/gosdn/applications/hostname-checker/app.go" // Based on "code.fbi.h-da.de/danet/gosdn/applications/hostname-checker/app.go"
var forever chan struct{} forever := make(chan struct{})
go func() { go func() {
for { for {
select { select {
// idea: make channels for different events? // idea: make channels for different events?
case stop := <-app.stopChan: case stop := <-app.stopChan:
// this takes full path now
err := clabconfig.ClabDestroy(app.managers[0].GetClabTwinFilename())
if err != nil {
fmt.Printf("Couldn't clean up twin: %v\n", err)
}
close(forever) close(forever)
fmt.Print("Received SIGINT/SIGSTOP, quitting..") fmt.Print("Received SIGINT/SIGSTOP, quitting..\n")
_ = stop _ = stop
return return
} }
......
...@@ -4,11 +4,16 @@ import ( ...@@ -4,11 +4,16 @@ import (
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
"os/signal"
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
"syscall"
"time"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
util "code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/util"
) )
// 1st level (Root entry) // 1st level (Root entry)
...@@ -50,6 +55,17 @@ type Link struct { ...@@ -50,6 +55,17 @@ type Link struct {
Endpoints []string `yaml:"endpoints"` Endpoints []string `yaml:"endpoints"`
} }
// return absolute clab config path based on gosdn root
// should return /home/user/path/to/gosdn/dev_env_data/clab
func ClabConfigPath() (string, error) {
gosdnPath, err := util.GenerateGosdnPath()
if err != nil {
return "", fmt.Errorf("Couldn't get Gosdn Path: %w\n", err)
}
return filepath.Join(gosdnPath, "/dev_env_data/clab"), nil
}
// Read file and parse into ClabConfig struct // Read file and parse into ClabConfig struct
func LoadConfig(filename string) (*ClabConfig, error) { func LoadConfig(filename string) (*ClabConfig, error) {
absFilepath, err := filepath.Abs(filename) absFilepath, err := filepath.Abs(filename)
...@@ -142,14 +158,14 @@ func DeriveConfig(clabconfig *ClabConfig, newIPv4Subnet, newIPv6Subnet string, p ...@@ -142,14 +158,14 @@ func DeriveConfig(clabconfig *ClabConfig, newIPv4Subnet, newIPv6Subnet string, p
} }
// WriteConfig writes the Config struct to a YAML file // WriteConfig writes the Config struct to a YAML file
func WriteConfig(filename string, dirname string, config *ClabConfig) error { // Takes config file as absolute path
func WriteConfig(filename string, config *ClabConfig) error {
data, err := yaml.Marshal(config) data, err := yaml.Marshal(config)
if err != nil { if err != nil {
return fmt.Errorf("failed to marshal YAML: %w", err) return fmt.Errorf("failed to marshal YAML: %w", err)
} }
fullPath := filepath.Join(dirname, filename) err = os.WriteFile(filename, data, 0644)
err = os.WriteFile(fullPath, data, 0644)
if err != nil { if err != nil {
return fmt.Errorf("failed to write file: %w", err) return fmt.Errorf("failed to write file: %w", err)
} }
...@@ -157,12 +173,27 @@ func WriteConfig(filename string, dirname string, config *ClabConfig) error { ...@@ -157,12 +173,27 @@ func WriteConfig(filename string, dirname string, config *ClabConfig) error {
return nil return nil
} }
func ClabDeploy(filename string, dirname string, stopchan chan os.Signal) error { func ClabDestroy(fullPath string) error {
fullPath := filepath.Join(dirname, filename) fmt.Println("Trying to destroy twin first")
cmd := exec.Command("sudo", "containerlab", "destroy", "-t", fullPath)
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("Couldn't destroy clab environment: %w", err)
}
fmt.Println("Output of containerlab: ", string(output))
time.Sleep(20 * time.Second)
return err
}
// Launch a containerlab environment, pass in absolute path of clab yaml
func ClabDeploy(fullPath string) error {
cmd := exec.Command("sudo", "containerlab", "deploy", "-t", fullPath, "--reconfigure") cmd := exec.Command("sudo", "containerlab", "deploy", "-t", fullPath, "--reconfigure")
// Run the command in a Goroutine // Run the command in a Goroutine
done := make(chan error, 1) done := make(chan error, 1)
stopdeploy := make(chan os.Signal, 1)
signal.Notify(stopdeploy, os.Interrupt, syscall.SIGTERM)
go func() { go func() {
output, err := cmd.CombinedOutput() // Use CombinedOutput to capture stdout and stderr output, err := cmd.CombinedOutput() // Use CombinedOutput to capture stdout and stderr
if err != nil { if err != nil {
...@@ -177,11 +208,17 @@ func ClabDeploy(filename string, dirname string, stopchan chan os.Signal) error ...@@ -177,11 +208,17 @@ func ClabDeploy(filename string, dirname string, stopchan chan os.Signal) error
// Wait for the deployment to finish or a signal to stop // Wait for the deployment to finish or a signal to stop
select { select {
case err := <-done: // Command finished case err := <-done: // Command finished
fmt.Println("Successfully deployed the twin")
return err return err
case <-stopchan: // Signal received to interrupt case <-stopdeploy: // Signal received to interrupt
if err := cmd.Process.Kill(); err != nil { if err := cmd.Process.Kill(); err != nil {
fmt.Printf("Failed to kill process: %v\n", err) fmt.Printf("Failed to kill process: %v\n", err)
} }
return fmt.Errorf("deployment interrupted by signal") err := ClabDestroy(fullPath)
if err != nil {
return fmt.Errorf("Deploying containerlab environment was interrupted and couldn't be cleaned up: %v", err)
}
return fmt.Errorf("Deployment interrupted by signal")
} }
} }
name: gosdn_csbi_arista_base
mgmt:
network: gosdn-csbi-arista-base-net
ipv4-subnet: 172.100.0.0/16
ipv6-subnet: 2001:db8::/64
mtu: 1500
topology:
nodes:
plugin-registry:
kind: linux
image: plugin-registry
mgmt-ipv4: 172.100.0.16
gosdn:
kind: linux
image: gosdn
ports:
- 55055:55055
- 8080:8080
- 40000:40000
cmd: --config /app/configs/containerlab-gosdn.toml
mgmt-ipv4: 172.100.0.5
env:
GOSDN_ADMIN_PASSWORD: TestPassword
binds:
- ../../../artifacts/ssl/gosdn:/app/ssl
gnmi-target-switch0:
kind: linux
image: registry.code.fbi.h-da.de/danet/gnmi-target/debian:master
#only for local use
#image: gnmi-target:latest
binds:
- ../../../artifacts/ssl/gnmi-target:/etc/gnmi-target/ssl
ports:
- 7030:7030
cmd: start --ca_file /etc/gnmi-target/ssl/ca.crt --cert /etc/gnmi-target/ssl/certs/gnmi-target-selfsigned.crt --key /etc/gnmi-target/ssl/private/gnmi-target-selfsigned.key
mgmt-ipv4: 172.100.0.11
startup-delay: 5
gnmi-target-switch1:
kind: linux
image: registry.code.fbi.h-da.de/danet/gnmi-target/debian:master
#only for local use
#image: gnmi-target:latest
binds:
- ../../../artifacts/ssl/gnmi-target:/etc/gnmi-target/ssl
ports:
- 7031:7030
cmd: start --ca_file /etc/gnmi-target/ssl/ca.crt --cert /etc/gnmi-target/ssl/certs/gnmi-target-selfsigned.crt --key /etc/gnmi-target/ssl/private/gnmi-target-selfsigned.key
mgmt-ipv4: 172.100.0.12
startup-delay: 5
centos0:
kind: linux
image: centos:8
mgmt-ipv4: 172.100.0.3
group: server
centos1:
kind: linux
image: centos:8
mgmt-ipv4: 172.100.0.4
group: server
mongodb:
kind: linux
image: mongo:7
ports:
- 27017:27017
env:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: example
mgmt-ipv4: 172.100.0.13
mongodb-express:
kind: linux
image: mongo-express:1.0.2
ports:
- 8081:8081
env:
ME_CONFIG_MONGODB_AUTH_USERNAME: root
ME_CONFIG_MONGODB_AUTH_PASSWORD: example
ME_CONFIG_MONGODB_SERVER: mongodb
ME_CONFIG_BASICAUTH: "false"
mgmt-ipv4: 172.100.0.14
rabbitmq:
kind: linux
image: rabbitmq:3-management
ports:
- 127.0.0.1:5672:5672
- 127.0.0.1:15672:15672
mgmt-ipv4: 172.100.0.15
links:
- endpoints: ["gnmi-target-switch0:eth1", "gnmi-target-switch1:eth1"]
- endpoints: ["gnmi-target-switch0:eth2", "centos0:eth1"]
- endpoints: ["gnmi-target-switch1:eth2", "centos1:eth1"]
...@@ -25,7 +25,7 @@ topology: ...@@ -25,7 +25,7 @@ topology:
env: env:
GOSDN_ADMIN_PASSWORD: TestPassword GOSDN_ADMIN_PASSWORD: TestPassword
binds: binds:
- ../../artifacts/ssl/gosdn:/app/ssl - ../../../artifacts/ssl/gosdn:/app/ssl
gnmi-target-switch0: gnmi-target-switch0:
kind: linux kind: linux
...@@ -33,7 +33,7 @@ topology: ...@@ -33,7 +33,7 @@ topology:
#only for local use #only for local use
#image: gnmi-target:latest #image: gnmi-target:latest
binds: binds:
- ../../artifacts/ssl/gnmi-target:/etc/gnmi-target/ssl - ../../../artifacts/ssl/gnmi-target:/etc/gnmi-target/ssl
ports: ports:
- 7030:7030 - 7030:7030
cmd: start --ca_file /etc/gnmi-target/ssl/ca.crt --cert /etc/gnmi-target/ssl/certs/gnmi-target-selfsigned.crt --key /etc/gnmi-target/ssl/private/gnmi-target-selfsigned.key cmd: start --ca_file /etc/gnmi-target/ssl/ca.crt --cert /etc/gnmi-target/ssl/certs/gnmi-target-selfsigned.crt --key /etc/gnmi-target/ssl/private/gnmi-target-selfsigned.key
...@@ -46,7 +46,7 @@ topology: ...@@ -46,7 +46,7 @@ topology:
#only for local use #only for local use
#image: gnmi-target:latest #image: gnmi-target:latest
binds: binds:
- ../../artifacts/ssl/gnmi-target:/etc/gnmi-target/ssl - ../../../artifacts/ssl/gnmi-target:/etc/gnmi-target/ssl
ports: ports:
- 7031:7030 - 7031:7030
cmd: start --ca_file /etc/gnmi-target/ssl/ca.crt --cert /etc/gnmi-target/ssl/certs/gnmi-target-selfsigned.crt --key /etc/gnmi-target/ssl/private/gnmi-target-selfsigned.key cmd: start --ca_file /etc/gnmi-target/ssl/ca.crt --cert /etc/gnmi-target/ssl/certs/gnmi-target-selfsigned.crt --key /etc/gnmi-target/ssl/private/gnmi-target-selfsigned.key
......
...@@ -35,14 +35,21 @@ func main() { ...@@ -35,14 +35,21 @@ func main() {
defer conn.Close() defer conn.Close()
auth := rtdt_auth.NewRtdtAuth(user, url, pass, conn) // logs in and stores token auth := rtdt_auth.NewRtdtAuth(user, url, pass, conn) // logs in and stores token
if auth == nil {
fmt.Println("Couldn't log in to gosdn, quitting!")
return
}
rtdt_app := app.NewApp(conn, auth) rtdt_app := app.NewApp(conn, auth)
rtdtMan := RtdtMan.NewRtdtManager(conn, auth, topology_file) rtdtMan := RtdtMan.NewRtdtManager(conn, auth, topology_file)
if rtdtMan == nil { if rtdtMan == nil {
fmt.Println("Couldn't initialize rtdt-manager, quitting!") fmt.Println("Couldn't initialize rtdt-manager, quitting!")
return return
} }
// Register manager with app and run
rtdt_app.AddManager(rtdtMan) rtdt_app.AddManager(rtdtMan)
_ = rtdtMan // If twin should be launched, uncomment:
//rtdtMan.LaunchTwin("172.101.0.0/16", "2001:db9::/64", "twin")
err = rtdt_app.Run() err = rtdt_app.Run()
if err != nil { if err != nil {
......
...@@ -3,11 +3,8 @@ package rtdtmanager ...@@ -3,11 +3,8 @@ package rtdtmanager
import ( import (
"fmt" "fmt"
"os" "os"
"os/exec"
"os/signal" "os/signal"
"path"
"path/filepath" "path/filepath"
"strings"
"syscall" "syscall"
"time" "time"
...@@ -19,53 +16,27 @@ import ( ...@@ -19,53 +16,27 @@ import (
confManPb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/configurationmanagement" confManPb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/configurationmanagement"
pnd "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/pnd" pnd "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/pnd"
topoPb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/topology" topoPb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/topology"
"code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/clab-config" clabconfig "code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/clab-config"
"code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/rtdt-auth" "code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/rtdt-auth"
"code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/rtdt-topology" util "code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/util"
"code.fbi.h-da.de/danet/gosdn/applications/venv-manager/containerlab"
"google.golang.org/grpc" "google.golang.org/grpc"
"gopkg.in/yaml.v3"
) )
func now() int64 {
return int64(time.Now().Nanosecond())
}
func getGosdnPath() (string, error) {
var execPath string
var absExecPath string
var err error
if execPath, err = os.Executable(); err != nil {
return "", nil
}
if absExecPath, err = filepath.Abs(execPath); err != nil {
return "", nil
}
executableDir := filepath.Dir(absExecPath)
projectRoot := filepath.Dir(executableDir)
fmt.Println("Project Root Path:", projectRoot)
return projectRoot, nil
}
type RtdtManager struct { type RtdtManager struct {
auth *rtdt_auth.RtdtAuth // auth struct for realnet gosdn auth *rtdt_auth.RtdtAuth // auth struct for realnet gosdn
conn *grpc.ClientConn // connection to twin's gosdn instance conn *grpc.ClientConn // connection to twin's gosdn instance
Pnd *pnd.PrincipalNetworkDomain // PND for realnet gosdn Pnd *pnd.PrincipalNetworkDomain // PND for realnet gosdn
topo *topoPb.Topology // Topology with which to create clab.yaml topo *topoPb.Topology // Topology with which to create clab.yaml
sdnConfig string sdnConfig string // TODO
clabFilename string // Clab file that exemplifies the topology clabFilename string // Clab file that exemplifies the topology of realnet
topoData *containerlab.YamlStruct clabTwinFilename string
clabData *clabconfig.ClabConfig clabData *clabconfig.ClabConfig // Represents the yaml config file as data structure
eventService event.ServiceInterface // Receive events from realnet gosdn eventService event.ServiceInterface // Receive events from realnet gosdn (not used yet)
stopChan chan os.Signal // Global stop channel TODO Can I use that like this? stopChan chan os.Signal // Global stop channel TODO Can I use that like this?
// TODO auth and conn for virtual net? // TODO auth and conn for virtual net?
} }
func NewRtdtManager(conn *grpc.ClientConn, auth *rtdt_auth.RtdtAuth, clabFilename string) *RtdtManager { func NewRtdtManager(conn *grpc.ClientConn, auth *rtdt_auth.RtdtAuth, clabFilename string) *RtdtManager {
projectRoot, _ := getGosdnPath()
rMan := RtdtManager{ rMan := RtdtManager{
conn: conn, conn: conn,
auth: auth, auth: auth,
...@@ -78,55 +49,30 @@ func NewRtdtManager(conn *grpc.ClientConn, auth *rtdt_auth.RtdtAuth, clabFilenam ...@@ -78,55 +49,30 @@ func NewRtdtManager(conn *grpc.ClientConn, auth *rtdt_auth.RtdtAuth, clabFilenam
fmt.Println(err) fmt.Println(err)
return nil return nil
} }
// Event system for realnet gosdn
if err := rMan.initEventSystem(); err != nil {
fmt.Println("Failed to initialize event system: ", err)
return nil
}
// Set up the twin here
// The topology of the currently running realnet needs to be parsed. We get this // The topology of the currently running realnet needs to be parsed. We get this
// from the clab.yaml file that was used to create it, that means that --topolog needs to point // from the clab.yaml file that was used to create it, that means that --topolog needs to point
// to it // to it
// TODO Implement
var err error var err error
if rMan.clabData, err = clabconfig.LoadConfig(rMan.clabFilename); err != nil { if rMan.clabData, err = clabconfig.LoadConfig(rMan.clabFilename); err != nil {
fmt.Println(err) fmt.Println(err)
return nil return nil
} }
var derivedConfig *clabconfig.ClabConfig
if derivedConfig, err = clabconfig.DeriveConfig(rMan.clabData, "172.101.0.0/16", "2001:db9::/64", "twin"); err != nil {
fmt.Println(err)
return nil
}
clabConfigDir := filepath.Join(projectRoot, "dev_env_data/clab")
if err := clabconfig.WriteConfig("clab-derived.yaml", clabConfigDir, derivedConfig); err != nil {
return nil
}
if err := clabconfig.ClabDeploy("clab-derived.yaml", clabConfigDir, rMan.stopChan); err != nil {
return nil
}
fmt.Println("Success: RtdtManager created") fmt.Println("Success: RtdtManager created")
return &rMan return &rMan
// Now we need to apply the topology to gosdn because it is not done automatically when creating
// devices
if err := rtdt_topology.ApplyTopology(rMan.topoData); err != nil {
fmt.Println(err)
return nil
}
// Write the topology to a file to check
if err := rMan.writeModifiedTopologyToFile(rMan.topoData); err != nil {
fmt.Println(err)
return nil
}
if err := rMan.initEventSystem(); err != nil {
fmt.Println(err)
return nil
}
// if err := rMan.manageConfig("import"); err != nil {
// fmt.Println(err)
// return nil
// }
return nil
} }
func (rMan *RtdtManager) fetchPndUUID() error { func (rMan *RtdtManager) fetchPndUUID() error {
pndService := pnd.NewPndServiceClient(rMan.conn) pndService := pnd.NewPndServiceClient(rMan.conn)
ctx := rMan.auth.CreateContextWithAuthorization() ctx := rMan.auth.CreateContextWithAuthorization()
pndResponse, err := pndService.GetPndList(ctx, &pnd.GetPndListRequest{Timestamp: now()}) pndResponse, err := pndService.GetPndList(ctx, &pnd.GetPndListRequest{Timestamp: util.Now()})
if err != nil { if err != nil {
return fmt.Errorf("Failed to retrieve PND information: %w", err) return fmt.Errorf("Failed to retrieve PND information: %w", err)
} }
...@@ -142,44 +88,45 @@ func (rMan *RtdtManager) fetchPndUUID() error { ...@@ -142,44 +88,45 @@ func (rMan *RtdtManager) fetchPndUUID() error {
func (rMan *RtdtManager) applyTopology() error { func (rMan *RtdtManager) applyTopology() error {
topoService := topoPb.NewTopologyServiceClient(rMan.conn) topoService := topoPb.NewTopologyServiceClient(rMan.conn)
ctx := rMan.auth.CreateContextWithAuthorization() ctx := rMan.auth.CreateContextWithAuthorization()
topoService.AddLink(ctx, &topoPb.AddLinkRequest{Timestamp: now()}) topoService.AddLink(ctx, &topoPb.AddLinkRequest{Timestamp: util.Now()})
return nil return nil
} }
// \cite venv-manager // \cite venv-manager
// Keep for later
// Write a clab.yaml file to launch a different // Write a clab.yaml file to launch a different
func (rMan *RtdtManager) writeModifiedTopologyToFile(clabStruct *containerlab.YamlStruct) error { // func (rMan *RtdtManager) writeModifiedTopologyToFile(clabStruct *containerlab.YamlStruct) error {
rMan.topoData.Mgmt.Network = "gosdn-csbi-arist-twin-net" // rMan.topoData.Mgmt.Network = "gosdn-csbi-arist-twin-net"
//
splitMainNetwork := strings.Split(rMan.topoData.Mgmt.Ipv4Subnet, ".") // splitMainNetwork := strings.Split(rMan.topoData.Mgmt.Ipv4Subnet, ".")
splitMainNetwork[1] = "101" // splitMainNetwork[1] = "101"
rMan.topoData.Mgmt.Ipv4Subnet = strings.Join(splitMainNetwork, ".") // rMan.topoData.Mgmt.Ipv4Subnet = strings.Join(splitMainNetwork, ".")
//
splitMainNetworkIPv6 := strings.Split(rMan.topoData.Mgmt.Ipv6Subnet, ":") // splitMainNetworkIPv6 := strings.Split(rMan.topoData.Mgmt.Ipv6Subnet, ":")
splitMainNetworkIPv6[1] = "db9" // splitMainNetworkIPv6[1] = "db9"
rMan.topoData.Mgmt.Ipv6Subnet = strings.Join(splitMainNetworkIPv6, ":") // rMan.topoData.Mgmt.Ipv6Subnet = strings.Join(splitMainNetworkIPv6, ":")
//
// Different network for our twin // // Different network for our twin
for i, node := range rMan.topoData.Topology.Nodes { // for i, node := range rMan.topoData.Topology.Nodes {
splitIPv4 := strings.Split(node.MgmtIpv4, ".") // splitIPv4 := strings.Split(node.MgmtIpv4, ".")
splitIPv4[1] = "101" // splitIPv4[1] = "101"
node.MgmtIpv4 = strings.Join(splitIPv4, ".") // node.MgmtIpv4 = strings.Join(splitIPv4, ".")
rMan.topoData.Topology.Nodes[i] = node // rMan.topoData.Topology.Nodes[i] = node
} // }
yaml, err := yaml.Marshal(clabStruct) // yaml, err := yaml.Marshal(clabStruct)
if err != nil { // if err != nil {
return err // return err
} // }
//
fname := "./topo.clab.tmp.yaml" // fname := "./topo.clab.tmp.yaml"
err = os.WriteFile(fname, yaml, 0600) // err = os.WriteFile(fname, yaml, 0600)
if err != nil { // if err != nil {
return err // return err
} // }
//
return nil // return nil
} // }
// This retrieves the topology from the running realnet gosdn instance // This retrieves the topology from the running realnet gosdn instance
// This is needed to generate the clab file to be used with the virtual net // This is needed to generate the clab file to be used with the virtual net
...@@ -187,7 +134,7 @@ func (rMan *RtdtManager) writeModifiedTopologyToFile(clabStruct *containerlab.Ya ...@@ -187,7 +134,7 @@ func (rMan *RtdtManager) writeModifiedTopologyToFile(clabStruct *containerlab.Ya
func (rMan *RtdtManager) fetchTopology() error { func (rMan *RtdtManager) fetchTopology() error {
topoService := topoPb.NewTopologyServiceClient(rMan.conn) topoService := topoPb.NewTopologyServiceClient(rMan.conn)
ctx := rMan.auth.CreateContextWithAuthorization() ctx := rMan.auth.CreateContextWithAuthorization()
topoResponse, err := topoService.GetTopology(ctx, &topoPb.GetTopologyRequest{Timestamp: int64(time.Now().Nanosecond())}) topoResponse, err := topoService.GetTopology(ctx, &topoPb.GetTopologyRequest{Timestamp: util.Now()})
if err != nil { if err != nil {
return fmt.Errorf("Failed to retrieve Topology: %w", err) return fmt.Errorf("Failed to retrieve Topology: %w", err)
} }
...@@ -201,27 +148,32 @@ func (rMan *RtdtManager) fetchTopology() error { ...@@ -201,27 +148,32 @@ func (rMan *RtdtManager) fetchTopology() error {
return nil return nil
} }
// To launch a new twin, this runs through the following steps:
// - Load the clab config for current realnet gosdn (passed on cli)
// - Derive the config we need for twin
// - Write the config to disk
// - Use that config to call "containerlab deploy"
// Takes
// Launch a second gosdn instance which will manage the virtual network // Launch a second gosdn instance which will manage the virtual network
func (rMan *RtdtManager) LaunchTwin() error { func (r *RtdtManager) LaunchTwin(twinSubnetIPv4, twinSubnetIPv6, twinName string) error {
// First create the clab environment var derivedConfig *clabconfig.ClabConfig
// cmdClab := exec.Command("sudo", "containerlab", "deploy") var clabConfigPath string
// output, err := cmdClab.Output() var err error
// if err != nil {
// return fmt.Errorf("Failed to execute containerlab: %w", err)
// }
// Now launch gosdn and then use the api to register the clab environment with it
exePath, err := os.Executable()
if err != nil {
return fmt.Errorf("Failed to retrieve folder of executable: %w", err)
}
fmt.Printf("The current executable's path is %s\n", exePath)
exeDir := path.Dir(exePath)
_ = exeDir
fmt.Println("Launching gosdn..")
cmdGosdn := exec.Command(filepath.Join(exeDir, "gosdn"))
outputGosdn, err := cmdGosdn.CombinedOutput()
fmt.Printf("Output of gosdn: %s\n", outputGosdn)
if r.clabData == nil {
err = fmt.Errorf("Can't launch a twin without loading a clab yaml file for physical network first")
return err
}
if derivedConfig, err = clabconfig.DeriveConfig(r.clabData, twinSubnetIPv4, twinSubnetIPv6, twinName); err != nil {
return fmt.Errorf("Failed to derive config for twin: %w", err)
}
clabConfigPath, err = clabconfig.ClabConfigPath()
clabConfigFullPath := filepath.Join(clabConfigPath, "twin-clab.yaml")
if err = clabconfig.WriteConfig(clabConfigFullPath, derivedConfig); err != nil {
return fmt.Errorf("Failed to write modified twin clab config to disk: %w", err)
}
// Now run deploy with new config file
clabconfig.ClabDeploy(clabConfigFullPath)
return nil return nil
} }
...@@ -242,7 +194,7 @@ func (rMan *RtdtManager) manageConfig(op string) error { ...@@ -242,7 +194,7 @@ func (rMan *RtdtManager) manageConfig(op string) error {
// Export from rtdt-manager into gosdn controller // Export from rtdt-manager into gosdn controller
case "export": case "export":
fmt.Println("Now exporting sdnConfig into gosdn controller") fmt.Println("Now exporting sdnConfig into gosdn controller")
// Implement me some day //TODO Implement me some day
return nil return nil
default: default:
return fmt.Errorf("Unknown config operation") return fmt.Errorf("Unknown config operation")
...@@ -254,34 +206,53 @@ func (rMan *RtdtManager) manageConfig(op string) error { ...@@ -254,34 +206,53 @@ func (rMan *RtdtManager) manageConfig(op string) error {
func (rMan *RtdtManager) initEventSystem() error { func (rMan *RtdtManager) initEventSystem() error {
ctx := rMan.auth.CreateContextWithAuthorization() ctx := rMan.auth.CreateContextWithAuthorization()
queueCredentials, err := registration.Register(ctx, rMan.auth.GetURL(), "basic-interface-monitoring", "SecurePresharedToken") queueCredentials, err := registration.Register(ctx, rMan.auth.GetURL(), "basic-interface-monitoring", "SecurePresharedToken")
fmt.Println("Acquired queueCredentials: ", queueCredentials)
// TODO: Find out how I can receive the ip address here (it returns rabbitmq) // TODO: Find out how I can receive the ip address here (it returns rabbitmq)
queueCredentials = "amqp://guest:guest@172.100.0.15:5672/" // fix by setting manually queueCredentials = "amqp://guest:guest@172.100.0.15:5672/" // fix by setting manually
if err != nil { if err != nil {
return fmt.Errorf("Encountered error while trying to register event system: %w", err) return fmt.Errorf("Encountered error while trying to register event system: %w", err)
} }
fmt.Println("Trying to register with amqp with following queueCredentials: ", queueCredentials) fmt.Println("Trying to register with amqp with following queueCredentials: ", queueCredentials)
eventService, err := event.NewEventService(queueCredentials, []event.Topic{event.ManagedNetworkElement}) // You have to have one event service for a topic
eventServiceMNE, err := event.NewEventService(queueCredentials, []event.Topic{event.ManagedNetworkElement})
if err != nil { if err != nil {
return fmt.Errorf("Failed to attach to event system: %w", err) return fmt.Errorf("Failed to attach to event system: %w", err)
} }
// Subscribe to a topic and call "callback()" when event happens eventServiceUser, err := event.NewEventService(queueCredentials, []event.Topic{event.User})
// eventService is of type Service and contains a channel if err != nil {
// Callback gets passed the event return fmt.Errorf("Failed to attach to event system: %w", err)
// Possible events: Update, Delete, Subscribe }
// Use this to map desired callbacks to different events
eventService.SubscribeToEventType([]event.TypeToCallbackTuple{ // Can have different callback per type per topic (e.g. adding mne or updating mne)
{Type: event.Update, Callback: rMan.callback}, eventServiceMNE.SubscribeToEventType([]event.TypeToCallbackTuple{
{Type: event.Type(event.Update), Callback: rMan.updateMNECallback},
})
eventServiceUser.SubscribeToEventType([]event.TypeToCallbackTuple{
{Type: event.Type(event.User), Callback: rMan.userEventCallback},
}) })
// Now iterate over all topics of service and create goRoutines // Now iterate over all topics of service and create goRoutines
// that consumes queue // that consumes queue
// This function is supposed to be removed in the future? // This function is supposed to be removed in the future?
eventService.SetupEventReciever(make(chan os.Signal, 1)) // doesn't seem to use channel internally.. eventServiceMNE.SetupEventReciever(make(chan os.Signal, 1)) // doesn't seem to use stop channel internally..
return nil return nil
} }
// Based on currently running network managed by gosdn, launch a new gosdn instance that mirrors it func (rMan *RtdtManager) updateMNECallback(event *event.Event) {
func (rMan *RtdtManager) callback(event *event.Event) { fmt.Println("MNE Event has happened (type: update), hurray!")
fmt.Println("Event has happened, hurray!") fmt.Println("EventID: ", event.ID.ID())
fmt.Println("Event Type: ", event.Type)
}
func (rMan *RtdtManager) userEventCallback(event *event.Event) {
fmt.Println("User Event has happened (type: update), hurray!")
fmt.Println("EventID: ", event.ID.ID()) fmt.Println("EventID: ", event.ID.ID())
fmt.Println("Event Type: ", event.Type) fmt.Println("Event Type: ", event.Type)
} }
// {Get,Set}ers
func (r RtdtManager) GetClabFilename() string {
return r.clabFilename
}
func (r RtdtManager) GetClabTwinFilename() string {
return r.clabTwinFilename
}
package util
import (
"fmt"
"os"
"path/filepath"
"strings"
"time"
)
func Now() int64 {
return int64(time.Now().Nanosecond())
}
func GenerateGosdnPath() (string, error) {
var execPath string
var absExecPath string
var err error
if execPath, err = os.Executable(); err != nil {
return "", err
}
if absExecPath, err = filepath.Abs(execPath); err != nil {
return "", err
}
executableDir := filepath.Dir(absExecPath)
projectRoot := filepath.Dir(executableDir)
return projectRoot, nil
}
// Get the path of a file, if path is a path, just return absolute path
func GetAbsPath(path string) (string, error) {
absPath, err := filepath.Abs(path)
if err != nil {
return "", fmt.Errorf("Failed to resolve absolut path of %s: %w", path, err)
}
if strings.HasSuffix(path, "/") {
return absPath, nil
}
info, err := os.Stat(absPath)
if err != nil {
return "", fmt.Errorf("Failed to stat path: %w", err)
}
// If path is not a directory, make it one
if !info.IsDir() {
absPath = filepath.Dir(absPath)
}
return absPath, nil
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment