diff --git a/models/yang/sonic/import.mk b/models/yang/sonic/import.mk index 20410c055..ee3c56e51 100644 --- a/models/yang/sonic/import.mk +++ b/models/yang/sonic/import.mk @@ -11,4 +11,5 @@ SONICYANG_IMPORTS += sonic-port.yang SONICYANG_IMPORTS += sonic-portchannel.yang SONICYANG_IMPORTS += sonic-mclag.yang SONICYANG_IMPORTS += sonic-types.yang -SONICYANG_IMPORTS += sonic-vrf.yang \ No newline at end of file +SONICYANG_IMPORTS += sonic-vrf.yang +SONICYANG_IMPORTS += sonic-loopback-interface.yang diff --git a/translib/transformer/loopback_openconfig_test.go b/translib/transformer/loopback_openconfig_test.go new file mode 100644 index 000000000..46903ba32 --- /dev/null +++ b/translib/transformer/loopback_openconfig_test.go @@ -0,0 +1,501 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2025 Cisco. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// + +//go:build testapp +// +build testapp + +package transformer_test + +import ( + "errors" + "github.com/Azure/sonic-mgmt-common/translib/db" + "github.com/Azure/sonic-mgmt-common/translib/tlerr" + "testing" + "time" +) + +func Test_openconfig_loopback_interfaces(t *testing.T) { + var url, url_input_body_json string + + t.Log("\n\n+++++++++++++ CONFIGURING LOOPBACK ++++++++++++") + + t.Log("\n\n--- POST to Create Loopback11 ---") + url = "/openconfig-interfaces:interfaces" + url_input_body_json = "{\"openconfig-interfaces:interface\": [{\"name\":\"Loopback11\", \"config\": {\"name\": \"Loopback11\"}}]}" + t.Run("Test Create Loopback11", processSetRequest(url, url_input_body_json, "POST", false, nil)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify Loopback Creation ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback11]/config" + expected_get_json := "{\"openconfig-interfaces:config\": {\"name\": \"Loopback11\", \"enabled\": true}}" + t.Run("Test GET Loopback interface creation config ", processGetRequest(url, nil, expected_get_json, false)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- PUT to Create Loopback22 ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback22]" + url_input_body_json = "{\"openconfig-interfaces:interface\": [{\"name\":\"Loopback22\", \"config\": {\"name\": \"Loopback22\"}}]}" + t.Run("Test Create Loopback22", processSetRequest(url, url_input_body_json, "PUT", false, nil)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify Loopback Creation ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback22]/config" + expected_get_json = "{\"openconfig-interfaces:config\": {\"name\": \"Loopback22\", \"enabled\": true}}" + t.Run("Test GET Loopback interface creation config ", processGetRequest(url, nil, expected_get_json, false)) + time.Sleep(1 * time.Second) + + t.Log("\n\n+++++++++++++ CONFIGURING INTERFACES ATTRIBUTES ++++++++++++") + t.Log("\n\n--- PATCH interface leaf nodes---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback11]/config/mtu" + url_input_body_json = "{\"openconfig-interfaces:mtu\": 9000}" + err_mtu_str := "Configuration for MTU is not supported for Loopback interface " + var expected_mtu_err error = errors.New(err_mtu_str) + t.Run("Test PATCH on interface mtu", processSetRequest(url, url_input_body_json, "PATCH", true, expected_mtu_err)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- PUT to Create Loopback100 ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback100]" + url_input_body_json = "{\"openconfig-interfaces:interface\": [{\"name\":\"Loopback100\", \"config\": {\"name\": \"Loopback100\"}}]}" + t.Run("Test PUT with Loopback100", processSetRequest(url, url_input_body_json, "PUT", false, nil)) + time.Sleep(1 * time.Second) + + pre_req_map := map[string]interface{}{"INTF_TABLE": map[string]interface{}{"Loopback100": map[string]interface{}{"NULL": "NULL"}}} + loadDB(db.ApplDB, pre_req_map) + + t.Log("\n\n--- Verify Loopback Creation Loockback100 --") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback100]" + expected_get_json = "{\"openconfig-interfaces:interface\":[{ \"config\": {\"name\": \"Loopback100\", \"enabled\": true}, \"state\": {\"name\": \"Loopback100\"}, \"name\": \"Loopback100\", \"subinterfaces\": {\"subinterface\": [{\"config\": {\"index\": 0}, \"index\": 0, \"state\": {\"index\": 0}}]}}]}" + t.Run("Test GET Loopback100 interface creation config ", processGetRequest(url, nil, expected_get_json, false)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- PATCH interface leaf enabled node ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback100]/config/enabled" + url_input_body_json = "{\"openconfig-interfaces:enabled\": true}" + t.Run("Test PATCH on interface enabled", processSetRequest(url, url_input_body_json, "PATCH", false, nil)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify PATCH interface leaf enabled node ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback100]/config/enabled" + expected_get_json = "{\"openconfig-interfaces:enabled\": true}" + t.Run("Test GET on interface PATCH enabled config", processGetRequest(url, nil, expected_get_json, false)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- DELETE at interface enabled ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback100]/config/enabled" + disable_err := errors.New("Disabling Loopback port is not supported") + t.Run("Test DELETE on interface enabled", processDeleteRequest(url, true, disable_err)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify DELETE at interface enabled ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback100]/config/enabled" + expected_get_json = "{\"openconfig-interfaces:enabled\": true}" + t.Run("Test GET on interface enabled delete config", processGetRequest(url, nil, expected_get_json, false)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- PATCH interface leaf enabled node false ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback100]/config/enabled" + url_input_body_json = "{\"openconfig-interfaces:enabled\": false}" + t.Run("Test PATCH on interface enabled", processSetRequest(url, url_input_body_json, "PATCH", true, disable_err)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- GET interface desc before patch ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback100]/config/description" + err_str := "Resource not found" + expected_err_invalid := tlerr.NotFoundError{Format: err_str} + expected_get_json = "{}" + t.Run("Test GET on interface config", processGetRequest(url, nil, expected_get_json, true, expected_err_invalid)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- PATCH interfaces desc ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback100]/config" + url_input_body_json = "{\"openconfig-interfaces:config\": { \"description\": \"UT_Loopback_Interface\", \"enabled\": true }}" + t.Run("Test PATCH on interface description config", processSetRequest(url, url_input_body_json, "PATCH", false, nil)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify PATCH interfaces desc config ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback100]/config" + expected_get_json = "{\"openconfig-interfaces:config\": {\"enabled\": true, \"name\": \"Loopback100\"}}" + t.Run("Test GET on interface desc config", processGetRequest(url, nil, expected_get_json, false)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- DELETE at interface desc ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback100]/config/description" + t.Run("Test DELETE on interface desc", processDeleteRequest(url, false)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify DELETE at interface desc ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback100]/config/description" + err_str = "Resource not found" + expected_err_invalid = tlerr.NotFoundError{Format: err_str} + expected_get_json = "{}" + t.Run("Test GET on interface config", processGetRequest(url, nil, expected_get_json, true, expected_err_invalid)) + time.Sleep(1 * time.Second) + + cleanuptbl := map[string]interface{}{"PORT_TABLE": map[string]interface{}{"Ethernet0": ""}} + unloadDB(db.ApplDB, cleanuptbl) + + //----------- + + t.Log("\n\n--- PATCH IPv4 address at addresses level ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback11]/subinterfaces/subinterface[index=0]/openconfig-if-ip:ipv4/addresses" + url_input_body_json = "{\"openconfig-if-ip:addresses\": {\"address\": [{\"ip\": \"24.4.4.4\", \"openconfig-if-ip:config\": {\"ip\": \"24.4.4.4\", \"prefix-length\": 24}}]}}" + time.Sleep(1 * time.Second) + t.Run("Test Patch/Set IPv4 address on subinterfaces addresses", processSetRequest(url, url_input_body_json, "PATCH", false, nil)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify IPv4 address at subinterfaces level ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback11]/subinterfaces" + expected_get_json = "{\"openconfig-interfaces:subinterfaces\": {\"subinterface\": [{\"config\": {\"index\": 0}, \"index\": 0, \"openconfig-if-ip:ipv4\": {\"addresses\": {\"address\": [{\"config\": {\"ip\": \"24.4.4.4\", \"prefix-length\": 24}, \"ip\": \"24.4.4.4\"}]}}, \"state\": {\"index\": 0}}]}}" + t.Run("Test Get/Verify Patch IPv4 address at subinterfaces level", processGetRequest(url, nil, expected_get_json, false)) + time.Sleep(1 * time.Second) + + //patch to update existing value (changing prefix length of ip) + t.Log("\n\n--- PATCH IPv4 Prefix length address at addresses level from 24 to 32 ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback11]/subinterfaces/subinterface[index=0]/openconfig-if-ip:ipv4/addresses" + url_input_body_json = "{\"openconfig-if-ip:addresses\": {\"address\": [{\"ip\": \"24.4.4.4\", \"openconfig-if-ip:config\": {\"ip\": \"24.4.4.4\", \"prefix-length\": 32}}]}}" + time.Sleep(1 * time.Second) + t.Run("Test Patch/Set IPv4 address on subinterfaces addresses", processSetRequest(url, url_input_body_json, "PATCH", false, nil)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify IPv4 address at subinterfaces level ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback11]/subinterfaces" + expected_get_json = "{\"openconfig-interfaces:subinterfaces\": {\"subinterface\": [{\"config\": {\"index\": 0}, \"index\": 0, \"openconfig-if-ip:ipv4\": {\"addresses\": {\"address\": [{\"config\": {\"ip\": \"24.4.4.4\", \"prefix-length\": 32}, \"ip\": \"24.4.4.4\"}]}}, \"state\": {\"index\": 0}}]}}" + t.Run("Test Get/Verify Patch IPv4 address at subinterfaces level", processGetRequest(url, nil, expected_get_json, false)) + time.Sleep(1 * time.Second) + + //patch to add new ip || for Ipv4 currently only one IP is allowed per interface + //Primary IP config already happened and replacing it with new one + + t.Log("\n\n--- PATCH IPv4 address at addresses level ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback11]/subinterfaces/subinterface[index=0]/openconfig-if-ip:ipv4/addresses" + url_input_body_json = "{\"openconfig-if-ip:addresses\": {\"address\": [{\"ip\": \"56.40.4.4\", \"openconfig-if-ip:config\": {\"ip\": \"56.40.4.4\", \"prefix-length\": 32}}]}}" + time.Sleep(1 * time.Second) + t.Run("Test Patch/Set IPv4 address on subinterfaces addresses", processSetRequest(url, url_input_body_json, "PATCH", false, nil)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify IPv4 address at subinterfaces level ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback11]/subinterfaces" + expected_get_json = "{\"openconfig-interfaces:subinterfaces\": {\"subinterface\": [{\"config\": {\"index\": 0}, \"index\": 0, \"openconfig-if-ip:ipv4\": {\"addresses\": {\"address\": [{\"config\": {\"ip\": \"56.40.4.4\", \"prefix-length\": 32}, \"ip\": \"56.40.4.4\"}]}}, \"state\": {\"index\": 0}}]}}" + t.Run("Test Get/Verify Patch IPv4 address at subinterfaces level", processGetRequest(url, nil, expected_get_json, false)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Put IPv4 address at addresses level ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback11]/subinterfaces/subinterface[index=0]/openconfig-if-ip:ipv4/addresses" + url_input_body_json = "{\"openconfig-if-ip:addresses\": {\"address\": [{\"ip\": \"44.40.4.4\", \"openconfig-if-ip:config\": {\"ip\": \"44.40.4.4\", \"prefix-length\": 32}}]}}" + time.Sleep(1 * time.Second) + t.Run("Test Put/Set IPv4 address on subinterfaces addresses", processSetRequest(url, url_input_body_json, "PUT", false, nil)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify IPv4 address at subinterfaces level ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback11]/subinterfaces" + expected_get_json = "{\"openconfig-interfaces:subinterfaces\": {\"subinterface\": [{\"config\": {\"index\": 0}, \"index\": 0, \"openconfig-if-ip:ipv4\": {\"addresses\": {\"address\": [{\"config\": {\"ip\": \"44.40.4.4\", \"prefix-length\": 32}, \"ip\": \"44.40.4.4\"}]}}, \"state\": {\"index\": 0}}]}}" + t.Run(" Verify Put IPv4 address at subinterfaces level", processGetRequest(url, nil, expected_get_json, false)) + time.Sleep(1 * time.Second) + + //Test conflicting IP (same subnet) + t.Log("\n\n--- PATCH IPv4 address at addresses level ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback22]/subinterfaces/subinterface[index=0]/openconfig-if-ip:ipv4/addresses" + url_input_body_json = "{\"openconfig-if-ip:addresses\": {\"address\": [{\"ip\": \"44.40.4.4\", \"openconfig-if-ip:config\": {\"ip\": \"44.40.4.4\", \"prefix-length\": 32}}]}}" + + err_str = "IP 44.40.4.4/32 overlaps with IP or IP Anycast 44.40.4.4/32 of Interface Loopback11" + expected_err1 := tlerr.InvalidArgsError{Format: err_str} + + time.Sleep(1 * time.Second) + t.Run("Test Patch/Set IPv4 address on another interface", processSetRequest(url, url_input_body_json, "PATCH", true, expected_err1)) + time.Sleep(1 * time.Second) + + //-------------- + t.Log("\n\n--- PATCH IPv4 address at addresses level ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback22]/subinterfaces/subinterface[index=0]/openconfig-if-ip:ipv4/addresses" + url_input_body_json = "{\"openconfig-if-ip:addresses\": {\"address\": [{\"ip\": \"34.4.4.4\", \"openconfig-if-ip:config\": {\"ip\": \"34.4.4.4\", \"prefix-length\": 24}}]}}" + time.Sleep(1 * time.Second) + t.Run("Test Patch/Set IPv4 address on subinterfaces addresses", processSetRequest(url, url_input_body_json, "PATCH", false, nil)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify IPv4 address at subinterfaces level ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback22]/subinterfaces" + expected_get_json = "{\"openconfig-interfaces:subinterfaces\": {\"subinterface\": [{\"config\": {\"index\": 0}, \"index\": 0, \"openconfig-if-ip:ipv4\": {\"addresses\": {\"address\": [{\"config\": {\"ip\": \"34.4.4.4\", \"prefix-length\": 24}, \"ip\": \"34.4.4.4\"}]}}, \"state\": {\"index\": 0}}]}}" + t.Run("Test Get/Verify Patch IPv4 address at subinterfaces level", processGetRequest(url, nil, expected_get_json, false)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- DELETE at interfaces/interface container Loopback22 interface---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback22]" + t.Run("Test DELETE on interface container", processDeleteRequest(url, false)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify DELETE at Loopback22 Interface ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback22]" + err_str = "Resource not found" + expected_get_json = "{}" + expected_err := tlerr.NotFoundError{Format: err_str} + t.Run("Test GET on deleted Loopback interface", processGetRequest(url, nil, expected_get_json, true, expected_err)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Test ethernet container ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback11]/openconfig-if-ethernet:ethernet/config/port-speed" + url_input_body_json = "{\"openconfig-if-ethernet:port-speed\":\"SPEED_40GB\"}" + err_str = "Error: Unsupported Interface: Loopback11" + invalid_port_err := errors.New(err_str) + t.Run("Test ethernet container", processSetRequest(url, url_input_body_json, "PATCH", true, invalid_port_err)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Test aggregate container ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback11]/openconfig-if-aggregate:aggregation/config/min-links" + url_input_body_json = "{\"openconfig-if-aggregator:min-links\": 2}" + err_str = "Container not supported for given interface type" + invalid_port_err = errors.New(err_str) + t.Run("Test aggregator container", processSetRequest(url, url_input_body_json, "PATCH", true, invalid_port_err)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- DELETE at interfaces/interface container Loopback11 interface ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback11]" + t.Run("Test DELETE on interface container", processDeleteRequest(url, false)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify DELETE at Loopback11 Interface ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback11]" + err_str = "Resource not found" + expected_err = tlerr.NotFoundError{Format: err_str} + t.Run("Test GET on deleted Loopback interface", processGetRequest(url, nil, "", true, expected_err)) + time.Sleep(1 * time.Second) +} + +func Test_openconfig_loopback_ipv6_ipv4_addresses(t *testing.T) { + t.Log("\n\n+++++++++++++ CONFIGURING LOOPBACK ++++++++++++") + + t.Log("\n\n--- PUT to Create Loopback14 ---") + url := "/openconfig-interfaces:interfaces/interface[name=Loopback14]" + url_input_body_json := "{\"openconfig-interfaces:interface\": [{\"name\":\"Loopback14\", \"config\": {\"name\": \"Loopback14\"}}]}" + t.Run("Test Create Loopback14", processSetRequest(url, url_input_body_json, "PUT", false, nil)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify Loopback Creation ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback14]/config" + expected_get_json := "{\"openconfig-interfaces:config\": {\"name\": \"Loopback14\", \"enabled\": true}}" + t.Run("Test GET Loopback interface creation config ", processGetRequest(url, nil, expected_get_json, false)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- PUT to Create Loopback15 ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback15]" + url_input_body_json = "{\"openconfig-interfaces:interface\": [{\"name\":\"Loopback15\", \"config\": {\"name\": \"Loopback15\"}}]}" + t.Run("Test Create Loopback15", processSetRequest(url, url_input_body_json, "PUT", false, nil)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify Loopback Creation ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback15]/config" + expected_get_json = "{\"openconfig-interfaces:config\": {\"name\": \"Loopback15\", \"enabled\": true}}" + t.Run("Test GET Loopback interface creation config ", processGetRequest(url, nil, expected_get_json, false)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- PUT to Create Loopback21 ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback21]" + url_input_body_json = "{\"openconfig-interfaces:interface\": [{\"name\":\"Loopback21\", \"config\": {\"name\": \"Loopback21\"}}]}" + t.Run("Test Create Loopback21", processSetRequest(url, url_input_body_json, "PUT", false, nil)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify Loopback Creation ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback21]/config" + expected_get_json = "{\"openconfig-interfaces:config\": {\"name\": \"Loopback21\", \"enabled\": true}}" + t.Run("Test GET Loopback interface creation config ", processGetRequest(url, nil, expected_get_json, false)) + time.Sleep(1 * time.Second) + + t.Log("\n\n-- PATCH IPv6 address at subinterfaces addresses ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback21]/subinterfaces/subinterface[index=0]/openconfig-if-ip:ipv6/addresses" + url_input_body_json = "{\"openconfig-if-ip:addresses\": {\"address\": [{\"ip\": \"2000::24\", \"openconfig-if-ip:config\": {\"ip\": \"2000::24\", \"prefix-length\": 64}}]}}" + time.Sleep(1 * time.Second) + t.Run("test Patch/Set IPv6 address on subinterfaces addresses", processSetRequest(url, url_input_body_json, "PATCH", false, nil)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify IPv6 address at subinterfaces level ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback21]/subinterfaces" + expected_get_json = "{\"openconfig-interfaces:subinterfaces\":{\"subinterface\":[{\"config\":{\"index\":0},\"index\":0,\"openconfig-if-ip:ipv6\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"2000::24\",\"prefix-length\":64},\"ip\":\"2000::24\"}]}}, \"state\":{\"index\":0}}]}}" + t.Run("Test Get/Verify Patch IPv6 address at subinterfaces level", processGetRequest(url, nil, expected_get_json, false)) + time.Sleep(1 * time.Second) + + //Test conflicting IP (same subnet) + t.Log("\n\n---PATCH IPv6 address at addresses level ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback14]/subinterfaces/subinterface[index=0]/openconfig-if-ip:ipv6/addresses" + url_input_body_json = "{\"openconfig-if-ip:addresses\": {\"address\": [{\"ip\": \"2000::24\", \"openconfig-if-ip:config\": {\"ip\": \"2000::24\", \"prefix-length\": 64}}]}}" + err_str := "IP 2000::24/64 overlaps with IP or IP Anycast 2000::24/64 of Interface Loopback21" + expected_err1 := tlerr.InvalidArgsError{Format: err_str} + time.Sleep(1 * time.Second) + t.Run("Test Patch/Set IPv6 address on another interface", processSetRequest(url, url_input_body_json, "PATCH", true, expected_err1)) + time.Sleep(1 * time.Second) + + // patch with same ip (updating existing address) + t.Log("\n\n--- PATCH IPv6 address at subinterfaces addresses ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback21]/subinterfaces/subinterface[index=0]/openconfig-if-ip:ipv6/addresses" + url_input_body_json = "{\"openconfig-if-ip:addresses\": {\"address\": [{\"ip\": \"2000::24\", \"openconfig-if-ip:config\": {\"ip\": \"2000::24\", \"prefix-length\": 64}}]}}" + time.Sleep(1 * time.Second) + t.Run("Test Patch/Set IPv6 address on subinterfaces addresses", processSetRequest(url, url_input_body_json, "PATCH", false, nil)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify IPv6 address at subinterfaces level ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback21]/subinterfaces" + expected_get_json = "{\"openconfig-interfaces:subinterfaces\":{\"subinterface\":[{\"config\":{\"index\":0},\"index\":0,\"openconfig-if-ip:ipv6\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"2000::24\",\"prefix-length\":64},\"ip\":\"2000::24\"}]}}, \"state\":{\"index\":0}}]}}" + t.Run(" Test Get/Verify Patch IPv6 address at subinterfaces level", processGetRequest(url, nil, expected_get_json, false)) + time.Sleep(1 * time.Second) + + // patch with new ip (adding new address) + t.Log("\n\n--- PATCH IPv6 address at subinterfaces addresses ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback21]/subinterfaces/subinterface[index=0]/openconfig-if-ip:ipv6/addresses" + url_input_body_json = "{\"openconfig-if-ip:addresses\": {\"address\": [{\"ip\": \"8000::42\", \"openconfig-if-ip:config\": {\"ip\": \"8000::42\", \"prefix-length\": 64}}]}}" + time.Sleep(1 * time.Second) + t.Run("Test Patch/Set IPv6 address on subinterfaces addresses", processSetRequest(url, url_input_body_json, "PATCH", false, nil)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify IPv6 address at subinterfaces level ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback21]/subinterfaces" + expected_get_json = "{\"openconfig-interfaces:subinterfaces\":{\"subinterface\":[{\"config\":{\"index\":0},\"index\":0,\"openconfig-if-ip:ipv6\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"2000::24\",\"prefix-length\":64},\"ip\":\"2000::24\"},{\"config\":{\"ip\":\"8000::42\",\"prefix-length\":64},\"ip\":\"8000::42\"}]}}, \"state\":{\"index\":0}}]}}" + t.Run(" Test Get/Verify Patch IPv6 address at subinterfaces level", processGetRequest(url, nil, expected_get_json, false)) + time.Sleep(1 * time.Second) + + //----------- + + t.Log("\n\n--- PATCH IPv4 address at addresses level ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback14]/subinterfaces/subinterface[index=0]/openconfig-if-ip:ipv4/addresses" + url_input_body_json = "{\"openconfig-if-ip:addresses\": {\"address\": [{\"ip\": \"74.74.74.74\", \"openconfig-if-ip:config\": {\"ip\": \"74.74.74.74\", \"prefix-length\": 24}}]}}" + time.Sleep(1 * time.Second) + t.Run("Test Patch/Set IPv4 address on subinterfaces addresses", processSetRequest(url, url_input_body_json, "PUT", false, nil)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify IPv4 address at subinterfaces level ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback14]/subinterfaces" + expected_get_json = "{\"openconfig-interfaces:subinterfaces\": {\"subinterface\": [{\"config\": {\"index\": 0}, \"index\": 0, \"openconfig-if-ip:ipv4\": {\"addresses\": {\"address\": [{\"config\": {\"ip\": \"74.74.74.74\", \"prefix-length\": 24}, \"ip\": \"74.74.74.74\"}]}}, \"state\": {\"index\": 0}}]}}" + t.Run("Test Get/Verify Patch IPv4 address at subinterfaces level", processGetRequest(url, nil, expected_get_json, false)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- PATCH IPv6 address at subinterfaces addresses ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback15]/subinterfaces/subinterface[index=0]/openconfig-if-ip:ipv6/addresses" + url_input_body_json = "{\"openconfig-if-ip:addresses\": {\"address\": [{\"ip\": \"2004::2\", \"openconfig-if-ip:config\": {\"ip\": \"2004::2\", \"prefix-length\": 64}}]}}" + time.Sleep(1 * time.Second) + t.Run("Test Patch/Set IPv6 address on subinterfaces addresses", processSetRequest(url, url_input_body_json, "PATCH", false, nil)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify IPv6 address at subinterfaces level ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback15]/subinterfaces" + expected_get_json = "{\"openconfig-interfaces:subinterfaces\":{\"subinterface\":[{\"config\":{\"index\":0},\"index\":0,\"openconfig-if-ip:ipv6\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"2004::2\",\"prefix-length\":64},\"ip\":\"2004::2\"}]}}, \"state\":{\"index\":0}}]}}" + t.Run("Test Get/Verify Patch IPv6 address at subinterfaces level", processGetRequest(url, nil, expected_get_json, false)) + + time.Sleep(1 * time.Second) + + //***currently for ipv6, replace operation doesn't remove Old Data, it just Appends the new data + //curr [a], put [b], ideally we should have [b], we have [a,b] + t.Log("\n\n--- PUT IPv6 address at subinterfaces addresses ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback15]/subinterfaces/subinterface[index=0]/openconfig-if-ip:ipv6/addresses" + url_input_body_json = "{\"openconfig-if-ip:addresses\": {\"address\": [{\"ip\": \"2006::8\", \"openconfig-if-ip:config\": {\"ip\": \"2006::8\", \"prefix-length\": 64}}]}}" + time.Sleep(1 * time.Second) + t.Run("Test Put/Set IPv6 address on subinterfaces addresses", processSetRequest(url, url_input_body_json, "PUT", false, nil)) + time.Sleep(1 * time.Second) + + t.Log("\n\n---Verify IPv6 address at subinterfaces level ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback15]/subinterfaces" + expected_get_json = "{\"openconfig-interfaces:subinterfaces\":{\"subinterface\":[{\"config\":{\"index\":0},\"index\":0,\"openconfig-if-ip:ipv6\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"2004::2\",\"prefix-length\":64},\"ip\":\"2004::2\"},{\"config\":{\"ip\":\"2006::8\",\"prefix-length\":64},\"ip\":\"2006::8\"}]}}, \"state\":{\"index\":0}}]}}" + t.Run("Test Get/Verify put IPv6 address at subinterfaces level", processGetRequest(url, nil, expected_get_json, false)) + time.Sleep(1 * time.Second) + + //we have [a,b], PUT [a_update], ideally we should have only [a_update] + //but we have [a_update, b] + t.Log("\n\n--- PUT IPv6 address at subinterfaces addresses ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback15]/subinterfaces/subinterface[index=0]/openconfig-if-ip:ipv6/addresses" + url_input_body_json = "{\"openconfig-if-ip:addresses\": {\"address\": [{\"ip\": \"2004::2\", \"openconfig-if-ip:config\": {\"ip\": \"2004::2\", \"prefix-length\": 64}}]}}" + time.Sleep(1 * time.Second) + t.Run("Test Put/Set IPv6 address on subinterfaces addresses", processSetRequest(url, url_input_body_json, "PUT", false, nil)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify IPv6 address at subinterfaces level ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback15]/subinterfaces" + expected_get_json = "{\"openconfig-interfaces:subinterfaces\":{\"subinterface\":[{\"config\":{\"index\":0},\"index\":0,\"openconfig-if-ip:ipv6\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"2004::2\",\"prefix-length\":64},\"ip\":\"2004::2\"},{\"config\":{\"ip\":\"2006::8\",\"prefix-length\":64},\"ip\":\"2006::8\"}]}}, \"state\":{\"index\":0}}]}}" + t.Run("Test Get/Verify put IPv6 address at subinterfaces level", processGetRequest(url, nil, expected_get_json, false)) + time.Sleep(1 * time.Second) + //------------------------------------------------------------------------------------------------------------------------------------ + err_str = "Resource not found" + expected_err := tlerr.NotFoundError{Format: err_str} + + t.Log("\n\n+++++++++++++ REMOVING IPv4 ADDRESS AT SUBINTERFACES LOOPBACK INTERFACE ++++++++++++") + t.Log("\n\n--- Delete/Clear existing IPv4 address on Loopback Interface ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback14]/subinterfaces/subinterface[index=0]/openconfig-if-ip:ipv4/addresses" + t.Run("Test Delete/Clear IPv4 on subinterfaces", processDeleteRequest(url, false)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Get/Verify IPv4 address at subinterfaces ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback14]/subinterfaces/subinterface[index=0]/openconfig-if-ip:ipv4" + expected_get_json = "{}" + t.Run("Test Get/Verify Delete IPv4 address at subinterfaces addresses", processGetRequest(url, nil, expected_get_json, false, nil)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Delete IPv6 address at subinterfaces addresses level---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback15]/subinterfaces/subinterface[index=0]/openconfig-if-ip:ipv6/addresses" + t.Run("Test Delete IPv6 address at subinterfaces addresses level", processDeleteRequest(url, false)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify Delete IPv6 address at subinterfaces addresses ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback15]/subinterfaces/subinterface[index=0]/ipv6/addresses" + expected_get_json = "{}" + t.Run("Test Get/Verify Delete IPv6 address at subinterfaces addresses", processGetRequest(url, nil, expected_get_json, false, nil)) + time.Sleep(1 * time.Second) + + t.Log("\n\n---Delete existing IPv6 address on Loopback Interface at subinterfaces addresses address level ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback21]/subinterfaces/subinterface[index=0]/openconfig-if-ip:ipv6/addresses/address[ip=8000::42]" + t.Run("Test Delete IPv6 on subinterfaces addresses address[x]", processDeleteRequest(url, false)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify delete IPv6 address at subinterfaces level ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback21]/subinterfaces" + expected_get_json = "{\"openconfig-interfaces:subinterfaces\":{\"subinterface\":[{\"config\":{\"index\":0},\"index\":0,\"openconfig-if-ip:ipv6\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"2000::24\",\"prefix-length\":64},\"ip\":\"2000::24\"}]}}, \"state\":{\"index\":0}}]}}" + t.Run(" Test Get/Verify delete IPv6 address at subinterfaces level", processGetRequest(url, nil, expected_get_json, false)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Delete IPv6 address at subinterfaces addresses level---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback21]/subinterfaces/subinterface[index=0]/openconfig-if-ip:ipv6/addresses" + t.Run("Test Delete IPv6 address at subinterfaces addresses level", processDeleteRequest(url, false)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify Delete IPv6 address at subinterfaces addresses ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback21]/subinterfaces/subinterface[index=0]/ipv6/addresses" + expected_get_json = "{}" + t.Run(" Test Get/Verify Delete IPv6 address at subinterfaces addresses", processGetRequest(url, nil, expected_get_json, false, nil)) + time.Sleep(1 * time.Second) + + //------ + + t.Log("\n\n--- DELETE at Loopback21 Interface ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback21]" + t.Run(" Test DELETE loopback interface", processDeleteRequest(url, false)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify DELETE at Loopback21 Interface ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback21]" + err_str = "Resource not found" + expected_err = tlerr.NotFoundError{Format: err_str} + expected_get_json = "{}" + t.Run("Test GET on deleted Loopback interface", processGetRequest(url, nil, expected_get_json, true, expected_err)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- DELETE at Loopback15 Interface ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback15]" + t.Run("Test DELETE Loopback interface", processDeleteRequest(url, false)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify DELETE at Loopback15 Interface ---") + url = "/openconfig-interfaces:interfaces/interface[name=Loopback15]" + err_str = "Resource not found" + expected_err = tlerr.NotFoundError{Format: err_str} + expected_get_json = "{}" + t.Run("Test GET on deleted Loopback interface", processGetRequest(url, nil, expected_get_json, true, expected_err)) + time.Sleep(1 * time.Second) +} diff --git a/translib/transformer/xfmr_intf.go b/translib/transformer/xfmr_intf.go index f18de7a26..563118c20 100644 --- a/translib/transformer/xfmr_intf.go +++ b/translib/transformer/xfmr_intf.go @@ -42,9 +42,12 @@ func init() { XlateFuncBind("DbToYang_intf_tbl_key_xfmr", DbToYang_intf_tbl_key_xfmr) XlateFuncBind("YangToDb_intf_mtu_xfmr", YangToDb_intf_mtu_xfmr) XlateFuncBind("DbToYang_intf_mtu_xfmr", DbToYang_intf_mtu_xfmr) + XlateFuncBind("YangToDb_intf_name_xfmr", YangToDb_intf_name_xfmr) + XlateFuncBind("DbToYang_intf_name_xfmr", DbToYang_intf_name_xfmr) XlateFuncBind("DbToYang_intf_admin_status_xfmr", DbToYang_intf_admin_status_xfmr) XlateFuncBind("YangToDb_intf_enabled_xfmr", YangToDb_intf_enabled_xfmr) XlateFuncBind("DbToYang_intf_enabled_xfmr", DbToYang_intf_enabled_xfmr) + XlateFuncBind("DbToYang_intf_description_xfmr", DbToYang_intf_description_xfmr) XlateFuncBind("DbToYang_intf_eth_aggr_id_xfmr", DbToYang_intf_eth_aggr_id_xfmr) XlateFuncBind("YangToDb_intf_eth_port_config_xfmr", YangToDb_intf_eth_port_config_xfmr) XlateFuncBind("DbToYang_intf_eth_port_config_xfmr", DbToYang_intf_eth_port_config_xfmr) @@ -88,6 +91,7 @@ const ( PORT_SPEED = "speed" PORT_AUTONEG = "autoneg" + PORT_DESCRIPTION = "description" PORTCHANNEL_INTERFACE_TN = "PORTCHANNEL_INTERFACE" PORTCHANNEL_MEMBER_TN = "PORTCHANNEL_MEMBER" DEFAULT_MTU = "9100" @@ -98,6 +102,7 @@ const ( COLON = ":" ETHERNET = "Eth" PORTCHANNEL = "PortChannel" + LOOPBACK = "Loopback" ) type TblData struct { @@ -134,10 +139,15 @@ var IntfTypeTblMap = map[E_InterfaceType]IntfTblData{ appDb: TblData{portTN: "LAG_TABLE", intfTN: "INTF_TABLE", keySep: COLON, memberTN: "LAG_MEMBER_TABLE"}, stateDb: TblData{portTN: "LAG_TABLE", intfTN: "INTERFACE_TABLE", keySep: PIPE}, }, + IntfTypeLoopback: IntfTblData{ + cfgDb: TblData{portTN: "LOOPBACK_INTERFACE", intfTN: "LOOPBACK_INTERFACE", keySep: PIPE}, + appDb: TblData{portTN: "INTF_TABLE", intfTN: "INTF_TABLE", keySep: COLON}, + stateDb: TblData{portTN: "INTERFACE_TABLE", intfTN: "INTERFACE_TABLE", keySep: PIPE}, + }, } var dbIdToTblMap = map[db.DBNum][]string{ - db.ConfigDB: {"PORT", "PORTCHANNEL"}, + db.ConfigDB: {"PORT", "PORTCHANNEL", "LOOPBACK_INTERFACE"}, db.ApplDB: {"PORT_TABLE", "LAG_TABLE"}, db.StateDB: {"PORT_TABLE", "LAG_TABLE"}, } @@ -164,6 +174,7 @@ const ( IntfTypeUnset E_InterfaceType = 0 IntfTypeEthernet E_InterfaceType = 1 IntfTypePortChannel E_InterfaceType = 2 + IntfTypeLoopback E_InterfaceType = 3 ) type E_InterfaceSubType int64 @@ -179,6 +190,8 @@ func getIntfTypeByName(name string) (E_InterfaceType, E_InterfaceSubType, error) return IntfTypeEthernet, IntfSubTypeUnset, err } else if strings.HasPrefix(name, PORTCHANNEL) { return IntfTypePortChannel, IntfSubTypeUnset, err + } else if strings.HasPrefix(name, LOOPBACK) { + return IntfTypeLoopback, IntfSubTypeUnset, err } else { err = errors.New("Interface name prefix not matched with supported types") return IntfTypeUnset, IntfSubTypeUnset, err @@ -224,6 +237,16 @@ func performIfNameKeyXfmrOp(inParams *XfmrParams, requestUriPath *string, ifName log.Errorf("Deleting LAG: %s failed! Err:%v", *ifName, err) return tlerr.InvalidArgsError{Format: err.Error()} } + + case IntfTypeLoopback: + err = deleteAllIPsForLoopbackInterface(inParams, ifName) + + if err != nil { + log.Errorf("Deleting Loopback Interface: %s failed! Err:%v", *ifName, err) + return errors.New("Loopback interface is not found " + *ifName) + } + return err + case IntfTypeEthernet: err = validateIntfExists(inParams.d, IntfTypeTblMap[IntfTypeEthernet].cfgDb.portTN, *ifName) if err != nil { @@ -268,6 +291,15 @@ func performIfNameKeyXfmrOp(inParams *XfmrParams, requestUriPath *string, ifName } } } + if ifType == IntfTypeLoopback { + if inParams.oper == UPDATE { + err = validateIntfExists(inParams.d, IntfTypeTblMap[IntfTypeLoopback].cfgDb.portTN, *ifName) + if err != nil { + errStr := "Loopback interface: " + *ifName + " does not exist" + return tlerr.InvalidArgsError{Format: errStr} + } + } + } } return err } @@ -590,12 +622,27 @@ var YangToDb_intf_enabled_xfmr FieldXfmrYangToDb = func(inParams XfmrParams) (ma } enabled, _ := inParams.param.(*bool) + if enabled == nil { + return res_map, nil + } var enStr string if *enabled { enStr = "up" } else { enStr = "down" } + + pathInfo := NewPathInfo(inParams.uri) + ifName := pathInfo.Var("name") + intfType, _, ierr := getIntfTypeByName(ifName) + if intfType == IntfTypeUnset || ierr != nil { + return res_map, errors.New("Invalid interface type") + } + + if IntfTypeLoopback == intfType && (enStr == "down" || inParams.oper == DELETE) { + return res_map, errors.New("Disabling Loopback port is not supported") + } + res_map[PORT_ADMIN_STATUS] = enStr return res_map, nil @@ -652,6 +699,11 @@ var YangToDb_intf_mtu_xfmr FieldXfmrYangToDb = func(inParams XfmrParams) (map[st } } intfType, _, _ := getIntfTypeByName(ifName) + // MTU is not supported for loopback interface in SONiC yang model + if IntfTypeLoopback == intfType { + log.Errorf("MTU configuration for Loopback type Interface: %s is NOT supported ", ifName) + return res_map, errors.New("Configuration for MTU is not supported for Loopback interface ") + } if inParams.oper == DELETE { log.Infof("Updating the Interface: %s with default MTU", ifName) @@ -742,6 +794,12 @@ var YangToDb_intf_eth_port_config_xfmr SubTreeXfmrYangToDb = func(inParams XfmrP return nil, err } + // check for loopback interface + if (intfType == IntfTypeLoopback) && inParams.oper != DELETE { + log.Info("YangToDb_intf_eth_port_config_xfmr: Configuration for ethernet container not supported for interface.") + return nil, errors.New("Error: Unsupported Interface: " + ifName) + } + intfsObj := getIntfsRoot(inParams.ygRoot) intfObj := intfsObj.Interface[uriIfName] @@ -845,6 +903,9 @@ var YangToDb_intf_eth_port_config_xfmr SubTreeXfmrYangToDb = func(inParams XfmrP value := db.Value{Field: res_map} intTbl := IntfTypeTblMap[intfType] + if intfType != IntfTypeEthernet { + return nil, errors.New("Speed config not supported for given Interface type") + } portSpeed := intfObj.Ethernet.Config.PortSpeed val, ok := intfOCToSpeedMap[portSpeed] if ok { @@ -915,6 +976,12 @@ var DbToYang_intf_eth_port_config_xfmr SubTreeXfmrDbToYang = func(inParams XfmrP err = tlerr.InvalidArgsError{Format: errStr} return err } + + //Check for loopback interface type + if intfType == IntfTypeLoopback { + return nil + } + intTbl := IntfTypeTblMap[intfType] tblName := intTbl.cfgDb.portTN entry, dbErr := inParams.dbs[db.ConfigDB].GetEntry(&db.TableSpec{Name: tblName}, db.Key{Comp: []string{ifName}}) @@ -1544,6 +1611,11 @@ var DbToYang_intf_get_ether_counters_xfmr SubTreeXfmrDbToYang = func(inParams Xf log.Info("DbToYang_intf_get_ether_counters_xfmr - Invalid interface type IntfTypeUnset") return errors.New("Invalid interface type IntfTypeUnset") } + // Check for loopback interface type + if intfType == IntfTypeLoopback { + log.Info("DbToYang_intf_get_ether_counters_xfmr - Loopback interface not supported") + return nil + } if !strings.Contains(targetUriPath, "/openconfig-interfaces:interfaces/interface/ethernet/state/counters") && !strings.Contains(targetUriPath, "/openconfig-interfaces:interfaces/interface/openconfig-if-ethernet:ethernet/state/counters") { @@ -2147,7 +2219,7 @@ var DbToYang_intf_ip_addr_xfmr SubTreeXfmrDbToYang = func(inParams XfmrParams) e return nil } - intfTypeList := [2]E_InterfaceType{IntfTypeEthernet, IntfTypePortChannel} + intfTypeList := [3]E_InterfaceType{IntfTypeEthernet, IntfTypePortChannel, IntfTypeLoopback} // Get IP from all configDb table interfaces for i := 0; i < len(intfTypeList); i++ { @@ -2845,7 +2917,7 @@ var Subscribe_intf_ip_addr_xfmr = func(inParams XfmrSubscInParams) (XfmrSubscOut result.dbDataMap = RedisDbSubscribeMap{db.ConfigDB: {tableName: {keyName: {}}}} } else { result.dbDataMap = RedisDbSubscribeMap{db.ConfigDB: {"INTERFACE": {keyName: {}}, - "PORTCHANNEL_INTERFACE": {keyName: {}}}} + "PORTCHANNEL_INTERFACE": {keyName: {}}, "LOOPBACK_INTERFACE": {keyName: {}}}} } } else if targetUriPath == addressStatePath { keyName = ifKey + ":" + ipKey @@ -2973,6 +3045,11 @@ var YangToDb_ipv6_enabled_xfmr FieldXfmrYangToDb = func(inParams XfmrParams) (ma if ierr != nil || intfType == IntfTypeUnset { return res_map, errors.New("YangToDb_ipv6_enabled_xfmr, Error: Unsupported Interface: " + ifUIName) } + // ipv6 Enabled leaf is not available in SONiC yang model for loopback interface + if intfType == IntfTypeLoopback { + log.Info("YangToDb_ipv6_enabled_xfmr: Configuration for ipv6 enabled not supported for given interface.") + return res_map, nil + } if ifUIName == "" { errStr := "Interface KEY not present" @@ -3082,6 +3159,11 @@ var DbToYang_ipv6_enabled_xfmr FieldXfmrDbtoYang = func(inParams XfmrParams) (ma log.Info("Interface Name = ", *ifUIName) intfType, _, _ := getIntfTypeByName(inParams.key) + // ipv6 Enabled leaf is not available in SONiC yang model for loopback interface + if intfType == IntfTypeLoopback { + log.Info("DbToYang_ipv6_enabled_xfmr not supported for given interface ") + return res_map, nil + } intTbl := IntfTypeTblMap[intfType] tblName, _ := getIntfTableNameByDBId(intTbl, inParams.curDb) @@ -3097,6 +3179,41 @@ var DbToYang_ipv6_enabled_xfmr FieldXfmrDbtoYang = func(inParams XfmrParams) (ma return res_map, nil } +var DbToYang_intf_description_xfmr FieldXfmrDbtoYang = func(inParams XfmrParams) (map[string]interface{}, error) { + result := make(map[string]interface{}) + + data := (*inParams.dbDataMap)[inParams.curDb] + + intfType, _, ierr := getIntfTypeByName(inParams.key) + if intfType == IntfTypeUnset || ierr != nil { + log.Info("DbToYang_intf_description_xfmr - Invalid interface type IntfTypeUnset") + return result, errors.New("Invalid interface type IntfTypeUnset") + } + + intTbl := IntfTypeTblMap[intfType] + + tblName, _ := getPortTableNameByDBId(intTbl, inParams.curDb) + if _, ok := data[tblName]; !ok { + log.Info("DbToYang_intf_description_xfmr table not found : ", tblName) + return result, errors.New("table not found : " + tblName) + } + + pTbl := data[tblName] + if _, ok := pTbl[inParams.key]; !ok { + log.Info("DbToYang_intf_description_xfmr Interface not found : ", inParams.key) + return result, errors.New("Interface not found : " + inParams.key) + } + prtInst := pTbl[inParams.key] + desc, ok := prtInst.Field[PORT_DESCRIPTION] + if ok && desc != "" { + result["description"] = desc + } else { + log.Info("Description field not found in DB") + } + + return result, nil +} + func getMemTableNameByDBId(intftbl IntfTblData, curDb db.DBNum) (string, error) { var tblName string @@ -3145,3 +3262,74 @@ func retrievePortChannelAssociatedWithIntf(inParams *XfmrParams, ifName *string) } return nil, err } + +func deleteAllIPsForLoopbackInterface(inParams *XfmrParams, ifName *string) error { + + subOpMap := make(map[db.DBNum]map[string]map[string]db.Value) + subIntfmap_del := make(map[string]map[string]db.Value) + intfType, _, ierr := getIntfTypeByName(*ifName) + if ierr != nil || intfType != IntfTypeLoopback { + return tlerr.InvalidArgsError{Format: "Invalid Loopback: " + *ifName} + } + + intTbl := IntfTypeTblMap[intfType] + tblName, _ := getIntfTableNameByDBId(intTbl, inParams.curDb) + + entry, dbErr := inParams.d.GetEntry(&db.TableSpec{Name: intTbl.cfgDb.intfTN}, db.Key{Comp: []string{*ifName}}) + if dbErr != nil || !entry.IsPopulated() { + // Not returning error from here since mgmt infra will return "Resource not found" error in case of non-existence entries + return nil + } + + subIntfmap_del[tblName] = make(map[string]db.Value) + subIntfmap_del[tblName][*ifName] = db.Value{Field: map[string]string{}} + + intfTable := &db.TableSpec{Name: tblName} + intfKeys, err := inParams.d.GetKeysPattern(intfTable, db.Key{Comp: []string{*ifName, "*"}}) + + if err == nil && len(intfKeys) > 0 { + for _, intfKey := range intfKeys { + ipPrefix := intfKey.Comp[1] + key := *ifName + "|" + ipPrefix + subIntfmap_del[tblName][key] = db.Value{Field: map[string]string{}} + } + } + subOpMap[db.ConfigDB] = subIntfmap_del + inParams.subOpDataMap[DELETE] = &subOpMap + return nil +} + +var YangToDb_intf_name_xfmr FieldXfmrYangToDb = func(inParams XfmrParams) (map[string]string, error) { + res_map := make(map[string]string) + + pathInfo := NewPathInfo(inParams.uri) + ifName := pathInfo.Var("name") + intfType, _, err := getIntfTypeByName(ifName) + if intfType == IntfTypeUnset || err != nil { + log.Info("DbToYang_intf_name_xfmr - Invalid interface type IntfTypeUnset") + return res_map, errors.New("Invalid interface type IntfTypeUnset") + } + + err = errors.New("Invalid interface config/name received") + configName, ok := inParams.param.(*string) + if !ok || ifName != *configName { + return nil, err + } + + res_map["NULL"] = "NULL" + return res_map, nil +} + +var DbToYang_intf_name_xfmr FieldXfmrDbtoYang = func(inParams XfmrParams) (map[string]interface{}, error) { + var err error + result := make(map[string]interface{}) + + intfType, _, err := getIntfTypeByName(inParams.key) + if intfType == IntfTypeUnset || err != nil { + log.Info("DbToYang_intf_name_xfmr - Invalid interface type IntfTypeUnset") + return result, errors.New("Invalid interface type IntfTypeUnset") + } + + result["name"] = inParams.key + return result, nil +}