diff --git a/applications/rtdt-manager/app/app.go b/applications/rtdt-manager/app/app.go index f4dec1cf8714e9fa354067cd1906ed32e3050f23..43572373a2d1ee83559c9ce2a6212fc61b7bbe1f 100644 --- a/applications/rtdt-manager/app/app.go +++ b/applications/rtdt-manager/app/app.go @@ -7,22 +7,30 @@ import ( "syscall" "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-manager" + "code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/util" "google.golang.org/grpc" ) // An App manages one realnet instance of gosdn and one (or potentially more) twin type App struct { - conn *grpc.ClientConn // connection to realnet gosdn - stopChan chan os.Signal - managers []*rtdtmanager.RtdtManager // future consideration: launch multiple siblings + gosdnPath string // Absolute root of gosdn repo on system + conn *grpc.ClientConn // connection to realnet gosdn + stopChan chan os.Signal + managers []*rtdtmanager.RtdtManager // future consideration: launch multiple siblings } func NewApp(conn *grpc.ClientConn, auth *rtdt_auth.RtdtAuth) *App { + gosdnPath, err := util.GenerateGosdnPath() + if err != nil { + return nil + } return &App{ - conn: conn, - stopChan: make(chan os.Signal), // TODO: Should this be shared globally? + gosdnPath: gosdnPath, + conn: conn, + stopChan: make(chan os.Signal), // TODO: Should this be shared globally? } } @@ -30,6 +38,10 @@ func (app *App) AddManager(rMan *rtdtmanager.RtdtManager) { 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 func (app *App) Run() error { if len(app.managers) == 0 { @@ -39,15 +51,21 @@ func (app *App) Run() error { signal.Notify(app.stopChan, os.Interrupt, syscall.SIGSTOP) // Run until SIGINT, SIGTERM is received - // Based on "code.fbi.h-da.de/danet/gosdn/applications/hostname-checker/app.go" - var forever chan struct{} + // Based on "code.fbi.h-da.de/danet/gosdn/applications/hostname-checker/app.go" + forever := make(chan struct{}) go func() { for { select { // idea: make channels for different events? 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) - fmt.Print("Received SIGINT/SIGSTOP, quitting..") + fmt.Print("Received SIGINT/SIGSTOP, quitting..\n") _ = stop return } diff --git a/applications/rtdt-manager/clab-config/clab-config.go b/applications/rtdt-manager/clab-config/clab-config.go index 97ff714c7cd55045abe44fe40961d713b778f20d..04b694c576b46b3657a47a7a3045c10e00746147 100644 --- a/applications/rtdt-manager/clab-config/clab-config.go +++ b/applications/rtdt-manager/clab-config/clab-config.go @@ -4,11 +4,16 @@ import ( "fmt" "os" "os/exec" + "os/signal" "path/filepath" "strconv" "strings" + "syscall" + "time" "gopkg.in/yaml.v3" + + util "code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/util" ) // 1st level (Root entry) @@ -50,6 +55,17 @@ type Link struct { 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 func LoadConfig(filename string) (*ClabConfig, error) { absFilepath, err := filepath.Abs(filename) @@ -142,14 +158,14 @@ func DeriveConfig(clabconfig *ClabConfig, newIPv4Subnet, newIPv6Subnet string, p } // 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) if err != nil { return fmt.Errorf("failed to marshal YAML: %w", err) } - fullPath := filepath.Join(dirname, filename) - err = os.WriteFile(fullPath, data, 0644) + err = os.WriteFile(filename, data, 0644) if err != nil { return fmt.Errorf("failed to write file: %w", err) } @@ -157,12 +173,27 @@ func WriteConfig(filename string, dirname string, config *ClabConfig) error { return nil } -func ClabDeploy(filename string, dirname string, stopchan chan os.Signal) error { - fullPath := filepath.Join(dirname, filename) +func ClabDestroy(fullPath string) error { + 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") // Run the command in a Goroutine done := make(chan error, 1) + stopdeploy := make(chan os.Signal, 1) + signal.Notify(stopdeploy, os.Interrupt, syscall.SIGTERM) go func() { output, err := cmd.CombinedOutput() // Use CombinedOutput to capture stdout and stderr if err != nil { @@ -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 select { case err := <-done: // Command finished + fmt.Println("Successfully deployed the twin") return err - case <-stopchan: // Signal received to interrupt + case <-stopdeploy: // Signal received to interrupt if err := cmd.Process.Kill(); err != nil { 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") } } diff --git a/applications/rtdt-manager/data/.gosdn_slim.clab.yaml.bak b/applications/rtdt-manager/data/.gosdn_slim.clab.yaml.bak new file mode 100644 index 0000000000000000000000000000000000000000..860448226b9ca3368bbfb7148525170278fca5bc --- /dev/null +++ b/applications/rtdt-manager/data/.gosdn_slim.clab.yaml.bak @@ -0,0 +1,101 @@ +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"] diff --git a/applications/rtdt-manager/data/gosdn_slim.clab.yaml b/applications/rtdt-manager/data/gosdn_slim.clab.yaml index f4d76846cddf22b054f8cee3c4e39d70589cb7ea..860448226b9ca3368bbfb7148525170278fca5bc 100644 --- a/applications/rtdt-manager/data/gosdn_slim.clab.yaml +++ b/applications/rtdt-manager/data/gosdn_slim.clab.yaml @@ -25,7 +25,7 @@ topology: env: GOSDN_ADMIN_PASSWORD: TestPassword binds: - - ../../artifacts/ssl/gosdn:/app/ssl + - ../../../artifacts/ssl/gosdn:/app/ssl gnmi-target-switch0: kind: linux @@ -33,7 +33,7 @@ topology: #only for local use #image: gnmi-target:latest binds: - - ../../artifacts/ssl/gnmi-target:/etc/gnmi-target/ssl + - ../../../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 @@ -46,7 +46,7 @@ topology: #only for local use #image: gnmi-target:latest binds: - - ../../artifacts/ssl/gnmi-target:/etc/gnmi-target/ssl + - ../../../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 diff --git a/applications/rtdt-manager/main.go b/applications/rtdt-manager/main.go index 5c58ca8f4a22f5f7eeec0db08c079cf05a09969d..f78258b0b22c16404d9f4a9362a9cd6dc93b20b1 100644 --- a/applications/rtdt-manager/main.go +++ b/applications/rtdt-manager/main.go @@ -35,14 +35,21 @@ func main() { defer conn.Close() 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) rtdtMan := RtdtMan.NewRtdtManager(conn, auth, topology_file) if rtdtMan == nil { fmt.Println("Couldn't initialize rtdt-manager, quitting!") return } + // Register manager with app and run + 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() if err != nil { diff --git a/applications/rtdt-manager/rtdt-manager/rtdt-manager.go b/applications/rtdt-manager/rtdt-manager/rtdt-manager.go index bdd440074b754efc92138eccd45734ee16f52235..7e13e3036da31716f36bcaad36ee8785a9a9d4ef 100644 --- a/applications/rtdt-manager/rtdt-manager/rtdt-manager.go +++ b/applications/rtdt-manager/rtdt-manager/rtdt-manager.go @@ -3,11 +3,8 @@ package rtdtmanager import ( "fmt" "os" - "os/exec" "os/signal" - "path" "path/filepath" - "strings" "syscall" "time" @@ -19,53 +16,27 @@ import ( confManPb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/configurationmanagement" pnd "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/pnd" 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-topology" - "code.fbi.h-da.de/danet/gosdn/applications/venv-manager/containerlab" + util "code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/util" "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 { - auth *rtdt_auth.RtdtAuth // auth struct for realnet gosdn - conn *grpc.ClientConn // connection to twin's gosdn instance - Pnd *pnd.PrincipalNetworkDomain // PND for realnet gosdn - topo *topoPb.Topology // Topology with which to create clab.yaml - sdnConfig string - clabFilename string // Clab file that exemplifies the topology - topoData *containerlab.YamlStruct - clabData *clabconfig.ClabConfig - eventService event.ServiceInterface // Receive events from realnet gosdn - stopChan chan os.Signal // Global stop channel TODO Can I use that like this? + auth *rtdt_auth.RtdtAuth // auth struct for realnet gosdn + conn *grpc.ClientConn // connection to twin's gosdn instance + Pnd *pnd.PrincipalNetworkDomain // PND for realnet gosdn + topo *topoPb.Topology // Topology with which to create clab.yaml + sdnConfig string // TODO + clabFilename string // Clab file that exemplifies the topology of realnet + clabTwinFilename string + clabData *clabconfig.ClabConfig // Represents the yaml config file as data structure + 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? // TODO auth and conn for virtual net? } func NewRtdtManager(conn *grpc.ClientConn, auth *rtdt_auth.RtdtAuth, clabFilename string) *RtdtManager { - projectRoot, _ := getGosdnPath() - rMan := RtdtManager{ conn: conn, auth: auth, @@ -78,55 +49,30 @@ func NewRtdtManager(conn *grpc.ClientConn, auth *rtdt_auth.RtdtAuth, clabFilenam fmt.Println(err) 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 // from the clab.yaml file that was used to create it, that means that --topolog needs to point // to it + // TODO Implement var err error if rMan.clabData, err = clabconfig.LoadConfig(rMan.clabFilename); err != nil { fmt.Println(err) 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") 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 { pndService := pnd.NewPndServiceClient(rMan.conn) 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 { return fmt.Errorf("Failed to retrieve PND information: %w", err) } @@ -142,44 +88,45 @@ func (rMan *RtdtManager) fetchPndUUID() error { func (rMan *RtdtManager) applyTopology() error { topoService := topoPb.NewTopologyServiceClient(rMan.conn) ctx := rMan.auth.CreateContextWithAuthorization() - topoService.AddLink(ctx, &topoPb.AddLinkRequest{Timestamp: now()}) + topoService.AddLink(ctx, &topoPb.AddLinkRequest{Timestamp: util.Now()}) return nil } // \cite venv-manager +// Keep for later // Write a clab.yaml file to launch a different -func (rMan *RtdtManager) writeModifiedTopologyToFile(clabStruct *containerlab.YamlStruct) error { - rMan.topoData.Mgmt.Network = "gosdn-csbi-arist-twin-net" - - splitMainNetwork := strings.Split(rMan.topoData.Mgmt.Ipv4Subnet, ".") - splitMainNetwork[1] = "101" - rMan.topoData.Mgmt.Ipv4Subnet = strings.Join(splitMainNetwork, ".") - - splitMainNetworkIPv6 := strings.Split(rMan.topoData.Mgmt.Ipv6Subnet, ":") - splitMainNetworkIPv6[1] = "db9" - rMan.topoData.Mgmt.Ipv6Subnet = strings.Join(splitMainNetworkIPv6, ":") - - // Different network for our twin - for i, node := range rMan.topoData.Topology.Nodes { - splitIPv4 := strings.Split(node.MgmtIpv4, ".") - splitIPv4[1] = "101" - node.MgmtIpv4 = strings.Join(splitIPv4, ".") - rMan.topoData.Topology.Nodes[i] = node - } - yaml, err := yaml.Marshal(clabStruct) - if err != nil { - return err - } - - fname := "./topo.clab.tmp.yaml" - err = os.WriteFile(fname, yaml, 0600) - if err != nil { - return err - } - - return nil -} +// func (rMan *RtdtManager) writeModifiedTopologyToFile(clabStruct *containerlab.YamlStruct) error { +// rMan.topoData.Mgmt.Network = "gosdn-csbi-arist-twin-net" +// +// splitMainNetwork := strings.Split(rMan.topoData.Mgmt.Ipv4Subnet, ".") +// splitMainNetwork[1] = "101" +// rMan.topoData.Mgmt.Ipv4Subnet = strings.Join(splitMainNetwork, ".") +// +// splitMainNetworkIPv6 := strings.Split(rMan.topoData.Mgmt.Ipv6Subnet, ":") +// splitMainNetworkIPv6[1] = "db9" +// rMan.topoData.Mgmt.Ipv6Subnet = strings.Join(splitMainNetworkIPv6, ":") +// +// // Different network for our twin +// for i, node := range rMan.topoData.Topology.Nodes { +// splitIPv4 := strings.Split(node.MgmtIpv4, ".") +// splitIPv4[1] = "101" +// node.MgmtIpv4 = strings.Join(splitIPv4, ".") +// rMan.topoData.Topology.Nodes[i] = node +// } +// yaml, err := yaml.Marshal(clabStruct) +// if err != nil { +// return err +// } +// +// fname := "./topo.clab.tmp.yaml" +// err = os.WriteFile(fname, yaml, 0600) +// if err != nil { +// return err +// } +// +// return nil +// } // 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 @@ -187,7 +134,7 @@ func (rMan *RtdtManager) writeModifiedTopologyToFile(clabStruct *containerlab.Ya func (rMan *RtdtManager) fetchTopology() error { topoService := topoPb.NewTopologyServiceClient(rMan.conn) 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 { return fmt.Errorf("Failed to retrieve Topology: %w", err) } @@ -201,27 +148,32 @@ func (rMan *RtdtManager) fetchTopology() error { 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 -func (rMan *RtdtManager) LaunchTwin() error { - // First create the clab environment - // cmdClab := exec.Command("sudo", "containerlab", "deploy") - // output, err := cmdClab.Output() - // 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) +func (r *RtdtManager) LaunchTwin(twinSubnetIPv4, twinSubnetIPv6, twinName string) error { + var derivedConfig *clabconfig.ClabConfig + var clabConfigPath string + var err error + 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 } @@ -242,7 +194,7 @@ func (rMan *RtdtManager) manageConfig(op string) error { // Export from rtdt-manager into gosdn controller case "export": fmt.Println("Now exporting sdnConfig into gosdn controller") - // Implement me some day + //TODO Implement me some day return nil default: return fmt.Errorf("Unknown config operation") @@ -254,34 +206,53 @@ func (rMan *RtdtManager) manageConfig(op string) error { func (rMan *RtdtManager) initEventSystem() error { ctx := rMan.auth.CreateContextWithAuthorization() 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) queueCredentials = "amqp://guest:guest@172.100.0.15:5672/" // fix by setting manually if err != nil { return fmt.Errorf("Encountered error while trying to register event system: %w", err) } 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 { return fmt.Errorf("Failed to attach to event system: %w", err) } - // Subscribe to a topic and call "callback()" when event happens - // eventService is of type Service and contains a channel - // Callback gets passed the event - // Possible events: Update, Delete, Subscribe - // Use this to map desired callbacks to different events - eventService.SubscribeToEventType([]event.TypeToCallbackTuple{ - {Type: event.Update, Callback: rMan.callback}, + eventServiceUser, err := event.NewEventService(queueCredentials, []event.Topic{event.User}) + if err != nil { + return fmt.Errorf("Failed to attach to event system: %w", err) + } + + // Can have different callback per type per topic (e.g. adding mne or updating mne) + 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 // that consumes queue // 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 } -// Based on currently running network managed by gosdn, launch a new gosdn instance that mirrors it -func (rMan *RtdtManager) callback(event *event.Event) { - fmt.Println("Event has happened, hurray!") +func (rMan *RtdtManager) updateMNECallback(event *event.Event) { + fmt.Println("MNE Event has happened (type: update), 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("Event Type: ", event.Type) } + +// {Get,Set}ers +func (r RtdtManager) GetClabFilename() string { + return r.clabFilename +} +func (r RtdtManager) GetClabTwinFilename() string { + return r.clabTwinFilename +} diff --git a/applications/rtdt-manager/util/util.go b/applications/rtdt-manager/util/util.go new file mode 100644 index 0000000000000000000000000000000000000000..ec1e048dc672de7798d2f029c137b1a6bffa2b1c --- /dev/null +++ b/applications/rtdt-manager/util/util.go @@ -0,0 +1,50 @@ +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 +}