diff --git a/applications/rtdt-manager/.gitignore b/applications/rtdt-manager/.gitignore index 62a0f8364060161d22ddcd805a50d2e653d5b507..97e240260a403059f75e4876461ea51d001f1ab0 100644 --- a/applications/rtdt-manager/.gitignore +++ b/applications/rtdt-manager/.gitignore @@ -1,2 +1,3 @@ *.bak +*.BAK twin* diff --git a/applications/rtdt-manager/data/venv_sdnconfig_full.json b/applications/rtdt-manager/data/venv_sdnconfig_full.json index 282dbdfee5e7d058ce5fda78238588ebd59fd69c..32fc735a2a851bd5bfab6f86eb11bbd93610d3a5 100644 --- a/applications/rtdt-manager/data/venv_sdnconfig_full.json +++ b/applications/rtdt-manager/data/venv_sdnconfig_full.json @@ -212,7 +212,7 @@ } } ], - "plugins": null, + "plugins": [], "networkelements": [ { "id": "db563e8d-0755-4df7-8458-56e3242b1cd4", diff --git a/applications/rtdt-manager/main.go b/applications/rtdt-manager/main.go index e595f0b65644f7bb24d6cc6913f13062b96c6498..df792348b185281792cadc0beb2cec04b6bcf88c 100644 --- a/applications/rtdt-manager/main.go +++ b/applications/rtdt-manager/main.go @@ -56,15 +56,25 @@ func main() { fmt.Println("Couldn't initialize rtdt-manager, quitting!") return } - // Deploy the realnet as VEnv (containerlab) + // Load the sdnconfig .json file that contain network elements and topology information + // of the physical network first as a starting point. In a deployed physical network, this information + // would also be retrievable, making this as realistic as it can be. sdnConfig := sdnconfig.NewSdnConfig() - sdnConfig.LoadSdnConfig(sdnConfigPath) + err = sdnConfig.LoadSdnConfig(sdnConfigPath) + if err != nil { + fmt.Printf("In main(): %v\n", err) + return + } + // This loads the base containerlab config file that contains information on how to start + // gosdn, rabbitmq, pluginregistry and mongodb+mongodbexpress as containers clabBaseConfig, err := clabconfig.LoadConfig(clabConfigName) if err != nil { fmt.Printf("In main(): %v\n", err) return } - // Start virtual environment for realnet + + // Start virtual environment for physical network. This can be skipped in an environment + // where an actual physical network is already running. err = rtdtMan.LaunchRealnetVEnv("realnet", sdnConfig, clabBaseConfig) if err != nil { fmt.Printf("In main(): %v\n", err) @@ -73,7 +83,7 @@ func main() { err = rtdtMan.InitEventSystem() if err != nil { - fmt.Printf("In main(): %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 7413e0bd7e41d5b974aa8e197ff2f57d13df8c73..6404a9e3a12d9f669ff521a033bb9c3621a2279b 100644 --- a/applications/rtdt-manager/rtdt-manager/rtdt-manager.go +++ b/applications/rtdt-manager/rtdt-manager/rtdt-manager.go @@ -43,17 +43,18 @@ func NewRtdtManager() *RtdtManager { return &rMan } -// When realnet is clab venv, use this to create it +// When realnet is supposed to be based on containerlab virtual environment, 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 + // Need to write the clab struct to disk: realnetClabPath, err := clabconfig.ClabConfigPath() if err != nil { return fmt.Errorf("Error in LaunchRealnet() %w", err) @@ -66,17 +67,37 @@ func (r *RtdtManager) LaunchRealnetVEnv(realnetName string, sdnConfig *sdnconfig 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) + fmt.Printf("Now trying to create realnet with config file: %s\n", realnetClabFName) + // NewVEnv tasks: + // - deploy the given clab config, + // - establish connection to it + // - get pnd + // - log in + // - save everything in VEnv struct + r.realnet = venv.NewVEnv(realnetName, realnetClabFName, "admin", "TestPassword", &r.waitGroup, sdnConfig) if r.realnet == nil { 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) + // if err != nil { + // fmt.Printf("Failed to apply configuration: %v\n", err) + // } // Now setup the just-created environment // Need to: @@ -92,17 +113,11 @@ func (r *RtdtManager) LaunchRealnetVEnv(realnetName string, sdnConfig *sdnconfig 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 - } + // err = r.realnet.RetrieveClabConfig() + // if err != nil { + // fmt.Printf("Error: Couldn't retrieve clab config: %v\n", err) + // return err + // } return nil } @@ -214,7 +229,7 @@ func (r *RtdtManager) LaunchTwin(twinSubnetIPv4, twinSubnetIPv6, twinName string return fmt.Errorf("Failed to write modified twin clab config to disk: %w", err) } - twin := venv.NewVEnv(twinName, twinClabFName, "admin", "TestPassword", &r.waitGroup) + twin := venv.NewVEnv(twinName, twinClabFName, "admin", "TestPassword", &r.waitGroup, nil) r.rtdt_twins = append(r.rtdt_twins, twin) return nil } @@ -305,7 +320,7 @@ func (r *RtdtManager) Run() error { // Receive events from realnet VEnv func (r *RtdtManager) InitEventSystem() error { - fmt.Println("Starting Event System for realnet!") + fmt.Println("Starting Event System for realnet!") realnet_auth := r.realnet.GetAuth() ctx := realnet_auth.CreateContextWithAuthorization() queueCredentials, err := registration.Register(ctx, realnet_auth.GetAddress(), "basic-interface-monitoring", "SecurePresharedToken") diff --git a/applications/rtdt-manager/sdnconfig/sdnconfig.go b/applications/rtdt-manager/sdnconfig/sdnconfig.go index a6a64bd3fe2fd9ccc4d5d9646e60b977ea3f4f9e..30077b39d2aef2bd9c16fe0a283402f7dc15c490 100644 --- a/applications/rtdt-manager/sdnconfig/sdnconfig.go +++ b/applications/rtdt-manager/sdnconfig/sdnconfig.go @@ -8,6 +8,8 @@ import ( "time" configPb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/configurationmanagement" + "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/conflict" + topoPb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/topology" 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" ) @@ -24,6 +26,7 @@ type Metadata struct { CreatedAt time.Time `json:"created_at"` LastUpdated time.Time `json:"last_updated"` } + type NetworkElement struct { ID string `json:"id"` Name string `json:"name"` @@ -67,15 +70,16 @@ type Port struct { } // this is needed because port in link differs from port in port-store (configuration field) +// TODO Does this still apply? type LinkPort struct { - ID string `json:"id"` - Name string `json:"name"` - Metadata Metadata `json:"metadata"` - Configuration PortConfig `json:"configuration"` + ID string `json:"Id"` + Name string `json:"Name"` + Metadata Metadata `json:"Metadata"` + Configuration PortConfig `json:"Configuration"` } type PortConfig struct { - IP string `json:"ip"` - PrefixLength int `json:"prefix_length"` + IP string `json:"Ip"` + PrefixLength int64 `json:"PrefixLength"` } func NewSdnConfig() *SdnConfig { @@ -104,6 +108,14 @@ func (s *SdnConfig) LoadSdnConfig(configFilename string) error { if err != nil { return fmt.Errorf("Error in LoadSdnConfig(): %w", err) } + + jsonData, err := json.MarshalIndent(s, "", " ") + if err != nil { + return fmt.Errorf("Error marshalling struct to JSON: %w", err) + } + fmt.Println("------- SDNCONFIG ------------") + fmt.Println(string(jsonData)) + fmt.Println("------- SDNCONFIG ------------") return nil } @@ -167,3 +179,41 @@ func (s *SdnConfig) GetMneByID(ID string) *NetworkElement { } return nil } + +func (n *Node) Convert() *topoPb.Node { + return &topoPb.Node{ + Id: n.ID, + Name: n.Name, + Metadata: &conflict.Metadata{}, + } +} + +func (p *LinkPort) Convert() *topoPb.Port { + return &topoPb.Port{ + Id: p.ID, + Name: p.Name, + Configuration: &topoPb.Configuration{ + Ip: p.Configuration.IP, + PrefixLength: p.Configuration.PrefixLength, + }, + Metadata: &conflict.Metadata{}, + } +} +func (p *Port) Convert() *topoPb.Port { + return &topoPb.Port{ + Id: p.ID, + Name: p.Name, + Configuration: &topoPb.Configuration{}, + } +} + +func (l *Link) Convert() *topoPb.Link { + return &topoPb.Link{ + Id: l.ID, + Name: l.Name, + SourceNode: l.SourceNode.Convert(), + SourcePort: l.SourcePort.Convert(), + TargetNode: l.TargetNode.Convert(), + TargetPort: l.TargetPort.Convert(), + } +} diff --git a/applications/rtdt-manager/test-config/downloaded-config.json b/applications/rtdt-manager/test-config/downloaded-config.json new file mode 100644 index 0000000000000000000000000000000000000000..99af02e721055ac04fdf4c5459274198d7abc009 --- /dev/null +++ b/applications/rtdt-manager/test-config/downloaded-config.json @@ -0,0 +1,266 @@ + { + "pndID": "5f20f34b-cbd0-4511-9ddc-c50cf6a3b49d", + "nodes": [ + { + "id": "db563e8d-0755-4df7-8458-56e3242b1cd4", + "name": "gnmi-target-switch0", + "metadata": { + "created_at": "2025-02-26T11:01:02.974Z", + "last_updated": "2025-02-26T11:01:02.974Z" + } + }, + { + "id": "f14ccc06-0143-4196-9aba-7812fae01d11", + "name": "gnmi-target-switch1", + "metadata": { + "created_at": "2025-02-26T11:01:02.992Z", + "last_updated": "2025-02-26T11:01:02.992Z" + } + }, + { + "id": "c1012f26-99ef-41d0-970b-4642012ebace", + "name": "centos0", + "metadata": { + "created_at": "2025-02-26T11:01:03.001Z", + "last_updated": "2025-02-26T11:01:03.001Z" + } + }, + { + "id": "15f1104a-26a7-49e9-ba57-9eed8720e3ad", + "name": "centos1", + "metadata": { + "created_at": "2025-02-26T11:01:03.004Z", + "last_updated": "2025-02-26T11:01:03.004Z" + } + } + ], + "ports": [ + { + "id": "1950bf78-2fe3-4e0f-a4a6-9c42c660fb36", + "name": "eth1", + "metadata": { + "created_at": "0001-01-01T00:00:00Z", + "last_updated": "0001-01-01T00:00:00Z" + }, + "configuration": "" + }, + { + "id": "b0338d9a-50e4-477f-bb30-18ad3c61302f", + "name": "eth1", + "metadata": { + "created_at": "0001-01-01T00:00:00Z", + "last_updated": "0001-01-01T00:00:00Z" + }, + "configuration": "" + }, + { + "id": "df55cdb4-aa55-495c-8e27-245e6ed6b806", + "name": "eth2", + "metadata": { + "created_at": "0001-01-01T00:00:00Z", + "last_updated": "0001-01-01T00:00:00Z" + }, + "configuration": "" + }, + { + "id": "505f067f-62a4-484e-9f06-673f668a1908", + "name": "eth1", + "metadata": { + "created_at": "0001-01-01T00:00:00Z", + "last_updated": "0001-01-01T00:00:00Z" + }, + "configuration": "" + }, + { + "id": "164c384d-7179-42c9-8c54-4d1ad148b7b8", + "name": "eth2", + "metadata": { + "created_at": "0001-01-01T00:00:00Z", + "last_updated": "0001-01-01T00:00:00Z" + }, + "configuration": "" + }, + { + "id": "b279ea32-fa61-4167-93aa-54afe45cfdb0", + "name": "eth1", + "metadata": { + "created_at": "0001-01-01T00:00:00Z", + "last_updated": "0001-01-01T00:00:00Z" + }, + "configuration": "" + } + ], + "links": [ + { + "ID": "b482a038-6e38-4dc5-906b-fd31abdbb23e", + "Metadata": { + "created_at": "0001-01-01T00:00:00Z", + "last_updated": "0001-01-01T00:00:00Z" + }, + "Name": "gnmi-target-switch0:gnmi-target-switch1", + "SourceNode": { + "id": "db563e8d-0755-4df7-8458-56e3242b1cd4", + "name": "gnmi-target-switch0", + "metadata": { + "created_at": "0001-01-01T00:00:00Z", + "last_updated": "0001-01-01T00:00:00Z" + } + }, + "SourcePort": { + "Id": "1950bf78-2fe3-4e0f-a4a6-9c42c660fb36", + "Name": "eth1", + "Metadata": { + "created_at": "0001-01-01T00:00:00Z", + "last_updated": "0001-01-01T00:00:00Z" + }, + "Configuration": { + "Ip": "192.168.178.2", + "PrefixLength": 24 + } + }, + "TargetNode": { + "id": "f14ccc06-0143-4196-9aba-7812fae01d11", + "name": "gnmi-target-switch1", + "metadata": { + "created_at": "0001-01-01T00:00:00Z", + "last_updated": "0001-01-01T00:00:00Z" + } + }, + "TargetPort": { + "Id": "b0338d9a-50e4-477f-bb30-18ad3c61302f", + "Name": "eth1", + "Metadata": { + "created_at": "0001-01-01T00:00:00Z", + "last_updated": "0001-01-01T00:00:00Z" + }, + "Configuration": { + "Ip": "192.168.178.3", + "PrefixLength": 24 + } + } + }, + { + "ID": "0bee4805-4a96-4870-9b4a-c0742f58d3e8", + "Metadata": { + "created_at": "0001-01-01T00:00:00Z", + "last_updated": "0001-01-01T00:00:00Z" + }, + "Name": "gnmi-target-switch0:centos0", + "SourceNode": { + "id": "db563e8d-0755-4df7-8458-56e3242b1cd4", + "name": "gnmi-target-switch0", + "metadata": { + "created_at": "2025-02-26T11:01:02.974Z", + "last_updated": "2025-02-26T11:01:02.974Z" + } + }, + "SourcePort": { + "Id": "df55cdb4-aa55-495c-8e27-245e6ed6b806", + "Name": "eth2", + "Metadata": { + "created_at": "0001-01-01T00:00:00Z", + "last_updated": "0001-01-01T00:00:00Z" + }, + "Configuration": { + "Ip": "192.168.178.2", + "PrefixLength": 24 + } + }, + "TargetNode": { + "id": "c1012f26-99ef-41d0-970b-4642012ebace", + "name": "centos0", + "metadata": { + "created_at": "0001-01-01T00:00:00Z", + "last_updated": "0001-01-01T00:00:00Z" + } + }, + "TargetPort": { + "Id": "505f067f-62a4-484e-9f06-673f668a1908", + "Name": "eth1", + "Metadata": { + "created_at": "0001-01-01T00:00:00Z", + "last_updated": "0001-01-01T00:00:00Z" + }, + "Configuration": { + "Ip": "192.168.178.3", + "PrefixLength": 24 + } + } + }, + { + "ID": "4fcd6017-dbf2-471b-bf03-42f73b993676", + "Metadata": { + "created_at": "0001-01-01T00:00:00Z", + "last_updated": "0001-01-01T00:00:00Z" + }, + "Name": "gnmi-target-switch1:centos1", + "SourceNode": { + "id": "f14ccc06-0143-4196-9aba-7812fae01d11", + "name": "gnmi-target-switch1", + "metadata": { + "created_at": "2025-02-26T11:01:02.992Z", + "last_updated": "2025-02-26T11:01:02.992Z" + } + }, + "SourcePort": { + "Id": "164c384d-7179-42c9-8c54-4d1ad148b7b8", + "Name": "eth2", + "Metadata": { + "created_at": "0001-01-01T00:00:00Z", + "last_updated": "0001-01-01T00:00:00Z" + }, + "Configuration": { + "Ip": "192.168.178.2", + "PrefixLength": 24 + } + }, + "TargetNode": { + "id": "15f1104a-26a7-49e9-ba57-9eed8720e3ad", + "name": "centos1", + "metadata": { + "created_at": "0001-01-01T00:00:00Z", + "last_updated": "0001-01-01T00:00:00Z" + } + }, + "TargetPort": { + "Id": "b279ea32-fa61-4167-93aa-54afe45cfdb0", + "Name": "eth1", + "Metadata": { + "created_at": "0001-01-01T00:00:00Z", + "last_updated": "0001-01-01T00:00:00Z" + }, + "Configuration": { + "Ip": "192.168.178.3", + "PrefixLength": 24 + } + } + } + ], + "plugins": null, + "networkelements": [ + { + "id": "db563e8d-0755-4df7-8458-56e3242b1cd4", + "name": "gnmi-target-switch0", + "transport_type": "gnmi", + "transport_address": "172.100.0.11:7030", + "transport_username": "admin", + "transport_password": "admin", + "transport_tls": true, + "plugin": "2455b300-67ee-4695-95cd-1c7a8e6e28ab", + "model": "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"config\":{\"enabled\":true,\"mtu\":1500,\"name\":\"eth0\"},\"name\":\"eth0\",\"state\":{\"admin-status\":\"UP\",\"ifindex\":787,\"loopback-mode\":false,\"oper-status\":\"UP\"},\"subinterfaces\":{\"subinterface\":[{\"config\":{\"index\":0},\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"172.100.0.11\",\"prefix-length\":16},\"ip\":\"172.100.0.11\"}]}},\"openconfig-if-ip:ipv6\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"2001:db8::9\",\"prefix-length\":64},\"ip\":\"2001:db8::9\"}]}}},{\"config\":{\"index\":1},\"index\":1,\"openconfig-if-ip:ipv6\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"fe80::42:acff:fe64:b\",\"prefix-length\":64},\"ip\":\"fe80::42:acff:fe64:b\"}]}}}]}},{\"config\":{\"enabled\":true,\"mtu\":9500,\"name\":\"eth1\"},\"name\":\"eth1\",\"state\":{\"admin-status\":\"UP\",\"ifindex\":792,\"loopback-mode\":false,\"oper-status\":\"UP\"},\"subinterfaces\":{\"subinterface\":[{\"config\":{\"index\":0},\"index\":0,\"openconfig-if-ip:ipv6\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"fe80::a8c1:abff:fe36:5da3\",\"prefix-length\":64},\"ip\":\"fe80::a8c1:abff:fe36:5da3\"}]}}}]}},{\"config\":{\"enabled\":true,\"mtu\":9500,\"name\":\"eth2\"},\"name\":\"eth2\",\"state\":{\"admin-status\":\"UP\",\"ifindex\":783,\"loopback-mode\":false,\"oper-status\":\"UP\"},\"subinterfaces\":{\"subinterface\":[{\"config\":{\"index\":0},\"index\":0,\"openconfig-if-ip:ipv6\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"fe80::a8c1:abff:fec9:790c\",\"prefix-length\":64},\"ip\":\"fe80::a8c1:abff:fec9:790c\"}]}}}]}},{\"config\":{\"enabled\":false,\"mtu\":0,\"name\":\"lo\"},\"name\":\"lo\",\"state\":{\"admin-status\":\"UP\",\"ifindex\":1,\"loopback-mode\":true,\"oper-status\":\"UNKNOWN\"},\"subinterfaces\":{\"subinterface\":[{\"config\":{\"index\":0},\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"127.0.0.1\",\"prefix-length\":8},\"ip\":\"127.0.0.1\"}]}},\"openconfig-if-ip:ipv6\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"::1\",\"prefix-length\":128},\"ip\":\"::1\"}]}}}]}}]},\"openconfig-network-instance:network-instances\":{\"network-instance\":[{\"config\":{\"name\":\"DEFAULT\"},\"name\":\"DEFAULT\",\"protocols\":{\"protocol\":[{\"config\":{\"identifier\":\"openconfig-policy-types:STATIC\",\"name\":\"STATIC\"},\"identifier\":\"openconfig-policy-types:STATIC\",\"name\":\"STATIC\",\"static-routes\":{\"static\":[{\"config\":{\"prefix\":\"0.0.0.0/0\"},\"next-hops\":{\"next-hop\":[{\"config\":{\"index\":\"AUTO_172.100.0.1\",\"next-hop\":\"172.100.0.1\"},\"index\":\"AUTO_172.100.0.1\",\"interface-ref\":{\"config\":{\"interface\":\"eth0\"}}}]},\"prefix\":\"0.0.0.0/0\"},{\"config\":{\"prefix\":\"::/0\"},\"next-hops\":{\"next-hop\":[{\"config\":{\"index\":\"AUTO_2001:db8::1\",\"next-hop\":\"2001:db8::1\"},\"index\":\"AUTO_2001:db8::1\",\"interface-ref\":{\"config\":{\"interface\":\"eth0\"}}}]},\"prefix\":\"::/0\"}]}}]}}]},\"openconfig-system:system\":{\"clock\":{\"config\":{\"timezone-name\":\"UTC\"}},\"config\":{\"domain-name\":\"Not.implemented.yet\",\"hostname\":\"gnmi-target-switch0\",\"motd-banner\":\"\\nThe programs included with the Debian GNU/Linux system are free software;\\nthe exact distribution terms for each program are described in the\\nindividual files in /usr/share/doc/*/copyright.\\n\\nDebian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent\\npermitted by applicable law.\\n\"},\"memory\":{\"state\":{\"free\":\"6650664\",\"physical\":\"63586184\",\"used\":\"56935520\"}},\"state\":{\"boot-time\":\"1740042208\",\"current-datetime\":\"2025-02-26T11:00:46Z\",\"software-version\":\"debian:12\"}}}", + "pnd_id": "5f20f34b-cbd0-4511-9ddc-c50cf6a3b49d" + }, + { + "id": "f14ccc06-0143-4196-9aba-7812fae01d11", + "name": "gnmi-target-switch1", + "transport_type": "gnmi", + "transport_address": "172.100.0.12:7030", + "transport_username": "admin", + "transport_password": "admin", + "transport_tls": true, + "plugin": "3de782ef-e51b-472c-a0fb-8ba48768e7ac", + "model": "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"config\":{\"enabled\":true,\"mtu\":1500,\"name\":\"eth0\"},\"name\":\"eth0\",\"state\":{\"admin-status\":\"UP\",\"ifindex\":789,\"loopback-mode\":false,\"oper-status\":\"UP\"},\"subinterfaces\":{\"subinterface\":[{\"config\":{\"index\":0},\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"172.100.0.12\",\"prefix-length\":16},\"ip\":\"172.100.0.12\"}]}},\"openconfig-if-ip:ipv6\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"2001:db8::a\",\"prefix-length\":64},\"ip\":\"2001:db8::a\"}]}}},{\"config\":{\"index\":1},\"index\":1,\"openconfig-if-ip:ipv6\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"fe80::42:acff:fe64:c\",\"prefix-length\":64},\"ip\":\"fe80::42:acff:fe64:c\"}]}}}]}},{\"config\":{\"enabled\":true,\"mtu\":9500,\"name\":\"eth1\"},\"name\":\"eth1\",\"state\":{\"admin-status\":\"UP\",\"ifindex\":791,\"loopback-mode\":false,\"oper-status\":\"UP\"},\"subinterfaces\":{\"subinterface\":[{\"config\":{\"index\":0},\"index\":0,\"openconfig-if-ip:ipv6\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"fe80::a8c1:abff:fe7d:5116\",\"prefix-length\":64},\"ip\":\"fe80::a8c1:abff:fe7d:5116\"}]}}}]}},{\"config\":{\"enabled\":true,\"mtu\":9500,\"name\":\"eth2\"},\"name\":\"eth2\",\"state\":{\"admin-status\":\"UP\",\"ifindex\":785,\"loopback-mode\":false,\"oper-status\":\"UP\"},\"subinterfaces\":{\"subinterface\":[{\"config\":{\"index\":0},\"index\":0,\"openconfig-if-ip:ipv6\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"fe80::a8c1:abff:fe4f:65ae\",\"prefix-length\":64},\"ip\":\"fe80::a8c1:abff:fe4f:65ae\"}]}}}]}},{\"config\":{\"enabled\":false,\"mtu\":0,\"name\":\"lo\"},\"name\":\"lo\",\"state\":{\"admin-status\":\"UP\",\"ifindex\":1,\"loopback-mode\":true,\"oper-status\":\"UNKNOWN\"},\"subinterfaces\":{\"subinterface\":[{\"config\":{\"index\":0},\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"127.0.0.1\",\"prefix-length\":8},\"ip\":\"127.0.0.1\"}]}},\"openconfig-if-ip:ipv6\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"::1\",\"prefix-length\":128},\"ip\":\"::1\"}]}}}]}}]},\"openconfig-network-instance:network-instances\":{\"network-instance\":[{\"config\":{\"name\":\"DEFAULT\"},\"name\":\"DEFAULT\",\"protocols\":{\"protocol\":[{\"config\":{\"identifier\":\"openconfig-policy-types:STATIC\",\"name\":\"STATIC\"},\"identifier\":\"openconfig-policy-types:STATIC\",\"name\":\"STATIC\",\"static-routes\":{\"static\":[{\"config\":{\"prefix\":\"0.0.0.0/0\"},\"next-hops\":{\"next-hop\":[{\"config\":{\"index\":\"AUTO_172.100.0.1\",\"next-hop\":\"172.100.0.1\"},\"index\":\"AUTO_172.100.0.1\",\"interface-ref\":{\"config\":{\"interface\":\"eth0\"}}}]},\"prefix\":\"0.0.0.0/0\"},{\"config\":{\"prefix\":\"::/0\"},\"next-hops\":{\"next-hop\":[{\"config\":{\"index\":\"AUTO_2001:db8::1\",\"next-hop\":\"2001:db8::1\"},\"index\":\"AUTO_2001:db8::1\",\"interface-ref\":{\"config\":{\"interface\":\"eth0\"}}}]},\"prefix\":\"::/0\"}]}}]}}]},\"openconfig-system:system\":{\"clock\":{\"config\":{\"timezone-name\":\"UTC\"}},\"config\":{\"domain-name\":\"Not.implemented.yet\",\"hostname\":\"gnmi-target-switch1\",\"motd-banner\":\"\\nThe programs included with the Debian GNU/Linux system are free software;\\nthe exact distribution terms for each program are described in the\\nindividual files in /usr/share/doc/*/copyright.\\n\\nDebian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent\\npermitted by applicable law.\\n\"},\"memory\":{\"state\":{\"free\":\"6650664\",\"physical\":\"63586184\",\"used\":\"56935520\"}},\"state\":{\"boot-time\":\"1740042208\",\"current-datetime\":\"2025-02-26T11:00:46Z\",\"software-version\":\"debian:12\"}}}", + "pnd_id": "5f20f34b-cbd0-4511-9ddc-c50cf6a3b49d" + } + ] +} diff --git a/applications/rtdt-manager/test-config/main.go b/applications/rtdt-manager/test-config/main.go index 8f6dc11cf1c7fa4714620d5a353e0d454448408a..6e5386b026ac9008f94fb03e6bcdd13638e8af42 100644 --- a/applications/rtdt-manager/test-config/main.go +++ b/applications/rtdt-manager/test-config/main.go @@ -15,26 +15,12 @@ import ( "google.golang.org/grpc/credentials/insecure" ) -// This is for testing sdnconfig files -func main() { - var sdnConfigPath string - flag.StringVar(&sdnConfigPath, "sdnconfig", "applications/rtdt-manager/data/sample_venv_sdnconfig.json", "SdnConfig file (json)") - flag.Parse() - fmt.Println("sdnConfigPath:", sdnConfigPath) - // load the test config, upload to db - testconfig := sdnconfig.NewSdnConfig() - wd, err := os.Getwd() - fmt.Println("Getwd returns:", wd) - err = testconfig.LoadSdnConfig(sdnConfigPath) - if err != nil { - fmt.Println("Couldn't load config, err:", err) - return - } +func SetupGosdnConn() (*pnd.PrincipalNetworkDomain, *rtdt_auth.RtdtAuth) { dialOption := grpc.WithTransportCredentials(insecure.NewCredentials()) conn, err := grpc.NewClient("172.100.0.5:55055", dialOption, grpc.WithDefaultCallOptions()) if err != nil { fmt.Printf("Error: Failed to create connection to gosdn: %v\n", err) - return + return nil, nil } else { fmt.Printf("Successfully created connection to gosdn\n") } @@ -49,25 +35,66 @@ func main() { fmt.Printf("Couldn't retrieve PND, retrying in 2 seconds..\n") time.Sleep(time.Second * 2) } - testdata, err := json.Marshal(testconfig) + return gosdn_pnd, testauth + +} + +func Download() { + gosdn_pnd, testauth := SetupGosdnConn() + if gosdn_pnd == nil || testauth == nil { + fmt.Println("ERROR: couldn't establish connection to gosdn") + } + newconfig := sdnconfig.NewSdnConfig() + newconfig.RetrieveSdnConfig(gosdn_pnd.Id, *testauth) + + data, err := json.MarshalIndent(newconfig, "", " ") if err != nil { fmt.Println("Failed to marshall data") return } - fmt.Println("Data to be uploaded:", string(testdata)) - testconfig.UploadSdnConfig(gosdn_pnd.Id, *testauth) - fmt.Println("Successully uploaded config.. waiting..") - time.Sleep(time.Second * 2) - newconfig := sdnconfig.NewSdnConfig() - newconfig.RetrieveSdnConfig(gosdn_pnd.Id, *testauth) + fmt.Println("Successully downloaded config:", string(data)) +} - data, err := json.Marshal(newconfig) +func Upload(sdnConfigPath string) { + gosdn_pnd, testauth := SetupGosdnConn() + testconfig := sdnconfig.NewSdnConfig() + wd, err := os.Getwd() + fmt.Println("Getwd returns:", wd) + err = testconfig.LoadSdnConfig(sdnConfigPath) + if err != nil { + fmt.Println("Couldn't load config, err:", err) + } + testdata, err := json.Marshal(testconfig) if err != nil { fmt.Println("Failed to marshall data") return } + fmt.Println("Data to be uploaded:", string(testdata)) - fmt.Println("Successully downloaded config:", string(data)) + testconfig.UploadSdnConfig(gosdn_pnd.Id, *testauth) } + +// This is for testing sdnconfig files +func main() { + var sdnConfigPath string + var mode string + flag.StringVar(&mode, "mode", "download", "Whether to download config or upload config") + flag.StringVar(&sdnConfigPath, "sdnconfig", "applications/rtdt-manager/data/sample_venv_sdnconfig.json", "SdnConfig file (json)") + flag.Parse() + + switch mode { + case "download": + fmt.Println("Mode: Download") + Download() + return + case "upload": + fmt.Println("Mode: Upload") + Upload(sdnConfigPath) + return + default: + fmt.Println("Unknown Mode!") + return + } +} diff --git a/applications/rtdt-manager/venv/venv.go b/applications/rtdt-manager/venv/venv.go index 88b0540fc2e040e513892d306b962892bd2137d4..7fffc7eeadc60e670998e1df167f605b590b4247 100644 --- a/applications/rtdt-manager/venv/venv.go +++ b/applications/rtdt-manager/venv/venv.go @@ -10,6 +10,7 @@ import ( "time" configPb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/configurationmanagement" + "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/conflict" "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/networkelement" "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/pnd" topoPb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/topology" @@ -37,7 +38,7 @@ type VEnv struct { conn *grpc.ClientConn // The connection to this specific environment's gosdn pnd *pnd.PrincipalNetworkDomain clabData *clabconfig.ClabConfig // Represents yaml file that was used to deploy - sdnConfig sdnconfig.SdnConfig // Represents json config file for configuration grpc interface + sdnConfig *sdnconfig.SdnConfig // Represents json config file for configuration grpc interface topology *rtdt_topology.Topology clabFilename string // This is the name of the yaml file clabData is based on StopChan <-chan struct{} @@ -49,7 +50,7 @@ type VEnv struct { // Accepts a yaml filename to deploy a container lab environment // TODO Split up into sub-functions // This takes FULL path name to clab config -func NewVEnv(name, clabFilename, user, pass string, wg *sync.WaitGroup) *VEnv { +func NewVEnv(name, clabFilename, user, pass string, wg *sync.WaitGroup, sdnConfig *sdnconfig.SdnConfig) *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 @@ -117,11 +118,34 @@ func NewVEnv(name, clabFilename, user, pass string, wg *sync.WaitGroup) *VEnv { clabData: clabData, clabFilename: clabFilename, waitGroup: wg, - topology: nil, // set this later - containerRegistryURL: "registry.code.fbi.h-da.de/danet/gnmi-target/", // TODO: Could let user choose + topology: nil, // set this later + sdnConfig: sdnConfig, + containerRegistryURL: "registry.code.fbi.h-da.de/danet/gnmi-target/debian:interface-enabled-test", // TODO: Could let user choose } } +func (v *VEnv) ApplyConfiguration(sdnConfig *sdnconfig.SdnConfig) error { + v.sdnConfig = sdnConfig + sdnConfigParsed, err := json.Marshal(v.sdnConfig) + if err != nil { + fmt.Println("PARSING ERROR") + return err + } + configService := configPb.NewConfigurationManagementServiceClient(v.auth.GetConn()) + request := &configPb.ImportSDNConfigRequest{ + Timestamp: util.Now(), + Pid: v.pnd.Id, + SdnConfigData: string(sdnConfigParsed), + } + response, err := configService.ImportSDNConfig(v.auth.CreateContextWithAuthorization(), request) + if err != nil { + fmt.Println("RESPONSE ERROR") + return err + } + fmt.Println("Configuration Response:", response.String()) + return nil +} + // Based on saved sdnconfig, create devices func (v *VEnv) CreateDevices() error { // Alternative (better) approach @@ -300,24 +324,27 @@ func (v *VEnv) ApplyRoutes() error { func (v *VEnv) UploadTopology() error { conn := v.auth.GetConn() topoService := topoPb.NewTopologyServiceClient(conn) - for _, link := range v.topology.Links { - ctx := v.auth.CreateContextWithAuthorization() - - l := link.Convert() - l.Name = l.SourceNode.Name + ":" + l.TargetNode.Name - l.SourcePort.Configuration = &topoPb.Configuration{Ip: "192.168.178.2", PrefixLength: 24} - l.TargetPort.Configuration = &topoPb.Configuration{Ip: "192.168.178.3", PrefixLength: 24} - addLinkRequest := &topoPb.AddLinkRequest{ + for _, link := range v.sdnConfig.Links { + req := &topoPb.AddLinkRequest{ Timestamp: util.Now(), - Link: l, + Link: &topoPb.Link{ + Id: link.ID, + Name: link.Name, + SourceNode: link.SourceNode.Convert(), + SourcePort: link.SourcePort.Convert(), + TargetNode: link.TargetNode.Convert(), + TargetPort: link.TargetPort.Convert(), + Metadata: &conflict.Metadata{}, + }, } - fmt.Println("AddLink is:", addLinkRequest.String()) - topoResponse, err := topoService.AddLink(ctx, addLinkRequest) + //fmt.Println("AddLink is:", req.String()) + ctx := v.auth.CreateContextWithAuthorization() + topoResponse, err := topoService.AddLink(ctx, req) if err != nil { return err } - fmt.Printf("Successfully uploaded Link to DB: %s\n", topoResponse.String()) + fmt.Printf("Successfully uploaded topo link to DB: %s\n", topoResponse.String()) } return nil }