From d2737db73739212b9c1b2d1a1590284928e199e0 Mon Sep 17 00:00:00 2001 From: "S.H." <sebastian.heiss94@proton.me> Date: Mon, 24 Feb 2025 22:12:45 +0100 Subject: [PATCH] Rework the mechanism for creating twins (and virtual realnet) - rtdt-manager.go: - LaunchRealnetVEnv() to take over functionality previously in main.go and derive realnet clab configuration via ProduceClabConfig() - ProduceClabConfig() merge a base clab config (gosdn, rabbitmq, mongo and so on) with a passed-in sdnconfig json to create the environment - clab-config.go: - Make LoadConfig take full path like WriteConfig - Implement mechanism to get templates of nodes that give information not retrievable from the running containerlab environment (nodeBlueprints and GetNodeTemplate()) - venv.go: - CreateDevices() now uses sdnconfig networkElements to create devices --- .../rtdt-manager/clab-config/clab-config.go | 102 +++++++---- .../rtdt-manager/data/realnet-clab.yaml | 4 +- .../data/realnet-deploy-clab.yaml | 88 ++++++++++ applications/rtdt-manager/main.go | 52 ++---- .../rtdt-manager/rtdt-manager/rtdt-manager.go | 165 +++++++++++++++--- .../rtdt-manager/sdnconfig/sdnconfig.go | 10 +- applications/rtdt-manager/venv/venv.go | 59 +++---- scripts/setup-clab-slim.sh | 8 +- 8 files changed, 358 insertions(+), 130 deletions(-) create mode 100644 applications/rtdt-manager/data/realnet-deploy-clab.yaml diff --git a/applications/rtdt-manager/clab-config/clab-config.go b/applications/rtdt-manager/clab-config/clab-config.go index df02f232c..d53ae7172 100644 --- a/applications/rtdt-manager/clab-config/clab-config.go +++ b/applications/rtdt-manager/clab-config/clab-config.go @@ -6,6 +6,7 @@ import ( "os/exec" "os/signal" "path/filepath" + "regexp" "strconv" "strings" "syscall" @@ -55,42 +56,99 @@ type Link struct { Endpoints []string `yaml:"endpoints"` } +// Pre-defined Node definitions for fall-back. Necessary because it's just not possible to retrieve all the fields +// from the DB +var nodeBlueprints = map[string]Node{ + "gnmi-target-switch": { + Kind: "linux", + Image: "registry.code.fbi.h-da.de/danet/gnmi-target/debian:interface-enabled-test", + Binds: []string{"../../../artifacts/ssl/gnmi-target:/etc/gnmi-target/ssl"}, + 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", + StartupDelay: 5, + }, + "ceos": { + Kind: "ceos", + }, +} + +func GetNodeTemplate(kind, ipv4subnet string) *Node { + re := regexp.MustCompile("(^\\D+)(\\d)") + matches := re.FindStringSubmatch(kind) + if matches == nil { + fmt.Println("GetNodeTemplate(): Couldn't extract regex for string", kind) + return nil + } + if node, exists := nodeBlueprints[matches[1]]; exists { + // make a deep copy of the blueprint, caller can choose what to do with it: + template := node + return &template + } + // This is a hacky way of supporting non-gnmi target hosts + if strings.HasPrefix(kind, "centos") { + digit, err := strconv.Atoi(matches[2]) + if err != nil { + fmt.Printf("Error trying to get centos digit: %v\n", err) + return nil + } + ipOffset := digit + 17 + fmt.Println("ipv4subnet: ", ipv4subnet) + ipv4subnetSplit := strings.Split(ipv4subnet, ".") + fmt.Printf("ipv4subnetSplit[0],[1],[2],[3]=%s:%s", ipv4subnetSplit[0], ipv4subnetSplit[1]) + ipv4subnetSplit[3] = strconv.Itoa(ipOffset) + ipv4 := strings.Join(ipv4subnetSplit, ".") + fmt.Println("ipv4 for centos node:", ipv4) + return &Node{ + MgmtIPv4: ipv4, + Kind: "linux", + Image: "centos:8", + Group: "server", + } + } + return nil +} + // 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 "", fmt.Errorf("Error: Couldn't get Gosdn Path: %w\n", err) } return filepath.Join(gosdnPath, "/applications/rtdt-manager/data"), nil } // Read file and parse into ClabConfig struct -// Only load gosdn environment: rabbitmq, mongodb, +// Take FULL path func LoadConfig(filename string) (*ClabConfig, error) { - cwd, err := os.Getwd() + data, err := os.ReadFile(filename) if err != nil { - return nil, fmt.Errorf("Failed to get working directory: %w", err) - } - absFilename := filepath.Join(cwd, filename) - if err != nil { - return nil, fmt.Errorf("Error in LoadConfig(): %w", err) - } - data, err := os.ReadFile(absFilename) - if err != nil { - return nil, fmt.Errorf("Failed to read file: %w", err) + return nil, fmt.Errorf("Error: Failed to read file: %w", err) } var clabconfig ClabConfig err = yaml.Unmarshal(data, &clabconfig) if err != nil { - return nil, fmt.Errorf("Failed to unmarshal YAML: %w", err) + return nil, fmt.Errorf("Error: Failed to unmarshal YAML: %w", err) } - fmt.Println("--- Successfully loaded clab file:", absFilename, "----") return &clabconfig, nil } +// WriteConfig writes the Config struct to a YAML file +// Takes config file as absolute FULL 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) + } + + err = os.WriteFile(filename, data, 0644) + if err != nil { + return fmt.Errorf("failed to write file: %w", err) + } + + return nil +} // incrementPort takes a port as a string, adds an offset, and returns the new port as a string. func incrementPort(port string, offset int) (string, error) { portNum, err := strconv.Atoi(port) @@ -183,21 +241,6 @@ func DeriveConfig(clabconfig *ClabConfig, newIPv4Subnet, newIPv6Subnet string, p return &derivedConfig, nil } -// WriteConfig writes the Config struct to a YAML file -// 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) - } - - err = os.WriteFile(filename, data, 0644) - if err != nil { - return fmt.Errorf("failed to write file: %w", err) - } - - return nil -} func ClabDestroy(fullPath string) error { fmt.Println("Trying to destroy venv: ", fullPath) @@ -233,7 +276,6 @@ func ClabDeploy(fullPath string) error { // Wait for the deployment to finish or a signal to stop select { case err := <-done: // Command finished - fmt.Println("Successfully deployed containerlab environment") return err case <-stopdeploy: // Signal received to interrupt if err := cmd.Process.Kill(); err != nil { diff --git a/applications/rtdt-manager/data/realnet-clab.yaml b/applications/rtdt-manager/data/realnet-clab.yaml index 0c4a7ecc1..772af1929 100644 --- a/applications/rtdt-manager/data/realnet-clab.yaml +++ b/applications/rtdt-manager/data/realnet-clab.yaml @@ -56,13 +56,13 @@ topology: centos0: kind: linux image: centos:8 - mgmt-ipv4: 172.100.0.3 + mgmt-ipv4: 172.100.0.16 group: server centos1: kind: linux image: centos:8 - mgmt-ipv4: 172.100.0.4 + mgmt-ipv4: 172.100.0.17 group: server mongodb: diff --git a/applications/rtdt-manager/data/realnet-deploy-clab.yaml b/applications/rtdt-manager/data/realnet-deploy-clab.yaml new file mode 100644 index 000000000..29ac6e680 --- /dev/null +++ b/applications/rtdt-manager/data/realnet-deploy-clab.yaml @@ -0,0 +1,88 @@ +name: gosdn_realnet +mgmt: + network: gosdn-realnet-net + ipv4-subnet: 172.100.0.0/16 + ipv6-subnet: 2001:db8::/64 + mtu: 1500 +topology: + nodes: + centos0: + kind: linux + image: centos:8 + mgmt-ipv4: 172.100.0.17 + group: server + centos1: + kind: linux + image: centos:8 + mgmt-ipv4: 172.100.0.18 + group: server + gnmi-target-switch0: + kind: linux + image: registry.code.fbi.h-da.de/danet/gnmi-target/debian:interface-enabled-test + 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 + binds: + - ../../../artifacts/ssl/gnmi-target:/etc/gnmi-target/ssl + startup-delay: 5 + gnmi-target-switch1: + kind: linux + image: registry.code.fbi.h-da.de/danet/gnmi-target/debian:interface-enabled-test + 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 + binds: + - ../../../artifacts/ssl/gnmi-target:/etc/gnmi-target/ssl + startup-delay: 5 + gosdn: + kind: linux + image: gosdn + ports: + - 55055:55055 + - 8080:8080 + - 40000:40000 + cmd: --config /app/configs/containerlab-gosdn.toml --plugin-registry clab-gosdn_realnet-plugin-registry:55057 -d mongodb://root:example@clab-gosdn_realnet-mongodb:27017 + mgmt-ipv4: 172.100.0.5 + env: + GOSDN_ADMIN_PASSWORD: TestPassword + binds: + - ../../../artifacts/ssl/gosdn:/app/ssl + mongodb: + kind: linux + image: mongo:7 + ports: + - 27017:27017 + mgmt-ipv4: 172.100.0.13 + env: + MONGO_INITDB_ROOT_PASSWORD: example + MONGO_INITDB_ROOT_USERNAME: root + mongodb-express: + kind: linux + image: mongo-express:1.0.2 + ports: + - 8081:8081 + mgmt-ipv4: 172.100.0.14 + env: + ME_CONFIG_BASICAUTH: "false" + ME_CONFIG_MONGODB_AUTH_PASSWORD: example + ME_CONFIG_MONGODB_AUTH_USERNAME: root + ME_CONFIG_MONGODB_SERVER: mongodb + plugin-registry: + kind: linux + image: plugin-registry + mgmt-ipv4: 172.100.0.16 + 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/main.go b/applications/rtdt-manager/main.go index b9ce5d595..e595f0b65 100644 --- a/applications/rtdt-manager/main.go +++ b/applications/rtdt-manager/main.go @@ -5,12 +5,12 @@ import ( "fmt" "os" "os/signal" - "sync" "syscall" + clabconfig "code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/clab-config" RtdtMan "code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/rtdt-manager" + "code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/sdnconfig" //"code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/sdnconfig" - venv "code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/venv" ) func main() { @@ -19,7 +19,6 @@ func main() { // Global stop channel, should be passed to all venvs and App stopChan := make(chan os.Signal, 1) signal.Notify(stopChan, os.Interrupt, syscall.SIGTERM) - var wg sync.WaitGroup var address string var pass string @@ -51,49 +50,30 @@ func main() { fmt.Println("--sdnconfig: Path to the sdnconfig .json file that contains information about network elements and links") } flag.Parse() - // Start virtual environment for realnet - // sdnConfig := sdnconfig.NewSdnConfig() - // sdnConfig.LoadSdnConfig(sdnConfigPath) - var realnet *venv.VEnv - realnet = venv.NewVEnv("realnet-test", clabConfigName, user, pass, &wg) - if realnet == nil { - fmt.Println("ERROR: Couldn't deploy the physical network") - return - } else { - fmt.Println("Successfully deployed physical network") - } - err = realnet.DeriveTopologyFromClabData() - if err != nil { - fmt.Printf("Error occured while trying to construct topology in realnet: %v\n", err) + // Register new RtdtManager + rtdtMan := RtdtMan.NewRtdtManager() + if rtdtMan == nil { + fmt.Println("Couldn't initialize rtdt-manager, quitting!") return } - err = realnet.UploadClabConfig() - if err != nil { - fmt.Printf("Error: Couldnt upload clab config: %v\n", err) - } - err = realnet.RetrieveClabConfig() + // Deploy the realnet as VEnv (containerlab) + sdnConfig := sdnconfig.NewSdnConfig() + sdnConfig.LoadSdnConfig(sdnConfigPath) + clabBaseConfig, err := clabconfig.LoadConfig(clabConfigName) if err != nil { - fmt.Printf("Error: Couldn't retrieve clab config: %v\n", err) - } - err = realnet.CreateDevices() - if err != nil { - fmt.Printf("Error: Couldn't create devices!") + fmt.Printf("In main(): %v\n", err) return } - err = realnet.UploadTopology() + // Start virtual environment for realnet + err = rtdtMan.LaunchRealnetVEnv("realnet", sdnConfig, clabBaseConfig) if err != nil { - fmt.Printf("Error occured while trying to upload realnet topology to DB: %v\n", err) - return - } - // Register realnet with a new rtdt-manager - rtdtMan := RtdtMan.NewRtdtManager(realnet, &wg, &stopChan) - if rtdtMan == nil { - fmt.Println("Couldn't initialize rtdt-manager, quitting!") + fmt.Printf("In main(): %v\n", err) return } + err = rtdtMan.InitEventSystem() if err != nil { - fmt.Printf("Error occured while initializing event system: %v\n", err) + fmt.Printf("In main(): %v\n", err) return } // Do performance tests of realnet before starting twin diff --git a/applications/rtdt-manager/rtdt-manager/rtdt-manager.go b/applications/rtdt-manager/rtdt-manager/rtdt-manager.go index 19d98feb5..0e8014523 100644 --- a/applications/rtdt-manager/rtdt-manager/rtdt-manager.go +++ b/applications/rtdt-manager/rtdt-manager/rtdt-manager.go @@ -16,30 +16,151 @@ import ( "code.fbi.h-da.de/danet/gosdn/application-framework/registration" clabconfig "code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/clab-config" rtdt_topology "code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/rtdt-topology" + "code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/sdnconfig" "code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/util" "code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/venv" ) // Manages an environment containing one physical network (realnet) and a digital twin type RtdtManager struct { - realnet *venv.VEnv - rtdt_twins []*venv.VEnv - eventService event.ServiceInterface // Receive events from realnet gosdn (not used yet) - waitGroup *sync.WaitGroup - stopChan *chan os.Signal + realnet *venv.VEnv + rtdt_twins []*venv.VEnv + eventService event.ServiceInterface // Receive events from realnet gosdn (not used yet) + waitGroup sync.WaitGroup + stopChan chan os.Signal + baseClabConfig *clabconfig.ClabConfig } // needs to be passed a running realnet VEnv -func NewRtdtManager(realnet *venv.VEnv, wg *sync.WaitGroup, stopChan *chan os.Signal) *RtdtManager { +func NewRtdtManager() *RtdtManager { rMan := RtdtManager{ - realnet: realnet, - waitGroup: wg, - stopChan: stopChan, + realnet: nil, + waitGroup: sync.WaitGroup{}, + stopChan: make(chan os.Signal, 1), + baseClabConfig: nil, } fmt.Println("Success: RtdtManager created") return &rMan } +// When realnet is clab venv, use this to create it +func (r *RtdtManager) LaunchRealnetVEnv(realnetName string, sdnConfig *sdnconfig.SdnConfig, baseClabConfig *clabconfig.ClabConfig) error { + r.baseClabConfig = baseClabConfig + var realnetClabFName string + // Generate a clabconfig file based on the base config and sdnconfig + realnetClab, err := r.ProduceClabConfig(realnetName, r.baseClabConfig, sdnConfig) + if err != nil { + return fmt.Errorf("Error in LaunchRealnet(): %w", err) + } + fmt.Println("Produced realnet ClabConfig file from sdnconfig and base config") + // Need to write the clab struct to disk + realnetClabPath, err := clabconfig.ClabConfigPath() + if err != nil { + return fmt.Errorf("Error in LaunchRealnet() %w", err) + } + realnetClabFName = filepath.Join(realnetClabPath, realnetName+"-deploy-clab.yaml") + err = clabconfig.WriteConfig(realnetClabFName, realnetClab) + if err != nil { + return fmt.Errorf("Error in LaunchRealnet() %w", err) + } else { + fmt.Println("Successfully wrote realnet clab config file") + } + + fmt.Printf("Trying to now create realnet with fname: %s -------------\n", realnetClabFName) + // NewVEnv tasks: deploy the given clab config, + r.realnet = venv.NewVEnv(realnetName, realnetClabFName, "admin", "TestPassword", &r.waitGroup) + if r.realnet == nil { + return fmt.Errorf("Error in LaunchRealnet: Couldn't deploy VEnv") + } + err = r.realnet.CreateDevices() + if err != nil { + fmt.Printf("Error: Couldn't create devices!") + return err + } + return nil + + // Now setup the just-created environment + // Need to: + // - Upload managed network elements + err = r.realnet.DeriveTopologyFromClabData() + if err != nil { + fmt.Printf("Error occured while trying to construct topology in r.realnet: %v\n", err) + return err + } + err = r.realnet.UploadClabConfig() + if err != nil { + fmt.Printf("Error: Couldnt upload clab config: %v\n", err) + return err + } + err = r.realnet.RetrieveClabConfig() + if err != nil { + fmt.Printf("Error: Couldn't retrieve clab config: %v\n", err) + return err + } + + err = r.realnet.UploadTopology() + if err != nil { + fmt.Printf("Error occured while trying to upload r.realnet topology to DB: %v\n", err) + return err + } + + return nil +} + +func (r *RtdtManager) ProduceClabConfig(clabName string, c *clabconfig.ClabConfig, sdnConfig *sdnconfig.SdnConfig) (*clabconfig.ClabConfig, error) { + var clabConfig *clabconfig.ClabConfig + if sdnConfig == nil { + return nil, fmt.Errorf("Can't produce clab config without loading sdnconfig\n") + } + // clabConfig can be nil, but wouldn't be very useful without base gosdn environment + if c != nil { + clabConfig = c + for nodeName, node := range clabConfig.Topology.Nodes { + // database and plugin-registry need to be loaded dynamically based on generated name + if strings.HasPrefix(nodeName, "gosdn") { + pluginRegistryAddress := fmt.Sprintf("clab-%s-plugin-registry:55057", clabConfig.Name) + dbAddress := fmt.Sprintf("mongodb://root:example@clab-%s-mongodb:27017", clabConfig.Name) + newCmd := fmt.Sprintf("%s --plugin-registry %s -d %s", node.Cmd, pluginRegistryAddress, dbAddress) + node.Cmd = newCmd + clabConfig.Topology.Nodes[nodeName] = node + fmt.Println("node.Cmd is now:", node.Cmd) + } + } + } else { + return nil, fmt.Errorf("No base clabconfig set\n") + } + if sdnConfig.Nodes == nil { + return nil, fmt.Errorf("sdnconfig exists, but Nodes is nil") + } + for _, node := range sdnConfig.Nodes { + var clabNode *clabconfig.Node + fmt.Println("Nodename:", node.Name) + + clabNode = clabconfig.GetNodeTemplate(node.Name, c.Mgmt.IPv4Subnet) + if clabNode == nil { + return nil, fmt.Errorf("No template exists for node %s", node.Name) + } + mne := sdnConfig.GetMneByID(node.ID) + if mne != nil { + ipv4 := strings.Split(mne.TransportAddress, ":") + clabNode.MgmtIPv4 = ipv4[0] // Doesn't work wor centos hosts + } else { + fmt.Printf("ProduceClabConfig(): Warning: No MNE entry for node: %s\n", node.Name) + } + + clabConfig.Topology.Nodes[node.Name] = *clabNode + } + for _, link := range sdnConfig.Links { + ep0 := link.SourceNode.Name + ":" + link.SourcePort.Name + ep1 := link.TargetNode.Name + ":" + link.TargetPort.Name + var clabLink = clabconfig.Link{ + Endpoints: []string{ep0, ep1}, + } + clabConfig.Topology.Links = append(clabConfig.Topology.Links, clabLink) + } + return clabConfig, 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 @@ -48,7 +169,7 @@ func NewRtdtManager(realnet *venv.VEnv, wg *sync.WaitGroup, stopChan *chan os.Si // Launch a second gosdn instance which will manage the virtual network func (r *RtdtManager) LaunchTwin(twinSubnetIPv4, twinSubnetIPv6, twinName string) error { var derivedConfig *clabconfig.ClabConfig - var baseConfig *clabconfig.ClabConfig + var twinClabConfig *clabconfig.ClabConfig var clabConfigPath string var err error var topo *rtdt_topology.Topology @@ -59,8 +180,8 @@ func (r *RtdtManager) LaunchTwin(twinSubnetIPv4, twinSubnetIPv6, twinName string return fmt.Errorf("Error: Couldn't get topology since it hasn't been retrieved from DB yet") } - // Get the basic containerlab config with gosdn, mongodb, rabbitmq - baseConfig, err = clabconfig.LoadConfig("base-clab.yaml") + // First add the base config's nodes + twinClabConfig = r.baseClabConfig // Now retrieve the nodes (gnmi-targets and hosts + topology) via MNE Api mneServiceRealnet := networkelement.NewNetworkElementServiceClient(conn) getRequest := &networkelement.GetAllRequest{ @@ -72,7 +193,7 @@ func (r *RtdtManager) LaunchTwin(twinSubnetIPv4, twinSubnetIPv6, twinName string elements := getAllResponse.GetMne() // These now need to go into the new VEnv's clabData so clab yaml config can be created if len(elements) > 0 { - baseConfig.InsertMNE(elements) + twinClabConfig.InsertMNE(elements) } // Also get topology from db // TODO: Maybe update the topology first before retrieving it? @@ -80,22 +201,26 @@ func (r *RtdtManager) LaunchTwin(twinSubnetIPv4, twinSubnetIPv6, twinName string rtdt_topology.NewTopology().RetrieveTopology(r.realnet.GetPnd().Id, r.realnet.GetAuth()) // Could pass topo, clabconfig and elements into this - if derivedConfig, err = clabconfig.DeriveConfig(baseConfig, twinSubnetIPv4, twinSubnetIPv6, twinName); err != nil { + // Get the basic containerlab config with gosdn, mongodb, rabbitmq + clabConfigPath, err = clabconfig.ClabConfigPath() + if err != nil { + return err + } + if derivedConfig, err = clabconfig.DeriveConfig(twinClabConfig, twinSubnetIPv4, twinSubnetIPv6, twinName); err != nil { return fmt.Errorf("Failed to derive config for twin: %w", err) } // Construct filepath for derived clab yaml - clabConfigPath, err = clabconfig.ClabConfigPath() - clabConfigFullPath := filepath.Join(clabConfigPath, "twin-clab.yaml") - if err = clabconfig.WriteConfig(clabConfigFullPath, derivedConfig); err != nil { + twinClabFName := filepath.Join(clabConfigPath, "twin-clab.yaml") + if err = clabconfig.WriteConfig(twinClabFName, derivedConfig); err != nil { return fmt.Errorf("Failed to write modified twin clab config to disk: %w", err) } - twin := venv.NewVEnv(twinName, clabConfigFullPath, "admin", "TestPassword", r.waitGroup) + twin := venv.NewVEnv(twinName, twinClabFName, "admin", "TestPassword", &r.waitGroup) r.rtdt_twins = append(r.rtdt_twins, twin) return nil } -// Apply the changes from a twin back to realnet +// Apply the changes from a twin back to realnet - NOT USED YET func (r *RtdtManager) ApplyChanges(twinName string) error { var twin *venv.VEnv for _, tw := range r.rtdt_twins { @@ -153,7 +278,7 @@ func (r *RtdtManager) Run() error { for { select { // idea: make channels for different events? - case stop := <-*r.stopChan: + case stop := <-r.stopChan: fmt.Print("Received SIGINT/SIGSTOP, quitting..\n") // this takes full path now for _, twin := range r.rtdt_twins { diff --git a/applications/rtdt-manager/sdnconfig/sdnconfig.go b/applications/rtdt-manager/sdnconfig/sdnconfig.go index 0705d89e8..a6a64bd3f 100644 --- a/applications/rtdt-manager/sdnconfig/sdnconfig.go +++ b/applications/rtdt-manager/sdnconfig/sdnconfig.go @@ -8,7 +8,6 @@ import ( "time" configPb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/configurationmanagement" - clabconfig "code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/clab-config" rtdt_auth "code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/rtdt-auth" "code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/util" ) @@ -160,8 +159,11 @@ func (s *SdnConfig) ApplySdnConfig(auth *rtdt_auth.RtdtAuth) error { return nil } -// Takes a base ClabConfig and expands it with topology -func (s *SdnConfig) GenerateClabConfig(clabConfig *clabconfig.ClabConfig) error { - +func (s *SdnConfig) GetMneByID(ID string) *NetworkElement { + for _, mne := range s.NetworkElements { + if mne.ID == ID { + return &mne + } + } return nil } diff --git a/applications/rtdt-manager/venv/venv.go b/applications/rtdt-manager/venv/venv.go index 15bb37a3a..1c9358cff 100644 --- a/applications/rtdt-manager/venv/venv.go +++ b/applications/rtdt-manager/venv/venv.go @@ -48,7 +48,8 @@ type VEnv struct { // Accepts a yaml filename to deploy a container lab environment // TODO Split up into sub-functions -func NewVEnv(name, clabFilename, user, pass string, wg *sync.WaitGroup) *VEnv { +// This takes FULL path name to clab config +func NewVEnv(name string, clabFilename, user, pass string, wg *sync.WaitGroup) *VEnv { fmt.Printf("[%s] - Creating new virtual environment\n", name) wg.Add(1) // Register the venv and run atleast until it calls wg.Done() var err error @@ -61,12 +62,13 @@ func NewVEnv(name, clabFilename, user, pass string, wg *sync.WaitGroup) *VEnv { fmt.Printf("[%s] - Failed to deploy the network\n", name) return nil } + fmt.Println("Successfully deployed containerlab environment") // After having deployed it, load the config into clabData var clabData *clab.ClabConfig clabData, err = clab.LoadConfig(clabFilename) if err != nil { - fmt.Printf("[%s] - Failed to load config from yaml file\n", name) + fmt.Printf("[%s] - Failed to load config from yaml file: %v\n", name, err) return nil } // get gosdn address inside clab environment @@ -120,41 +122,29 @@ func NewVEnv(name, clabFilename, user, pass string, wg *sync.WaitGroup) *VEnv { } } +// Based on saved sdnconfig, create devices func (v *VEnv) CreateDevices() error { - for _, node := range v.topology.Nodes { - if strings.HasPrefix(node.Name, "gnmi-target-") { - fmt.Printf("[%s] - Creating Network Element for node: %s\n", v.Name, node.Name) - //ports := strings.Split(node.Ports[0], ":") - //port := ports[1] - addr := node.MgmtIpv4 + ":7030" // gnmi targets will always listen on 7030 - opt := &tpb.TransportOption{ - Address: addr, - Username: "admin", - Password: "admin", - Tls: true, - TransportOption: &tpb.TransportOption_GnmiTransportOption{ - GnmiTransportOption: &tpb.GnmiTransportOption{}, - }, - } - // Openconfig pluginid? TODO decide - pluginID, _ := uuid.Parse("d1c269a2-6482-4010-b0d8-679dff73153b") // TODO Get this dynamically - pndID, _ := uuid.Parse(v.pnd.Id) - - fmt.Printf("[%s] - Found target: %s with addr: %s\n", v.Name, node.Name, addr) - fmt.Printf("[%s] - Gosdn controller at %s\n", v.Name, v.auth.GetAddress()) - listResponse, err := gosdnutil.AddNetworkElement(v.auth, v.auth.GetAddress(), node.Name, node.ID, opt, pluginID, pndID, []string{"/"}) - if err != nil { - fmt.Printf("[%s] - Failed to add network elements: %v\n", v.Name, err) - return nil - } else { - fmt.Printf("[%s] - Successfully created network element\n", v.Name) - } - fmt.Printf("[%s] - Got response from AddNetworkElement: %v\n", v.Name, listResponse) - - fmt.Printf("[%s] - Success: registered mne with gosdn controller\n", v.Name) + // Alternative (better) approach + for _, mne := range v.sdnConfig.NetworkElements { + fmt.Printf("[%s] - Found mne target: %s with addr: %s\n", v.Name, mne.Name, mne.TransportAddress) + opt := &tpb.TransportOption{ + Address: mne.TransportAddress, + Username: "admin", + Password: "admin", + Tls: true, + TransportOption: &tpb.TransportOption_GnmiTransportOption{ + GnmiTransportOption: &tpb.GnmiTransportOption{}, + }, } + pluginID, _ := uuid.Parse("d1c269a2-6482-4010-b0d8-679dff73153b") // TODO Get this dynamically + pndID, _ := uuid.Parse(v.pnd.Id) + listResponse, err := gosdnutil.AddNetworkElement(v.auth, v.auth.GetAddress(), mne.Name, mne.ID, opt, pluginID, pndID, []string{"/"}) + if err != nil { + return fmt.Errorf("[%s] - Failed to add network element: %w\n", v.Name, err) + } + fmt.Printf("[%s] - Got response from AddNetworkElement: %v\n", v.Name, listResponse) + fmt.Printf("[%s] - Success: registered mne with gosdn controller\n", v.Name) } - return nil } @@ -331,6 +321,7 @@ func (v *VEnv) UploadTopology() error { // What this does: Upload the nodes that make up the gosdn environment // via the gosdn configuration service to the db (applies to db, message broker, etc.) // This is probably not the intended use for the configuration interface? +// TODO Delete! Wrong approach func (v *VEnv) UploadClabConfig() error { if v.clabData == nil { return fmt.Errorf("Can't upload clabconfig when it hasn't been loaded yet (is nil pointer)") diff --git a/scripts/setup-clab-slim.sh b/scripts/setup-clab-slim.sh index 390ae6f8e..b7943d03f 100755 --- a/scripts/setup-clab-slim.sh +++ b/scripts/setup-clab-slim.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash ## Note: This script does not clean up network elements already existing in the storage. Running it repeatedly will ## create a garbage storage. @@ -9,7 +9,7 @@ GOSDN_ADDRESS="172.100.0.5:55055" ADMINPW="TestPassword" OPENCONFIG_PLUGIN="d1c269a2-6482-4010-b0d8-679dff73153b" ## Adjust this if timer is to short. -SLEEP_TIMER=20 +SLEEP_TIMER=0 ## Check for provided flags. while getopts s: flag @@ -27,8 +27,8 @@ echo 'Logging in via gosdnc and setting up gnmi-targets in controller...' ## Call login and create entries for all the network elements. ## Could be a bit more automated in the future, but fine for now. $GOSDNC_PATH login --controller $GOSDN_ADDRESS --u admin --p $ADMINPW -$GOSDNC_PATH mne create --address 172.100.0.11:7030 --name gnmi-target-switch0 --password admin --plugin-id $OPENCONFIG_PLUGIN --tls --username admin -$GOSDNC_PATH mne create --address 172.100.0.12:7030 --name gnmi-target-switch1 --password admin --plugin-id $OPENCONFIG_PLUGIN --tls --username admin +$GOSDNC_PATH mne create --address 172.100.0.11:7030 --name gnmi-target-switch0 --password admin --plugin-id $OPENCONFIG_PLUGIN --tls --username admin --uuid 1dc7f8b1-d520-4926-b0af-65c143607211 +$GOSDNC_PATH mne create --address 172.100.0.12:7030 --name gnmi-target-switch1 --password admin --plugin-id $OPENCONFIG_PLUGIN --tls --username admin --uuid d0d59768-b990-45e7-833f-06c525692cd1 ## Start gosdnc in prompt mode if GOSDNC_START=true if $GOSDNC_START; then -- GitLab