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

Finalize new mechanism for twin creation, now the basis is a .json...

Finalize new mechanism for twin creation, now the basis is a .json configuration file that can be used to restore the exact state of a gosdn network, also many other changes: clab-config deep copy function, switch to using local gnmi-target, add Transpose() function for generating a modified sdnconfig struct, remove UploadClabConig() (was based on wrong understanding of configurationmanagement service)
parent 67db13a2
No related branches found
No related tags found
No related merge requests found
Pipeline #264154 failed
...@@ -5,6 +5,7 @@ gl-codeclimate.json ...@@ -5,6 +5,7 @@ gl-codeclimate.json
# containerlab # containerlab
clab-gosdn*/ clab-gosdn*/
clab-*/
*.yml.bak *.yml.bak
dev_env_data/clab/*.yaml.bak dev_env_data/clab/*.yaml.bak
......
*.bak *.bak
*.BAK *.BAK
twin* twin*
tmp/
...@@ -13,7 +13,6 @@ import ( ...@@ -13,7 +13,6 @@ import (
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"code.fbi.h-da.de/danet/gosdn/api/go/gosdn/networkelement"
util "code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/util" util "code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/util"
) )
...@@ -60,9 +59,10 @@ type Link struct { ...@@ -60,9 +59,10 @@ type Link struct {
// from the DB // from the DB
var nodeBlueprints = map[string]Node{ var nodeBlueprints = map[string]Node{
"gnmi-target-switch": { "gnmi-target-switch": {
Kind: "linux", Kind: "linux",
Image: "registry.code.fbi.h-da.de/danet/gnmi-target/debian:interface-enabled-test", Image: "gnmi-target-local",
Binds: []string{"../../../artifacts/ssl/gnmi-target:/etc/gnmi-target/ssl"}, //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", 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, StartupDelay: 5,
}, },
...@@ -71,6 +71,41 @@ var nodeBlueprints = map[string]Node{ ...@@ -71,6 +71,41 @@ var nodeBlueprints = map[string]Node{
}, },
} }
// Function for deep copying clab config struct
func (c *ClabConfig) Copy() *ClabConfig {
newC := &ClabConfig{
Name: c.Name,
Mgmt: c.Mgmt,
Topology: Topology{
Nodes: make(map[string]Node),
Links: make([]Link, len(c.Topology.Links)),
},
}
for name, node := range c.Topology.Nodes {
newNode := Node{
Kind: node.Kind,
Image: node.Image,
Ports: append([]string{}, node.Ports...),
Cmd: node.Cmd,
MgmtIPv4: node.MgmtIPv4,
Env: make(map[string]string),
Binds: append([]string{}, node.Binds...),
StartupDelay: node.StartupDelay,
Group: node.Group,
}
for envName, env := range node.Env {
newNode.Env[envName] = env
}
newC.Topology.Nodes[name] = newNode
}
for i, link := range c.Topology.Links {
newC.Topology.Links[i] = Link{
Endpoints: append([]string{}, link.Endpoints...),
}
}
return newC
}
func GetNodeTemplate(kind, ipv4subnet string) *Node { func GetNodeTemplate(kind, ipv4subnet string) *Node {
re := regexp.MustCompile("(^\\D+)(\\d)") re := regexp.MustCompile("(^\\D+)(\\d)")
matches := re.FindStringSubmatch(kind) matches := re.FindStringSubmatch(kind)
...@@ -90,6 +125,8 @@ func GetNodeTemplate(kind, ipv4subnet string) *Node { ...@@ -90,6 +125,8 @@ func GetNodeTemplate(kind, ipv4subnet string) *Node {
fmt.Printf("Error trying to get centos digit: %v\n", err) fmt.Printf("Error trying to get centos digit: %v\n", err)
return nil return nil
} }
// Alter the ideitifying octet in the ipaddress, not ideal but works for now
// Has to be done this way because sdnconfig doesn't save an address for non-gnmi-targets
ipOffset := digit + 17 ipOffset := digit + 17
ipv4subnetSplit := strings.Split(ipv4subnet, ".") ipv4subnetSplit := strings.Split(ipv4subnet, ".")
ipv4subnetSplit[3] = strconv.Itoa(ipOffset) ipv4subnetSplit[3] = strconv.Itoa(ipOffset)
...@@ -136,14 +173,18 @@ func LoadConfig(filename string) (*ClabConfig, error) { ...@@ -136,14 +173,18 @@ func LoadConfig(filename string) (*ClabConfig, error) {
func WriteConfig(filename string, config *ClabConfig) error { 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)
}
dir := filepath.Dir(filename)
if _, err := os.Stat(dir); os.IsNotExist(err) {
if err := os.MkdirAll(dir, 0700); err != nil {
return fmt.Errorf("failed to create directory %s: %w", dir, err)
}
} }
err = os.WriteFile(filename, data, 0644) err = os.WriteFile(filename, 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)
} }
return nil return nil
} }
...@@ -156,46 +197,30 @@ func incrementPort(port string, offset int) (string, error) { ...@@ -156,46 +197,30 @@ func incrementPort(port string, offset int) (string, error) {
return strconv.Itoa(portNum + offset), nil return strconv.Itoa(portNum + offset), nil
} }
// TODO // Take a clab yaml config file and derive another clab config from it (only use to derive base clab)
// What we get from this relevant for ClabConfig: func DeriveConfig(clabConfig *ClabConfig, newIPv4Subnet, newIPv6Subnet string, clabName string) (*ClabConfig, error) {
// Name, Plugin? TransportOptions (Address, Type), TLS yes/no
// Not for ClabConfig but useful: Pnd, GnmiSubscribepaths,
func (c *ClabConfig) InsertMNE(mnes []*networkelement.ManagedNetworkElement) {
for _, mne := range mnes {
c.Topology.Nodes[mne.Name] = Node{
MgmtIPv4: mne.TransportAddress,
}
}
}
// Take a clab yaml config file and derive another clab config from it
func DeriveConfig(clabconfig *ClabConfig, newIPv4Subnet, newIPv6Subnet string, postfix string) (*ClabConfig, error) {
// Create deep copy // Create deep copy
derivedConfig := *clabconfig derivedConfig := *clabConfig
derivedConfig.Topology.Nodes = make(map[string]Node) derivedConfig.Topology.Nodes = make(map[string]Node)
derivedConfig.Topology.Links = append([]Link{}, clabconfig.Topology.Links...) // Copy links derivedConfig.Topology.Links = append([]Link{}, clabConfig.Topology.Links...) // Copy links
portOffset := 5 // TODO set dynamically in some way portOffset := 5 // TODO set dynamically in some way
derivedConfig.Name = fmt.Sprintf("%s-%s", clabconfig.Name, postfix) derivedConfig.Name = fmt.Sprintf("gosdn_%s", clabName)
derivedConfig.Mgmt.Network = fmt.Sprintf("gosdn-%s-net", clabName)
subnetParts := strings.Split(newIPv4Subnet, ".") subnetParts := strings.Split(newIPv4Subnet, ".")
derivedConfig.Mgmt.IPv4Subnet = newIPv4Subnet derivedConfig.Mgmt.IPv4Subnet = newIPv4Subnet
derivedConfig.Mgmt.IPv6Subnet = newIPv6Subnet derivedConfig.Mgmt.IPv6Subnet = newIPv6Subnet
derivedConfig.Mgmt.Network = fmt.Sprintf("%s-%s", clabconfig.Name, postfix)
// Adjust all nodes // Adjust all nodes
for name, node := range clabconfig.Topology.Nodes { for name, node := range clabConfig.Topology.Nodes {
splitIPv4 := strings.Split(node.MgmtIPv4, ".") splitIPv4 := strings.Split(node.MgmtIPv4, ".")
splitIPv4[0], splitIPv4[1], splitIPv4[2] = subnetParts[0], subnetParts[1], subnetParts[2] splitIPv4[0], splitIPv4[1], splitIPv4[2] = subnetParts[0], subnetParts[1], subnetParts[2]
node.MgmtIPv4 = strings.Join(splitIPv4, ".") node.MgmtIPv4 = strings.Join(splitIPv4, ".")
if strings.HasPrefix(name, "gosdn") { // if strings.HasPrefix(name, "gosdn") {
node.Cmd = "--config /app/configs/containerlab-gosdn-twin.toml" // pluginRegistryAddress := fmt.Sprintf("clab-%s-plugin-registry:55057", clabConfig.Name)
//node.Binds = []string{"../../../applications/rtdt-manager/data/ssl/gosdn:/app/ssl"} // dbAddress := fmt.Sprintf("mongodb://root:example@clab-%s-mongodb:27017", clabConfig.Name)
} // node.Cmd = fmt.Sprintf("%s --plugin-registry %s -d %s", node.Cmd, pluginRegistryAddress, dbAddress)
//
// //use separate ssl folders, testing
// if strings.HasPrefix(name, "gnmi-target") {
// node.Binds = []string{"../../../applications/rtdt-manager/data/ssl/gnmi-target:/etc/gnmi-target/ssl"}
// } // }
// Ports: host side needs to be incremented or there will be conflicts // Ports: host side needs to be incremented or there will be conflicts
// for now just use 5 as increment // for now just use 5 as increment
...@@ -224,18 +249,6 @@ func DeriveConfig(clabconfig *ClabConfig, newIPv4Subnet, newIPv6Subnet string, p ...@@ -224,18 +249,6 @@ func DeriveConfig(clabconfig *ClabConfig, newIPv4Subnet, newIPv6Subnet string, p
} }
derivedConfig.Topology.Nodes[name] = node derivedConfig.Topology.Nodes[name] = node
} }
// Update Links so they reference the correct nodes
// for i, link := range clabconfig.Topology.Links {
// for j, endpoint := range link.Endpoints {
// parts := strings.Split(endpoint, ":")
// if len(parts) != 2 {
// return nil, fmt.Errorf("invalid link endpoint: %s", endpoint)
// }
// clabconfig.Topology.Links[i].Endpoints[j] = strings.Join(parts, ":")
// }
// }
return &derivedConfig, nil return &derivedConfig, nil
} }
......
...@@ -20,7 +20,7 @@ topology: ...@@ -20,7 +20,7 @@ topology:
- 55055:55055 - 55055:55055
- 8080:8080 - 8080:8080
- 40000:40000 - 40000:40000
cmd: --config /app/configs/containerlab-gosdn.toml cmd: --config /app/configs/containerlab-gosdn.toml --plugin-registry clab-gosdn_realnet-plugin-registry:55057 -d mongodb://root:example@clab-realnet_gosdn-mongodb:27017
mgmt-ipv4: 172.100.0.5 mgmt-ipv4: 172.100.0.5
env: env:
GOSDN_ADMIN_PASSWORD: TestPassword GOSDN_ADMIN_PASSWORD: TestPassword
......
...@@ -18,7 +18,7 @@ topology: ...@@ -18,7 +18,7 @@ topology:
group: server group: server
gnmi-target-switch0: gnmi-target-switch0:
kind: linux kind: linux
image: registry.code.fbi.h-da.de/danet/gnmi-target/debian:interface-enabled-test image: gnmi-target-local
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
mgmt-ipv4: 172.100.0.11 mgmt-ipv4: 172.100.0.11
binds: binds:
...@@ -26,7 +26,7 @@ topology: ...@@ -26,7 +26,7 @@ topology:
startup-delay: 5 startup-delay: 5
gnmi-target-switch1: gnmi-target-switch1:
kind: linux kind: linux
image: registry.code.fbi.h-da.de/danet/gnmi-target/debian:interface-enabled-test image: gnmi-target-local
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
mgmt-ipv4: 172.100.0.12 mgmt-ipv4: 172.100.0.12
binds: binds:
...@@ -39,7 +39,7 @@ topology: ...@@ -39,7 +39,7 @@ topology:
- 55055:55055 - 55055:55055
- 8080:8080 - 8080:8080
- 40000:40000 - 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 cmd: --config /app/configs/containerlab-gosdn.toml --plugin-registry clab-gosdn_realnet-plugin-registry:55057 -d mongodb://root:example@172.100.0.13:27017
mgmt-ipv4: 172.100.0.5 mgmt-ipv4: 172.100.0.5
env: env:
GOSDN_ADMIN_PASSWORD: TestPassword GOSDN_ADMIN_PASSWORD: TestPassword
......
...@@ -93,7 +93,7 @@ func main() { ...@@ -93,7 +93,7 @@ func main() {
} }
if withTwin { if withTwin {
rtdtMan.LaunchTwin("172.101.0.0/16", "2001:db9::/64", "TEST-TWIN") rtdtMan.LaunchTwin("172.101.0.0/16", "2001:db9::/64", "test-twin")
} }
if err := rtdtMan.Run(); err != nil { if err := rtdtMan.Run(); err != nil {
fmt.Println("Program exited with errors: %w", err) fmt.Println("Program exited with errors: %w", err)
......
...@@ -10,14 +10,10 @@ import ( ...@@ -10,14 +10,10 @@ import (
"sync" "sync"
"time" "time"
"code.fbi.h-da.de/danet/gosdn/api/go/gosdn/networkelement"
"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/application-framework/registration" "code.fbi.h-da.de/danet/gosdn/application-framework/registration"
clabconfig "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"
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/sdnconfig"
"code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/util"
"code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/venv" "code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/venv"
) )
...@@ -49,7 +45,7 @@ func (r *RtdtManager) LaunchRealnetVEnv(realnetName string, sdnConfig *sdnconfig ...@@ -49,7 +45,7 @@ func (r *RtdtManager) LaunchRealnetVEnv(realnetName string, sdnConfig *sdnconfig
var realnetClabFName string var realnetClabFName string
// Generate a clabconfig file based on the base config and sdnconfig // Generate a clabconfig file based on the base config and sdnconfig
realnetClab, err := r.ProduceClabConfig(realnetName, r.baseClabConfig, sdnConfig) realnetClab, err := r.ProduceClabConfig(realnetName, sdnConfig, r.baseClabConfig.Copy())
if err != nil { if err != nil {
return fmt.Errorf("Error in LaunchRealnet(): %w", err) return fmt.Errorf("Error in LaunchRealnet(): %w", err)
} }
...@@ -59,6 +55,7 @@ func (r *RtdtManager) LaunchRealnetVEnv(realnetName string, sdnConfig *sdnconfig ...@@ -59,6 +55,7 @@ func (r *RtdtManager) LaunchRealnetVEnv(realnetName string, sdnConfig *sdnconfig
if err != nil { if err != nil {
return fmt.Errorf("Error in LaunchRealnet() %w", err) return fmt.Errorf("Error in LaunchRealnet() %w", err)
} }
realnetClabPath += "/tmp"
realnetClabFName = filepath.Join(realnetClabPath, realnetName+"-deploy-clab.yaml") realnetClabFName = filepath.Join(realnetClabPath, realnetName+"-deploy-clab.yaml")
err = clabconfig.WriteConfig(realnetClabFName, realnetClab) err = clabconfig.WriteConfig(realnetClabFName, realnetClab)
if err != nil { if err != nil {
...@@ -78,87 +75,50 @@ func (r *RtdtManager) LaunchRealnetVEnv(realnetName string, sdnConfig *sdnconfig ...@@ -78,87 +75,50 @@ func (r *RtdtManager) LaunchRealnetVEnv(realnetName string, sdnConfig *sdnconfig
if r.realnet == nil { if r.realnet == nil {
return fmt.Errorf("Error in LaunchRealnet: Couldn't deploy VEnv") return fmt.Errorf("Error in LaunchRealnet: Couldn't deploy VEnv")
} }
//TODO: REPLACE THIS WITH ApplyConfiguration() ???
// Create the devices based on sdn config file
// err = r.realnet.CreateDevices()
// if err != nil {
// fmt.Printf("Error: Couldn't create devices!")
// return err
// }
// // Apply the topology based on links in the sdn config file:
// 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 fmt.Errorf("--------------------ONLY RUN UNTIL HERE----------------------------")
// This doesn't work for some reason..
err = r.realnet.ApplyConfiguration(sdnConfig) err = r.realnet.ApplyConfiguration(sdnConfig)
if err != nil { if err != nil {
fmt.Printf("Failed to apply configuration: %v\n", err) fmt.Printf("Failed to apply configuration: %v\n", err)
} }
return fmt.Errorf("--------------------ONLY RUN UNTIL HERE----------------------------")
// 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
}
return nil
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
// }
return nil return nil
} }
func (r *RtdtManager) ProduceClabConfig(clabName string, c *clabconfig.ClabConfig, sdnConfig *sdnconfig.SdnConfig) (*clabconfig.ClabConfig, error) { // takes a prepared sdnconfig to produce a clabConfig struct based on it
var clabConfig *clabconfig.ClabConfig func (r *RtdtManager) ProduceClabConfig(name string, sdnConfig *sdnconfig.SdnConfig, baseClab *clabconfig.ClabConfig) (*clabconfig.ClabConfig, error) {
// Test abort conditions
if sdnConfig == nil { if sdnConfig == nil {
return nil, fmt.Errorf("Can't produce clab config without loading sdnconfig\n") 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 { if sdnConfig.Nodes == nil {
return nil, fmt.Errorf("sdnconfig exists, but Nodes is nil") return nil, fmt.Errorf("sdnconfig exists, but Nodes is nil")
} }
// First get all the node of base gosdn environment (rabbitmq, mongodb etc.)
clabConfig := baseClab
pluginRegistryAddress := clabConfig.Topology.Nodes["plugin-registry"].MgmtIPv4
mongodbAddress := clabConfig.Topology.Nodes["mongodb"].MgmtIPv4
// Replace the nodes on the basis of base config
// var NewNodes map[string]clabconfig.Node
for nodename, node := range clabConfig.Topology.Nodes {
// newName := fmt.Sprintf("%s-%s", nodename, name)
if strings.HasPrefix(nodename, "gosdn") {
node.Binds = []string{"../../../../artifacts/ssl/gosdn:/app/ssl"}
pluginRegistryAddress := fmt.Sprintf("%s:55057", pluginRegistryAddress)
dbAddress := fmt.Sprintf("mongodb://root:example@%s:27017", mongodbAddress)
node.Cmd = fmt.Sprintf("%s --plugin-registry %s -d %s", node.Cmd, pluginRegistryAddress, dbAddress)
clabConfig.Topology.Nodes[nodename] = node
}
}
// Create the entries in the clabconfig struct
for _, node := range sdnConfig.Nodes { for _, node := range sdnConfig.Nodes {
var clabNode *clabconfig.Node var clabNode *clabconfig.Node
clabNode = clabconfig.GetNodeTemplate(node.Name, c.Mgmt.IPv4Subnet) clabNode = clabconfig.GetNodeTemplate(node.Name, clabConfig.Mgmt.IPv4Subnet)
if clabNode == nil { if clabNode == nil {
return nil, fmt.Errorf("No template exists for node %s", node.Name) return nil, fmt.Errorf("No template exists for node %s", node.Name)
} }
mne := sdnConfig.GetMneByID(node.ID) mne := sdnConfig.GetMneByID(node.ID)
if mne != nil { if mne != nil {
ipv4 := strings.Split(mne.TransportAddress, ":") ipv4 := strings.Split(mne.TransportAddress, ":")
clabNode.MgmtIPv4 = ipv4[0] // Doesn't work wor centos hosts clabNode.MgmtIPv4 = ipv4[0] // Doesn't work for non-gnmi-targets
} else { } else {
fmt.Printf("ProduceClabConfig(): Warning: No MNE entry for node: %s\n", node.Name) fmt.Printf("ProduceClabConfig(): Warning: No MNE entry for node: %s\n", node.Name)
} }
...@@ -183,50 +143,46 @@ func (r *RtdtManager) ProduceClabConfig(clabName string, c *clabconfig.ClabConfi ...@@ -183,50 +143,46 @@ func (r *RtdtManager) ProduceClabConfig(clabName string, c *clabconfig.ClabConfi
// - Use that config to call "containerlab deploy" // - Use that config to call "containerlab deploy"
// Launch a second gosdn instance which will manage the virtual network // Launch a second gosdn instance which will manage the virtual network
func (r *RtdtManager) LaunchTwin(twinSubnetIPv4, twinSubnetIPv6, twinName string) error { func (r *RtdtManager) LaunchTwin(twinSubnetIPv4, twinSubnetIPv6, twinName string) error {
var derivedConfig *clabconfig.ClabConfig
var twinClabConfig *clabconfig.ClabConfig
var clabConfigPath string var clabConfigPath string
var err error var err error
var topo *rtdt_topology.Topology var prefixLength int
conn := r.realnet.GetConn() prefixRegex := regexp.MustCompile("/(\\d+)$")
prefixMatch := prefixRegex.FindStringSubmatch(twinSubnetIPv4)
topo = r.realnet.GetTopology() if len(prefixMatch) > 1 {
if topo == nil { prefixLength, err = strconv.Atoi(prefixMatch[1])
return fmt.Errorf("Error: Couldn't get topology since it hasn't been retrieved from DB yet") if err != nil {
return err
}
} else {
return fmt.Errorf("Couldn't extract prefix length from ipv4 subnet address")
} }
// First add the base config's nodes // First add the base config's nodes
twinClabConfig = r.baseClabConfig TwinSdnConfig := sdnconfig.SdnConfig{}
// Now retrieve the nodes (gnmi-targets and hosts + topology) via MNE Api // Get the up-to-date sdnconfig from realnet DB
mneServiceRealnet := networkelement.NewNetworkElementServiceClient(conn) err = TwinSdnConfig.RetrieveSdnConfig(r.realnet.GetPnd().String(), *r.realnet.GetAuth())
getRequest := &networkelement.GetAllRequest{ if err != nil {
Timestamp: util.Now(), return fmt.Errorf("LaunchTwin Error: %v\n", err)
Pid: r.realnet.GetPnd().Id,
} }
mneCtx := r.realnet.GetAuth().CreateContextWithAuthorization() // Transpose the retrieved SdnConfig structs
getAllResponse, err := mneServiceRealnet.GetAll(mneCtx, getRequest) TwinSdnConfig.Transpose(twinName, twinSubnetIPv4, twinSubnetIPv6, prefixLength)
elements := getAllResponse.GetMne() fmt.Println("TwinSdnConfig:")
// These now need to go into the new VEnv's clabData so clab yaml config can be created fmt.Println(TwinSdnConfig)
if len(elements) > 0 { // produce the twin clab configuration based on the transposed sdnconfig and derived clab
twinClabConfig.InsertMNE(elements) baseClabConfig, err := clabconfig.DeriveConfig(r.baseClabConfig.Copy(), twinSubnetIPv4, twinSubnetIPv6, twinName)
twinClabConfig, err := r.ProduceClabConfig(twinName, &TwinSdnConfig, baseClabConfig)
if err != nil {
return err
} }
// Also get topology from db // Construct filepath for derived clab yaml
// TODO: Maybe update the topology first before retrieving it?
// reminder: Topology might not include all targets? TODO: Check UploadTopology
rtdt_topology.NewTopology().RetrieveTopology(r.realnet.GetPnd().Id, r.realnet.GetAuth())
// Could pass topo, clabconfig and elements into this
// Get the basic containerlab config with gosdn, mongodb, rabbitmq // Get the basic containerlab config with gosdn, mongodb, rabbitmq
clabConfigPath, err = clabconfig.ClabConfigPath() clabConfigPath, err = clabconfig.ClabConfigPath()
if err != nil { if err != nil {
return err return err
} }
if derivedConfig, err = clabconfig.DeriveConfig(twinClabConfig, twinSubnetIPv4, twinSubnetIPv6, twinName); err != nil { clabConfigPath += "/tmp"
return fmt.Errorf("Failed to derive config for twin: %w", err) twinClabFName := filepath.Join(clabConfigPath, twinName+"-clab.yaml")
} if err = clabconfig.WriteConfig(twinClabFName, twinClabConfig); err != nil {
// Construct filepath for derived clab yaml
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) return fmt.Errorf("Failed to write modified twin clab config to disk: %w", err)
} }
......
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"strings"
"time" "time"
configPb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/configurationmanagement" configPb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/configurationmanagement"
...@@ -120,6 +121,7 @@ func (s *SdnConfig) LoadSdnConfig(configFilename string) error { ...@@ -120,6 +121,7 @@ func (s *SdnConfig) LoadSdnConfig(configFilename string) error {
return fmt.Errorf("Error in LoadSdnConfig(): %w", err) return fmt.Errorf("Error in LoadSdnConfig(): %w", err)
} }
// Output
// jsonData, err := json.MarshalIndent(s, "", " ") // jsonData, err := json.MarshalIndent(s, "", " ")
// if err != nil { // if err != nil {
// return fmt.Errorf("Error marshalling struct to JSON: %w", err) // return fmt.Errorf("Error marshalling struct to JSON: %w", err)
...@@ -130,6 +132,52 @@ func (s *SdnConfig) LoadSdnConfig(configFilename string) error { ...@@ -130,6 +132,52 @@ func (s *SdnConfig) LoadSdnConfig(configFilename string) error {
return nil return nil
} }
// Transpose an sdnconfig struct networkElements
// takes name of network, address range in ipv4 and ipv6 (e.g. 172.101.0.0) and ipv4 prefix for network
// Limitations:
// - No differing prefix lengths between realnet, twin,
// - No prefix lengths other than 8, 16, 24 (cidr)
func (s *SdnConfig) Transpose(name, addressIpv4, addressIpv6 string, ipv4PrefixLength int) {
ipv4Parts := strings.Split(addressIpv4, ".")
for i := range s.Nodes {
s.Nodes[i].Name += fmt.Sprintf("-%s", name)
}
for i, mne := range s.NetworkElements {
HostIpv4Parts := strings.Split(s.NetworkElements[i].TransportAddress, ".")
var NewIpv4 string
switch ipv4PrefixLength {
case 8:
NewIpv4 = fmt.Sprintf("%s.%s.%s.%s", ipv4Parts[0], HostIpv4Parts[1], HostIpv4Parts[2], HostIpv4Parts[3])
case 16:
NewIpv4 = fmt.Sprintf("%s.%s.%s.%s", ipv4Parts[0], ipv4Parts[1], HostIpv4Parts[2], HostIpv4Parts[3])
case 24:
NewIpv4 = fmt.Sprintf("%s.%s.%s.%s", ipv4Parts[0], ipv4Parts[1], ipv4Parts[2], HostIpv4Parts[3])
default:
fmt.Printf("Error: unsupported ipv4 prefix length\n")
}
s.NetworkElements[i].Name += fmt.Sprintf("-%s", name)
// TransportAddress has form 172.100.0.5:7030
s.NetworkElements[i].TransportAddress = NewIpv4
s.NetworkElements[i].Model = s.transposeModelIpv4(mne.Model, mne.TransportAddress, addressIpv6, ipv4PrefixLength)
}
for i := range s.Links {
s.Links[i].SourceNode.Name += fmt.Sprintf("-%s", name)
s.Links[i].TargetNode.Name += fmt.Sprintf("-%s", name)
}
}
func (s *SdnConfig) transposeModelIpv4(model, ipv4, oldIpv4 string, ipv4PrefixLength int) string {
ipv4Parts := strings.Split(ipv4, ".")
OldIpv4Parts := strings.Split(oldIpv4, ".")
networkPortionLength := ipv4PrefixLength / 8
oldNetworkPortion := strings.Join(OldIpv4Parts[:networkPortionLength-1], ".")
newNetworkPortion := strings.Join(ipv4Parts[:networkPortionLength-1], ".")
strings.ReplaceAll(model, oldNetworkPortion, newNetworkPortion)
return model
}
func (s *SdnConfig) RetrieveSdnConfig(pndId string, auth rtdt_auth.RtdtAuth) error { func (s *SdnConfig) RetrieveSdnConfig(pndId string, auth rtdt_auth.RtdtAuth) error {
var err error var err error
conn := auth.GetConn() conn := auth.GetConn()
......
...@@ -29,7 +29,6 @@ import ( ...@@ -29,7 +29,6 @@ import (
"github.com/openconfig/ygot/ygot" "github.com/openconfig/ygot/ygot"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/credentials/insecure"
"gopkg.in/yaml.v3"
) )
type VEnv struct { type VEnv struct {
...@@ -81,8 +80,8 @@ func NewVEnv(name, clabFilename, user, pass string, wg *sync.WaitGroup, sdnConfi ...@@ -81,8 +80,8 @@ func NewVEnv(name, clabFilename, user, pass string, wg *sync.WaitGroup, sdnConfi
} }
} }
fmt.Printf("[%s] - Sleep for 10 seconds to give containers time to settle..\n", name) fmt.Printf("[%s] - Sleep for 15 seconds to give containers time to settle..\n", name)
time.Sleep(time.Second * 10) time.Sleep(time.Second * 15)
// Now log into gosdn physical network // Now log into gosdn physical network
dialOption := grpc.WithTransportCredentials(insecure.NewCredentials()) dialOption := grpc.WithTransportCredentials(insecure.NewCredentials())
gosdnconn, err := grpc.NewClient(gosdnAddress, dialOption, grpc.WithDefaultCallOptions()) gosdnconn, err := grpc.NewClient(gosdnAddress, dialOption, grpc.WithDefaultCallOptions())
...@@ -124,6 +123,7 @@ func NewVEnv(name, clabFilename, user, pass string, wg *sync.WaitGroup, sdnConfi ...@@ -124,6 +123,7 @@ func NewVEnv(name, clabFilename, user, pass string, wg *sync.WaitGroup, sdnConfi
} }
} }
// Upload a sdnconfig .json file to the DB
func (v *VEnv) ApplyConfiguration(sdnConfig *sdnconfig.SdnConfig) error { func (v *VEnv) ApplyConfiguration(sdnConfig *sdnconfig.SdnConfig) error {
v.sdnConfig = sdnConfig v.sdnConfig = sdnConfig
sdnConfigParsed, err := json.Marshal(v.sdnConfig) sdnConfigParsed, err := json.Marshal(v.sdnConfig)
...@@ -351,88 +351,6 @@ func (v *VEnv) UploadTopology() error { ...@@ -351,88 +351,6 @@ func (v *VEnv) UploadTopology() error {
return nil return nil
} }
// 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)")
}
var clabToUpload = *v.clabData
for nodename, node := range v.clabData.Topology.Nodes {
if !strings.Contains(nodename, "gosdn") &&
!strings.Contains(nodename, "mongodb") &&
!strings.Contains(nodename, "rabbitmq") &&
!strings.Contains(nodename, "plugin-registry") {
fmt.Println("Removing node:", node)
delete(clabToUpload.Topology.Nodes, nodename)
}
}
clabToUpload.Topology.Links = nil
clabConfig, err := yaml.Marshal(clabToUpload)
if err != nil {
return fmt.Errorf("Encountered error marshalling clabConfig: %w", err)
}
var jsonData interface{}
if err := yaml.Unmarshal(clabConfig, &jsonData); err != nil {
return fmt.Errorf("failed to unmarshal YAML to JSON: %w", err)
}
jsonBytes, err := json.MarshalIndent(jsonData, "", " ")
if err != nil {
return fmt.Errorf("Failed to marshal JSON: %w", err)
}
req := &configPb.ImportSDNConfigRequest{
Timestamp: util.Now(),
Pid: v.pnd.GetId(), // TODO: check if this can be nil
SdnConfigData: string(jsonBytes),
}
conn := v.auth.GetConn()
configService := configPb.NewConfigurationManagementServiceClient(conn)
ctx := v.auth.CreateContextWithAuthorization()
configResponse, err := configService.ImportSDNConfig(ctx, req)
if err != nil {
return err
}
fmt.Println("Successfully uploaded clab config:", configResponse.String())
return nil
}
// TODO This doesn't really work this way..
func (v *VEnv) RetrieveClabConfig() error {
ctx := v.auth.CreateContextWithAuthorization()
conn := v.auth.GetConn()
configService := configPb.NewConfigurationManagementServiceClient(conn)
req := &configPb.ExportSDNConfigRequest{
Timestamp: util.Now(),
Pid: v.pnd.GetId(),
}
configResponse, err := configService.ExportSDNConfig(ctx, req)
if err != nil {
return fmt.Errorf("Failed retrieving config clab data: %w", err)
}
var jsonData map[string]interface{}
err = json.Unmarshal([]byte(configResponse.SdnConfigData), &jsonData)
if err != nil {
return fmt.Errorf("Failed to unmarshal SDN config data: %w", err)
}
// Pretty-print the JSON data
formattedJSON, err := json.MarshalIndent(jsonData, "", " ")
if err != nil {
return fmt.Errorf("failed to format JSON data: %w", err)
}
fmt.Println("Retrieved SDN Config Data:")
fmt.Println(string(formattedJSON)) // Print the formatted JSON
return nil
}
func getTypedValue(value string) *gnmi.TypedValue { func getTypedValue(value string) *gnmi.TypedValue {
if boolVal, err := strconv.ParseBool(value); err == nil { if boolVal, err := strconv.ParseBool(value); err == nil {
return &gnmi.TypedValue{Value: &gnmi.TypedValue_BoolVal{BoolVal: boolVal}} return &gnmi.TypedValue{Value: &gnmi.TypedValue_BoolVal{BoolVal: boolVal}}
......
...@@ -89,7 +89,7 @@ func EnsureIntendedConfigurationIsAppliedOnNetworkElement(mne NetworkElement) er ...@@ -89,7 +89,7 @@ func EnsureIntendedConfigurationIsAppliedOnNetworkElement(mne NetworkElement) er
} }
fmt.Println("Made it here 1") fmt.Println("Made it here 1")
req.Update = []*gpb.Update{{ req.Replace = []*gpb.Update{{
Path: path, Path: path,
Val: &gpb.TypedValue{ Val: &gpb.TypedValue{
Value: &gpb.TypedValue_JsonIetfVal{JsonIetfVal: []byte(model)}, Value: &gpb.TypedValue_JsonIetfVal{JsonIetfVal: []byte(model)},
......
...@@ -109,7 +109,6 @@ func (c ConfigurationManagementServer) ExportSDNConfig(ctx context.Context, requ ...@@ -109,7 +109,6 @@ func (c ConfigurationManagementServer) ExportSDNConfig(ctx context.Context, requ
ReattachConfig: *plug.GetClient().ReattachConfig(), ReattachConfig: *plug.GetClient().ReattachConfig(),
} }
sdnConfig.Plugins = append(sdnConfig.Plugins, loadedPlugin) sdnConfig.Plugins = append(sdnConfig.Plugins, loadedPlugin)
} }
sdnConfig.Nodes, err = c.nodeService.GetAll() sdnConfig.Nodes, err = c.nodeService.GetAll()
...@@ -334,6 +333,7 @@ func (c ConfigurationManagementServer) createNetworkElements(sdnConfig *loadedSD ...@@ -334,6 +333,7 @@ func (c ConfigurationManagementServer) createNetworkElements(sdnConfig *loadedSD
// TODO: change TransportOption - type is not needed; this should // TODO: change TransportOption - type is not needed; this should
// be removed as soon as we remove the csbi device type // be removed as soon as we remove the csbi device type
Type: spb.Type_TYPE_OPENCONFIG, Type: spb.Type_TYPE_OPENCONFIG,
Tls: inputNetworkElement.TransportTLS,
} }
plugin, err := c.pluginService.RequestPlugin(uuid.MustParse(inputNetworkElement.Plugin)) plugin, err := c.pluginService.RequestPlugin(uuid.MustParse(inputNetworkElement.Plugin))
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment