diff --git a/config/transformer/models_list b/config/transformer/models_list index ba3f12d0a..4ba3f0ca2 100644 --- a/config/transformer/models_list +++ b/config/transformer/models_list @@ -7,3 +7,5 @@ openconfig-if-ethernet.yang openconfig-interfaces.yang openconfig-interfaces-annot.yang openconfig-if-ip.yang +openconfig-if-aggregate.yang +openconfig-interfaces-annot.yang diff --git a/go.sum b/go.sum index 404176218..d53532d67 100644 --- a/go.sum +++ b/go.sum @@ -109,6 +109,7 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= @@ -174,5 +175,6 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +inet.af v0.0.0-20181218191229-53da77bc832c h1:U3RoiyEF5b3Y1SVL6NNvpkgqUz2qS3a0OJh9kpSCN04= inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a h1:1XCVEdxrvL6c0TGOhecLuB7U9zYNdxZEjvOqJreKZiM= inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a/go.mod h1:e83i32mAQOW1LAqEIweALsuK2Uw4mhQadA5r7b0Wobo= diff --git a/models/yang/annotations/openconfig-interfaces-annot.yang b/models/yang/annotations/openconfig-interfaces-annot.yang index df3ff8647..70c115449 100644 --- a/models/yang/annotations/openconfig-interfaces-annot.yang +++ b/models/yang/annotations/openconfig-interfaces-annot.yang @@ -43,6 +43,12 @@ module openconfig-interfaces-annot { } } + deviation /oc-intf:interfaces/oc-intf:interface/oc-eth:ethernet/oc-eth:state/oc-lag:aggregate-id { + deviate add { + sonic-ext:field-transformer "intf_eth_aggr_id_xfmr"; + } + } + deviation /oc-intf:interfaces/oc-intf:interface/oc-intf:state { deviate add { sonic-ext:db-name "APPL_DB"; @@ -70,6 +76,20 @@ module openconfig-interfaces-annot { } } + deviation /oc-intf:interfaces/oc-intf:interface/oc-lag:aggregation/oc-lag:config/oc-lag:min-links { + deviate add { + sonic-ext:field-transformer "lag_min_links_xfmr"; + sonic-ext:field-name "min_links"; + } + } + + deviation /oc-intf:interfaces/oc-intf:interface/oc-lag:aggregation/oc-lag:state { + deviate add { + sonic-ext:subtree-transformer "intf_lag_state_xfmr"; + sonic-ext:path-transformer "intf_lag_state_path_xfmr"; + } + } + deviation /oc-intf:interfaces/oc-intf:interface/oc-intf:state/oc-intf:enabled { deviate add { sonic-ext:field-transformer "intf_enabled_xfmr"; diff --git a/models/yang/extensions/openconfig-interfaces-deviation.yang b/models/yang/extensions/openconfig-interfaces-deviation.yang index 33885d8f1..a6cc663c3 100644 --- a/models/yang/extensions/openconfig-interfaces-deviation.yang +++ b/models/yang/extensions/openconfig-interfaces-deviation.yang @@ -223,10 +223,6 @@ module openconfig-interfaces-deviation { deviate not-supported; } - deviation /oc-intf:interfaces/oc-intf:interface/oc-eth:ethernet/oc-eth:config/oc-lag:aggregate-id { - deviate not-supported; - } - deviation /oc-intf:interfaces/oc-intf:interface/oc-eth:ethernet/oc-eth:state/oc-eth:mac-address { deviate not-supported; } @@ -239,10 +235,6 @@ module openconfig-interfaces-deviation { deviate not-supported; } - deviation /oc-intf:interfaces/oc-intf:interface/oc-eth:ethernet/oc-eth:state/oc-lag:aggregate-id { - deviate not-supported; - } - deviation /oc-intf:interfaces/oc-intf:interface/oc-eth:ethernet/oc-eth:state/oc-eth:hw-mac-address { deviate not-supported; } @@ -255,6 +247,30 @@ module openconfig-interfaces-deviation { deviate not-supported; } + deviation /oc-intf:interfaces/oc-intf:interface/oc-eth:ethernet/oc-vlan:switched-vlan { + deviate not-supported; + } + + deviation /oc-intf:interfaces/oc-intf:interface/oc-lag:aggregation/oc-vlan:switched-vlan { + deviate not-supported; + } + + deviation /oc-intf:interfaces/oc-intf:interface/oc-lag:aggregation/oc-lag:config/oc-lag:lag-type { + deviate not-supported; + } + + deviation /oc-intf:interfaces/oc-intf:interface/oc-lag:aggregation/oc-lag:state/oc-lag:lag-type { + deviate not-supported; + } + + deviation /oc-intf:interfaces/oc-intf:interface/oc-lag:aggregation/oc-lag:state/oc-lag:lag-speed { + deviate not-supported; + } + + deviation /oc-intf:interfaces/oc-intf:interface/oc-lag:aggregation/oc-lag:state/oc-lag:member { + deviate not-supported; + } + deviation /oc-intf:interfaces/oc-intf:interface/oc-eth:ethernet/oc-eth:state/oc-eth:counters/oc-eth:in-mac-control-frames { deviate not-supported; } @@ -287,18 +303,10 @@ module openconfig-interfaces-deviation { deviate not-supported; } - deviation /oc-intf:interfaces/oc-intf:interface/oc-eth:ethernet/oc-vlan:switched-vlan { - deviate not-supported; - } - deviation /oc-intf:interfaces/oc-intf:interface/oc-tun:tunnel { deviate not-supported; } - deviation /oc-intf:interfaces/oc-intf:interface/oc-lag:aggregation { - deviate not-supported; - } - deviation /oc-intf:interfaces/oc-intf:interface/oc-vlan:routed-vlan { deviate not-supported; } diff --git a/models/yang/sonic/import.mk b/models/yang/sonic/import.mk index 7cac2d4e0..8868cf898 100644 --- a/models/yang/sonic/import.mk +++ b/models/yang/sonic/import.mk @@ -7,4 +7,5 @@ # SONICYANG_IMPORTS += sonic-sflow.yang SONICYANG_IMPORTS += sonic-interface.yang -SONICYANG_IMPORTS += sonic-port.yang \ No newline at end of file +SONICYANG_IMPORTS += sonic-port.yang +SONICYANG_IMPORTS += sonic-portchannel.yang \ No newline at end of file diff --git a/translib/transformer/portchannel_openconfig_test.go b/translib/transformer/portchannel_openconfig_test.go new file mode 100644 index 000000000..da122d922 --- /dev/null +++ b/translib/transformer/portchannel_openconfig_test.go @@ -0,0 +1,111 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2024 Dell, Inc. // +// // +// 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 ( + "github.com/Azure/sonic-mgmt-common/translib/tlerr" + "testing" + "time" +) + +func Test_openconfig_portchannel(t *testing.T) { + var url, url_input_body_json string + + t.Log("\n\n+++++++++++++ CONFIGURING PORTCHANNEL ++++++++++++") + + t.Log("\n\n--- POST to Create PortChannel 111 ---") + url = "/openconfig-interfaces:interfaces" + url_input_body_json = "{\"openconfig-interfaces:interface\": [{\"name\":\"PortChannel111\", \"config\": {\"name\": \"PortChannel111\", \"mtu\": 9100, \"description\": \"put_pc\", \"enabled\": true}}]}" + t.Run("Test Create PortChannel111", processSetRequest(url, url_input_body_json, "POST", false, nil)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify PortChannel Creation ---") + url = "/openconfig-interfaces:interfaces/interface[name=PortChannel111]/config" + expected_get_json := "{\"openconfig-interfaces:config\": {\"description\": \"put_pc\", \"enabled\": true, \"mtu\": 9100, \"name\": \"PortChannel111\"}}" + t.Run("Test GET PortChannel interface creation config ", processGetRequest(url, nil, expected_get_json, false)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Initialize PortChannel Member ---") + t.Log("\n\n--- DELETE interface IP Addr ---") + url = "/openconfig-interfaces:interfaces/interface[name=Ethernet0]/subinterfaces/subinterface[index=0]/openconfig-if-ip:ipv4/addresses" + t.Run("DELETE on interface IP Addr", processDeleteRequest(url, true)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- PATCH to Add PortChannel Member ---") + url = "/openconfig-interfaces:interfaces/interface[name=Ethernet0]/openconfig-if-ethernet:ethernet/config/openconfig-if-aggregate:aggregate-id" + url_input_body_json = "{\"openconfig-if-aggregate:aggregate-id\":\"PortChannel111\"}" + t.Run("Test PATCH on Ethernet aggregate-id", processSetRequest(url, url_input_body_json, "PATCH", false, nil)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify the added PortChannel Member ---") + url = "/openconfig-interfaces:interfaces/interface[name=Ethernet0]/openconfig-if-ethernet:ethernet/config/openconfig-if-aggregate:aggregate-id" + expected_get_json = "{\"openconfig-if-aggregate:aggregate-id\": \"PortChannel111\"}" + t.Run("Test GET on portchannel agg-id", processGetRequest(url, nil, expected_get_json, false)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- PATCH PortChannel min-links ---") + url = "/openconfig-interfaces:interfaces/interface[name=PortChannel111]/openconfig-if-aggregate:aggregation/config/min-links" + url_input_body_json = "{\"openconfig-if-aggregate:min-links\":3}" + t.Run("Test PATCH min-links on portchannel", processSetRequest(url, url_input_body_json, "PATCH", false, nil)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify PATCH PortChannel config ---") + url = "/openconfig-interfaces:interfaces/interface[name=PortChannel111]/openconfig-if-aggregate:aggregation/config" + expected_get_json = "{\"openconfig-if-aggregate:config\": {\"min-links\": 3}}" + t.Run("Test GET on portchannel config", processGetRequest(url, nil, expected_get_json, false)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- DELETE PortChannel min-links ---") + url = "/openconfig-interfaces:interfaces/interface[name=PortChannel111]/openconfig-if-aggregate:aggregation/config/min-links" + t.Run("Verify DELETE on PortChannel min-links", processDeleteRequest(url, true)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify DELETE PortChannel min-links ---") + url = "/openconfig-interfaces:interfaces/interface[name=PortChannel111]/openconfig-if-aggregate:aggregation/config" + expected_get_json = "{\"openconfig-if-aggregate:config\": {\"min-links\": 3}}" + t.Run("Test GET on portchannel min-links after DELETE", processGetRequest(url, nil, expected_get_json, false)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- PATCH PortChannel interface Config ---") + url = "/openconfig-interfaces:interfaces/interface[name=PortChannel111]/config" + url_input_body_json = "{\"openconfig-interfaces:config\": { \"mtu\": 8900, \"description\": \"agg_intf_conf\", \"enabled\": false}}" + t.Run("Test PATCH PortChannel interface Config", processSetRequest(url, url_input_body_json, "PATCH", false, nil)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify PATCH interfaces config ---") + url = "/openconfig-interfaces:interfaces/interface[name=PortChannel111]/config" + expected_get_json = "{\"openconfig-interfaces:config\": {\"description\": \"agg_intf_conf\", \"enabled\": false, \"mtu\": 8900, \"name\": \"PortChannel111\"}}" + t.Run("Test GET PortChannel interface Config", processGetRequest(url, nil, expected_get_json, false)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- DELETE PortChannel interface ---") + url = "/openconfig-interfaces:interfaces/interface[name=PortChannel111]" + t.Run("Test DELETE on PortChannel", processDeleteRequest(url, true)) + time.Sleep(1 * time.Second) + + t.Log("\n\n--- Verify DELETE at PortChannel Interface ---") + url = "/openconfig-interfaces:interfaces/interface[name=PortChannel111]" + err_str := "Resource not found" + expected_err_invalid := tlerr.NotFoundError{Format: err_str} + t.Run("Test GET on deleted PortChannel", processGetRequest(url, nil, "", true, expected_err_invalid)) + time.Sleep(1 * time.Second) +} diff --git a/translib/transformer/sw_portchannel.go b/translib/transformer/sw_portchannel.go new file mode 100644 index 000000000..08226176e --- /dev/null +++ b/translib/transformer/sw_portchannel.go @@ -0,0 +1,449 @@ +// // +// Copyright 2024 Dell, Inc. // +// // +// 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. // +// // +//////////////////////////////////////////////////////////////////////////////// + +package transformer + +import ( + "errors" + "strconv" + "strings" + + "github.com/Azure/sonic-mgmt-common/translib/db" + "github.com/Azure/sonic-mgmt-common/translib/ocbinds" + "github.com/Azure/sonic-mgmt-common/translib/tlerr" + log "github.com/golang/glog" + "github.com/openconfig/ygot/ygot" +) + +func init() { + XlateFuncBind("YangToDb_lag_min_links_xfmr", YangToDb_lag_min_links_xfmr) + XlateFuncBind("DbToYang_lag_min_links_xfmr", DbToYang_lag_min_links_xfmr) + XlateFuncBind("DbToYang_intf_lag_state_xfmr", DbToYang_intf_lag_state_xfmr) + XlateFuncBind("Subscribe_intf_lag_state_xfmr", Subscribe_intf_lag_state_xfmr) + XlateFuncBind("DbToYangPath_intf_lag_state_path_xfmr", DbToYangPath_intf_lag_state_path_xfmr) +} + +const ( + PORTCHANNEL_TABLE = "PORTCHANNEL" + DEFAULT_PORTCHANNEL_MIN_LINKS = "1" + DEFAULT_PORTCHANNEL_SPEED = "0" +) + +/* Validate whether LAG exists in DB */ +func validatePortChannel(d *db.DB, lagName string) error { + + intfType, _, ierr := getIntfTypeByName(lagName) + if ierr != nil || intfType != IntfTypePortChannel { + return tlerr.InvalidArgsError{Format: "Invalid PortChannel: " + lagName} + } + + err := validateIntfExists(d, PORTCHANNEL_TABLE, lagName) + if err != nil { + errStr := "PortChannel: " + lagName + " does not exist" + return tlerr.InvalidArgsError{Format: errStr} + } + + return nil +} + +func uint16Conv(sval string) (uint16, error) { + v, err := strconv.ParseUint(sval, 10, 16) + if err != nil { + errStr := "Conversion of string: " + "sval" + " to int failed" + if log.V(3) { + log.Error(errStr) + } + return 0, errors.New(errStr) + } + return uint16(v), nil +} + +func deleteLagIntfAndMembers(inParams *XfmrParams, lagName *string) error { + log.Info("Inside deleteLagIntfAndMembers") + var err error + + subOpMap := make(map[db.DBNum]map[string]map[string]db.Value) + resMap := make(map[string]map[string]db.Value) + lagMap := make(map[string]db.Value) + lagMemberMap := make(map[string]db.Value) + lagIntfMap := make(map[string]db.Value) + lagMap[*lagName] = db.Value{Field: map[string]string{}} + + intTbl := IntfTypeTblMap[IntfTypePortChannel] + subOpMap[db.ConfigDB] = resMap + inParams.subOpDataMap[DELETE] = &subOpMap + /* Validate given PortChannel exists */ + intfType, _, ierr := getIntfTypeByName(*lagName) + if ierr != nil || intfType != IntfTypePortChannel { + return tlerr.InvalidArgsError{Format: "Invalid PortChannel: " + *lagName} + } + + entry, err := inParams.d.GetEntry(&db.TableSpec{Name: PORTCHANNEL_TABLE}, db.Key{Comp: []string{*lagName}}) + if err != 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 + } + + /* Validate L3 Configuration only operation is not Delete */ + if inParams.oper != DELETE { + err = validateL3ConfigExists(inParams.d, lagName) + if err != nil { + return err + } + } + + /* Handle PORTCHANNEL_MEMBER TABLE */ + var flag bool = false + ts := db.TableSpec{Name: intTbl.cfgDb.memberTN + inParams.d.Opts.KeySeparator + *lagName} + lagKeys, err := inParams.d.GetKeys(&ts) + if err == nil { + for key := range lagKeys { + flag = true + log.Info("Member port", lagKeys[key].Get(1)) + memberKey := *lagName + "|" + lagKeys[key].Get(1) + lagMemberMap[memberKey] = db.Value{Field: map[string]string{}} + } + if flag { + resMap["PORTCHANNEL_MEMBER"] = lagMemberMap + } + } + + /* Handle PORTCHANNEL_INTERFACE TABLE */ + processIntfTableRemoval(inParams.d, *lagName, PORTCHANNEL_INTERFACE_TN, lagIntfMap) + if len(lagIntfMap) != 0 { + resMap[PORTCHANNEL_INTERFACE_TN] = lagIntfMap + } + + /* Handle PORTCHANNEL TABLE */ + resMap["PORTCHANNEL"] = lagMap + subOpMap[db.ConfigDB] = resMap + log.Info("subOpMap: ", subOpMap) + inParams.subOpDataMap[DELETE] = &subOpMap + return nil +} + +func getLagStateAttr(attr *string, ifName *string, lagInfoMap map[string]db.Value, + oc_val *ocbinds.OpenconfigInterfaces_Interfaces_Interface_Aggregation_State) error { + lagEntries, ok := lagInfoMap[*ifName] + if !ok { + errStr := "Cannot find info for Interface: " + *ifName + return errors.New(errStr) + } + switch *attr { + case "min-links": + links, _ := strconv.Atoi(lagEntries.Field["min-links"]) + minlinks := uint16(links) + oc_val.MinLinks = &minlinks + } + return nil +} + +func getLagState(inParams XfmrParams, d *db.DB, ifName *string, lagInfoMap map[string]db.Value, + oc_val *ocbinds.OpenconfigInterfaces_Interfaces_Interface_Aggregation_State) error { + log.V(3).Info("getLagState() called") + lagEntries, ok := lagInfoMap[*ifName] + if !ok { + errStr := "Cannot find info for Interface: " + *ifName + return errors.New(errStr) + } + links, _ := strconv.Atoi(lagEntries.Field["min-links"]) + minlinks := uint16(links) + oc_val.MinLinks = &minlinks + + return nil +} + +/* Get PortChannel Info */ +func fillLagInfoForIntf(inParams XfmrParams, d *db.DB, ifName *string, lagInfoMap map[string]db.Value, targetUri *string) error { + var err error + var lagMemKeys []db.Key + intTbl := IntfTypeTblMap[IntfTypePortChannel] + /* Get members list */ + ts := db.TableSpec{Name: PORTCHANNEL_MEMBER_TN + d.Opts.KeySeparator + *ifName} + lagMemKeys, err = d.GetKeys(&ts) + if err != nil { + return err + } + log.Info("lag-member-table keys", lagMemKeys) + + var lagMembers []string + var memberPortsStr strings.Builder + for i := range lagMemKeys { + ethName := lagMemKeys[i].Get(1) + lagMembers = append(lagMembers, ethName) + memberPortsStr.WriteString(ethName + ",") + } + lagInfoMap[*ifName] = db.Value{Field: make(map[string]string)} + + /* Get MinLinks value */ + curr, err := d.GetEntry(&db.TableSpec{Name: intTbl.cfgDb.portTN}, db.Key{Comp: []string{*ifName}}) + if err != nil { + errStr := "Failed to Get PortChannel details" + return errors.New(errStr) + } + var links int + if val, ok := curr.Field["min_links"]; ok { + min_links, err := strconv.Atoi(val) + if err != nil { + errStr := "Conversion of string to int failed" + return errors.New(errStr) + } + links = min_links + } else { + log.V(3).Info("Minlinks set to 1 (dafault value)") + min_links, err := strconv.Atoi(DEFAULT_PORTCHANNEL_MIN_LINKS) + if err != nil { + errStr := "Conversion of string to int failed" + return errors.New(errStr) + } + links = min_links + } + lagInfoMap[*ifName].Field["min-links"] = strconv.Itoa(links) + + log.Infof("Updated the lag-info-map for Interface: %s", *ifName) + + return err +} + +// YangToDb_lag_min_links_xfmr is a Yang to DB translation overloaded method for handle min-links config +var YangToDb_lag_min_links_xfmr FieldXfmrYangToDb = func(inParams XfmrParams) (map[string]string, error) { + if log.V(3) { + log.Info("Entering YangToDb_lag_min_links_xfmr") + } + res_map := make(map[string]string) + var err error + + pathInfo := NewPathInfo(inParams.uri) + ifKey := pathInfo.Var("name") + + log.Infof("Received Min links config for path: %s; template: %s vars: %v ifKey: %s", pathInfo.Path, pathInfo.YangPath, pathInfo.Vars, ifKey) + + if inParams.param == nil { + if log.V(3) { + log.Info("YangToDb_lag_min_links_xfmr Error: No Params") + } + return res_map, err + } + + minLinks, _ := inParams.param.(*uint16) + + if int(*minLinks) > 32 || int(*minLinks) < 0 { + errStr := "Min links value is invalid for the PortChannel: " + ifKey + log.Info(errStr) + err = tlerr.InvalidArgsError{Format: errStr} + return res_map, err + } + + res_map["min_links"] = strconv.Itoa(int(*minLinks)) + return res_map, nil +} + +var DbToYang_lag_min_links_xfmr FieldXfmrDbtoYang = func(inParams XfmrParams) (map[string]interface{}, error) { + if log.V(3) { + log.Info("Entering DbToYang_lag_min_links_xfmr") + } + var err error + result := make(map[string]interface{}) + + err = validatePortChannel(inParams.d, inParams.key) + if err != nil { + log.Infof("DbToYang_lag_min_links_xfmr Error: %v ", err) + return result, err + } + data := (*inParams.dbDataMap)[inParams.curDb] + links, ok := data[PORTCHANNEL_TABLE][inParams.key].Field["min_links"] + if ok { + linksUint16, err := uint16Conv(links) + if err != nil { + return result, err + } + result["min-links"] = linksUint16 + } else { + if log.V(3) { + log.Info("min-links set to 1 (default value)") + } + linksUint16, err := uint16Conv(DEFAULT_PORTCHANNEL_MIN_LINKS) + if err != nil { + return result, err + } + result["min-links"] = linksUint16 + } + + return result, err +} + +// DbToYang_intf_lag_state_xfmr is a DB to Yang translation overloaded method for PortChannel GET operation +var DbToYang_intf_lag_state_xfmr SubTreeXfmrDbToYang = func(inParams XfmrParams) error { + var err error + + intfsObj := getIntfsRoot(inParams.ygRoot) + if intfsObj == nil || intfsObj.Interface == nil { + errStr := "Failed to Get root object!" + log.Errorf(errStr) + return errors.New(errStr) + } + pathInfo := NewPathInfo(inParams.uri) + ifName := pathInfo.Var("name") + if _, ok := intfsObj.Interface[ifName]; !ok { + obj, _ := intfsObj.NewInterface(ifName) + ygot.BuildEmptyTree(obj) + } + intfObj := intfsObj.Interface[ifName] + if intfObj.Aggregation == nil { + ygot.BuildEmptyTree(intfObj) + } + if intfObj.Aggregation.State == nil { + ygot.BuildEmptyTree(intfObj.Aggregation) + } + intfType, _, err := getIntfTypeByName(ifName) + if intfType != IntfTypePortChannel || err != nil { + intfTypeStr := strconv.Itoa(int(intfType)) + errStr := "TableXfmrFunc - Invalid interface type: " + intfTypeStr + log.Warning(errStr) + return errors.New(errStr) + } + /*Validate given PortChannel exists */ + err = validatePortChannel(inParams.d, ifName) + if err != nil { + return err + } + + targetUriPath := pathInfo.YangPath + log.Info("targetUriPath is ", targetUriPath) + lagInfoMap := make(map[string]db.Value) + ocAggregationStateVal := intfObj.Aggregation.State + err = fillLagInfoForIntf(inParams, inParams.d, &ifName, lagInfoMap, &targetUriPath) + if err != nil { + log.Errorf("Failed to get info: %s failed!", ifName) + return err + } + log.Info("Succesfully completed DB map population!", lagInfoMap) + switch targetUriPath { + case "/openconfig-interfaces:interfaces/interface/openconfig-if-aggregate:aggregation/state/min-links": + log.Info("Get is for min-links") + attr := "min-links" + err = getLagStateAttr(&attr, &ifName, lagInfoMap, ocAggregationStateVal) + if err != nil { + return err + } + case "/openconfig-interfaces:interfaces/interface/openconfig-if-aggregate:aggregation/state": + log.Info("Get is for State Container!") + err = getLagState(inParams, inParams.d, &ifName, lagInfoMap, ocAggregationStateVal) + if err != nil { + return err + } + default: + log.Infof(targetUriPath + " - Not an supported Get attribute") + } + return err +} + +func updateMemberPortsMtu(inParams *XfmrParams, lagName *string, mtuValStr *string) error { + log.Info("Inside updateLagIntfAndMembersMtu") + var err error + resMap := make(map[string]string) + intPortChannelTbl := IntfTypeTblMap[IntfTypePortChannel] + + /* Validate given PortChannel exits */ + err = validatePortChannel(inParams.d, *lagName) + if err != nil { + return err + } + ts := db.TableSpec{Name: intPortChannelTbl.cfgDb.memberTN + inParams.d.Opts.KeySeparator + *lagName} + lagKeys, err := inParams.d.GetKeys(&ts) + if err == nil { + subOpMap := make(map[db.DBNum]map[string]map[string]db.Value) + intfMap := make(map[string]map[string]db.Value) + intTbl := IntfTypeTblMap[IntfTypeEthernet] + resMap["mtu"] = *mtuValStr + intfMap[intTbl.cfgDb.portTN] = make(map[string]db.Value) + + for key := range lagKeys { + portName := lagKeys[key].Get(1) + intfMap[intTbl.cfgDb.portTN][portName] = db.Value{Field: resMap} + log.Info("Member port ", portName, " updated with mtu ", *mtuValStr) + } + + subOpMap[db.ConfigDB] = intfMap + inParams.subOpDataMap[UPDATE] = &subOpMap + } + return err +} + +var Subscribe_intf_lag_state_xfmr SubTreeXfmrSubscribe = func(inParams XfmrSubscInParams) (XfmrSubscOutParams, error) { + var err error + var result XfmrSubscOutParams + + if inParams.subscProc == TRANSLATE_SUBSCRIBE { + + log.Info("Subscribe_intf_lag_state_xfmr: inParams.subscProc: ", inParams.subscProc) + + pathInfo := NewPathInfo(inParams.uri) + targetUriPath := pathInfo.YangPath + + log.Infof("Subscribe_intf_lag_state_xfmr:- URI:%s pathinfo:%s ", inParams.uri, pathInfo.Path) + log.Infof("Subscribe_intf_lag_state_xfmr:- Target URI path:%s", targetUriPath) + + result.nOpts = new(notificationOpts) + result.nOpts.pType = OnChange + result.nOpts.mInterval = 15 + result.isVirtualTbl = false + result.needCache = true + + ifName := pathInfo.Var("name") + log.Info("Subscribe_intf_lag_state_xfmr: ifName: ", ifName) + + // for PORTCHANNEL_MEMBER table + po_mem_key := "*" + "|" + "*" + + if ifName == "" { + ifName = "*" + } else if ifName != "*" { + po_mem_key = ifName + "|" + "*" + } + + result.secDbDataMap = RedisDbYgNodeMap{db.ConfigDB: { + "PORTCHANNEL_MEMBER": {po_mem_key: DBKeyYgNodeInfo{}}, + "PORTCHANNEL": {ifName: map[string]string{"min_links": "min-links"}}}} + log.Info("Subscribe_intf_lag_state_xfmr: result ", result) + } + + return result, err +} + +var DbToYangPath_intf_lag_state_path_xfmr PathXfmrDbToYangFunc = func(params XfmrDbToYgPathParams) error { + intfRoot := "/openconfig-interfaces:interfaces/interface" + + if (params.tblName != "PORTCHANNEL") && + (params.tblName != "PORTCHANNEL_MEMBER") { + log.Info("DbToYangPath_intf_lag_state_path_xfmr: from wrong table ", params.tblName) + return nil + } + + if (params.tblName == "PORTCHANNEL") && (len(params.tblKeyComp) > 0) { + params.ygPathKeys[intfRoot+"/name"] = params.tblKeyComp[0] + } else if (params.tblName == "PORTCHANNEL_MEMBER") && (len(params.tblKeyComp) > 1) { + params.ygPathKeys[intfRoot+"/name"] = params.tblKeyComp[0] + } else { + log.Info("DbToYangPath_intf_lag_state_path_xfmr, wrong param: tbl ", params.tblName, " key ", params.tblKeyComp) + return nil + } + + log.Info("DbToYangPath_intf_lag_state_path_xfmr: params.ygPathkeys: ", params.ygPathKeys) + + return nil +} diff --git a/translib/transformer/xfmr_intf.go b/translib/transformer/xfmr_intf.go index b35f768bb..f18de7a26 100644 --- a/translib/transformer/xfmr_intf.go +++ b/translib/transformer/xfmr_intf.go @@ -45,6 +45,7 @@ func init() { 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_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) XlateFuncBind("Subscribe_intf_eth_port_config_xfmr", Subscribe_intf_eth_port_config_xfmr) @@ -86,13 +87,17 @@ const ( PORT_ADMIN_STATUS = "admin_status" PORT_SPEED = "speed" PORT_AUTONEG = "autoneg" - DEFAULT_MTU = "9100" + + PORTCHANNEL_INTERFACE_TN = "PORTCHANNEL_INTERFACE" + PORTCHANNEL_MEMBER_TN = "PORTCHANNEL_MEMBER" + DEFAULT_MTU = "9100" ) const ( - PIPE = "|" - COLON = ":" - ETHERNET = "Eth" + PIPE = "|" + COLON = ":" + ETHERNET = "Eth" + PORTCHANNEL = "PortChannel" ) type TblData struct { @@ -124,12 +129,17 @@ var IntfTypeTblMap = map[E_InterfaceType]IntfTblData{ stateDb: TblData{portTN: "PORT_TABLE", intfTN: "INTERFACE_TABLE", keySep: PIPE}, CountersHdl: CounterData{OIDTN: "COUNTERS_PORT_NAME_MAP", CountersTN: "COUNTERS", PopulateCounters: populatePortCounters}, }, + IntfTypePortChannel: IntfTblData{ + cfgDb: TblData{portTN: "PORTCHANNEL", intfTN: "PORTCHANNEL_INTERFACE", memberTN: "PORTCHANNEL_MEMBER", keySep: PIPE}, + appDb: TblData{portTN: "LAG_TABLE", intfTN: "INTF_TABLE", keySep: COLON, memberTN: "LAG_MEMBER_TABLE"}, + stateDb: TblData{portTN: "LAG_TABLE", intfTN: "INTERFACE_TABLE", keySep: PIPE}, + }, } var dbIdToTblMap = map[db.DBNum][]string{ - db.ConfigDB: {"PORT"}, - db.ApplDB: {"PORT_TABLE"}, - db.StateDB: {"PORT_TABLE"}, + db.ConfigDB: {"PORT", "PORTCHANNEL"}, + db.ApplDB: {"PORT_TABLE", "LAG_TABLE"}, + db.StateDB: {"PORT_TABLE", "LAG_TABLE"}, } var intfOCToSpeedMap = map[ocbinds.E_OpenconfigIfEthernet_ETHERNET_SPEED]string{ @@ -151,8 +161,9 @@ var intfOCToSpeedMap = map[ocbinds.E_OpenconfigIfEthernet_ETHERNET_SPEED]string{ type E_InterfaceType int64 const ( - IntfTypeUnset E_InterfaceType = 0 - IntfTypeEthernet E_InterfaceType = 1 + IntfTypeUnset E_InterfaceType = 0 + IntfTypeEthernet E_InterfaceType = 1 + IntfTypePortChannel E_InterfaceType = 2 ) type E_InterfaceSubType int64 @@ -166,6 +177,8 @@ func getIntfTypeByName(name string) (E_InterfaceType, E_InterfaceSubType, error) var err error if strings.HasPrefix(name, ETHERNET) { return IntfTypeEthernet, IntfSubTypeUnset, err + } else if strings.HasPrefix(name, PORTCHANNEL) { + return IntfTypePortChannel, IntfSubTypeUnset, err } else { err = errors.New("Interface name prefix not matched with supported types") return IntfTypeUnset, IntfSubTypeUnset, err @@ -197,8 +210,20 @@ func performIfNameKeyXfmrOp(inParams *XfmrParams, requestUriPath *string, ifName var err error switch inParams.oper { case DELETE: + if strings.HasPrefix(*requestUriPath, "/openconfig-interfaces:interfaces/interface/subinterfaces/subinterface") && subintfid != 0 { + errStr := "Subinterfaces not supported" + log.Error(errStr) + return tlerr.NotSupported(errStr) + } + if *requestUriPath == "/openconfig-interfaces:interfaces/interface" { switch ifType { + case IntfTypePortChannel: + err := deleteLagIntfAndMembers(inParams, ifName) + if err != nil { + log.Errorf("Deleting LAG: %s failed! Err:%v", *ifName, err) + return tlerr.InvalidArgsError{Format: err.Error()} + } case IntfTypeEthernet: err = validateIntfExists(inParams.d, IntfTypeTblMap[IntfTypeEthernet].cfgDb.portTN, *ifName) if err != nil { @@ -234,10 +259,44 @@ func performIfNameKeyXfmrOp(inParams *XfmrParams, requestUriPath *string, ifName } } } + if ifType == IntfTypePortChannel { + if (inParams.oper == UPDATE) || (inParams.oper == REPLACE) { + err = validateIntfExists(inParams.d, IntfTypeTblMap[IntfTypePortChannel].cfgDb.portTN, *ifName) + if err != nil { //No Matching PortChannel to UPDATE/REPLACE + errStr := "PortChannel: " + *ifName + " does not exist" + return tlerr.InvalidArgsError{Format: errStr} + } + } + } } return err } +/* Validate interface in L3 mode, if true return error */ +func validateL3ConfigExists(d *db.DB, ifName *string) error { + intfType, _, ierr := getIntfTypeByName(*ifName) + if intfType == IntfTypeUnset || ierr != nil { + return errors.New("Invalid interface type IntfTypeUnset") + } + intTbl := IntfTypeTblMap[intfType] + IntfMapObj, err := d.GetEntry(&db.TableSpec{Name: intTbl.cfgDb.intfTN}, db.Key{Comp: []string{*ifName}}) + if err == nil && IntfMapObj.IsPopulated() { + errStr := "L3 Configuration exists for Interface: " + *ifName + + // L3 config exists if interface in interface table + return tlerr.InvalidArgsError{Format: errStr} + } + return nil +} + +func processIntfTableRemoval(d *db.DB, ifName string, tblName string, intfMap map[string]db.Value) { + intfKey, _ := d.GetKeysByPattern(&db.TableSpec{Name: tblName}, "*"+ifName) + if len(intfKey) != 0 { + key := ifName + intfMap[key] = db.Value{Field: map[string]string{}} + } +} + /* Validate whether intf exists in DB */ func validateIntfExists(d *db.DB, intfTs string, ifName string) error { if len(ifName) == 0 { @@ -351,7 +410,10 @@ var intf_table_xfmr TableXfmrFunc = func(inParams XfmrParams) ([]string, error) strings.HasPrefix(targetUriPath, "/openconfig-interfaces:interfaces/interface/openconfig-if-ethernet:ethernet") { //Checking interface type at container level, if not Ethernet type return nil return nil, nil - + } else if intfType != IntfTypePortChannel && + strings.HasPrefix(targetUriPath, "/openconfig-interfaces:interfaces/interface/openconfig-if-aggregate:aggregation") { + //Checking interface type at container level, if not PortChannel type return nil + return nil, nil } else if strings.HasPrefix(targetUriPath, "/openconfig-interfaces:interfaces/interface/state/counters") { tblList = append(tblList, "NONE") } else if strings.HasPrefix(targetUriPath, "/openconfig-interfaces:interfaces/interface/state") || @@ -608,6 +670,19 @@ var YangToDb_intf_mtu_xfmr FieldXfmrYangToDb = func(inParams XfmrParams) (map[st intfTypeVal, _ := inParams.param.(*uint16) intTypeValStr := strconv.FormatUint(uint64(*intfTypeVal), 10) + if IntfTypePortChannel == intfType { + /* Apply the MTU to all the portchannel member ports */ + updateMemberPortsMtu(&inParams, &ifName, &intTypeValStr) + } else if IntfTypeEthernet == intfType { + /* Do not allow MTU configuration on a portchannel member port */ + lagId, _ := retrievePortChannelAssociatedWithIntf(&inParams, &ifName) + if lagId != nil { + log.Infof("%s is member of %s", ifName, *lagId) + errStr := "Configuration not allowed when port is member of Portchannel." + return nil, tlerr.InvalidArgsError{Format: errStr} + } + } + res_map["mtu"] = intTypeValStr return res_map, nil } @@ -649,9 +724,10 @@ var DbToYang_intf_mtu_xfmr FieldXfmrDbtoYang = func(inParams XfmrParams) (map[st return result, err } -// YangToDb_intf_eth_port_config_xfmr handles port-speed, and auto-neg config. +// YangToDb_intf_eth_port_config_xfmr handles port-speed, auto-neg, and aggregate-id config. var YangToDb_intf_eth_port_config_xfmr SubTreeXfmrYangToDb = func(inParams XfmrParams) (map[string]map[string]db.Value, error) { var err error + var lagStr string memMap := make(map[string]map[string]db.Value) pathInfo := NewPathInfo(inParams.uri) @@ -682,10 +758,87 @@ var YangToDb_intf_eth_port_config_xfmr SubTreeXfmrYangToDb = func(inParams XfmrP intfObj.Ethernet.Config == nil || //case 3 (intfObj.Ethernet.Config != nil && requestUriPath == "/openconfig-interfaces:interfaces/interface/openconfig-if-ethernet:ethernet/config") { + + // Delete all the Vlans for Interface and member port removal from port-channel + lagId, err := retrievePortChannelAssociatedWithIntf(&inParams, &ifName) + if lagId != nil { + log.Infof("%s is member of %s", ifName, *lagId) + } + if err != nil { + errStr := "Retrieveing PortChannel associated with Interface: " + ifName + " failed!" + return nil, errors.New(errStr) + } + if lagId != nil { + lagStr = *lagId + intTbl := IntfTypeTblMap[IntfTypePortChannel] + tblName, _ := getMemTableNameByDBId(intTbl, inParams.curDb) + + m := make(map[string]string) + value := db.Value{Field: m} + m["NULL"] = "NULL" + intfKey := lagStr + "|" + ifName + if _, ok := memMap[tblName]; !ok { + memMap[tblName] = make(map[string]db.Value) + } + memMap[tblName][intfKey] = value + } return memMap, err } } + /* Handle AggregateId config */ + if intfObj.Ethernet.Config.AggregateId != nil { + if !strings.HasPrefix(ifName, ETHERNET) { + return nil, errors.New("Invalid config request") + } + intTbl := IntfTypeTblMap[IntfTypePortChannel] + tblName, _ := getMemTableNameByDBId(intTbl, inParams.curDb) + + switch inParams.oper { + case CREATE: + fallthrough + case REPLACE: + fallthrough + case UPDATE: + log.Info("Add member port") + lagId := intfObj.Ethernet.Config.AggregateId + lagStr = *lagId + + intfType, _, err := getIntfTypeByName(ifName) + if intfType != IntfTypeEthernet || err != nil { + intfTypeStr := strconv.Itoa(int(intfType)) + errStr := "Invalid interface type" + intfTypeStr + log.Error(errStr) + return nil, tlerr.InvalidArgsError{Format: errStr} + } + /* Check if PortChannel exists */ + err = validateIntfExists(inParams.d, intTbl.cfgDb.portTN, lagStr) + if err != nil { + return nil, err + } + + case DELETE: + lagId, err := retrievePortChannelAssociatedWithIntf(&inParams, &ifName) + if lagId != nil { + log.Infof("%s is member of %s", ifName, *lagId) + } + if lagId == nil || err != nil { + return nil, nil + } + lagStr = *lagId + } /* End of switch case */ + if len(lagStr) != 0 { + m := make(map[string]string) + value := db.Value{Field: m} + m["NULL"] = "NULL" + intfKey := lagStr + "|" + ifName + if _, ok := memMap[tblName]; !ok { + memMap[tblName] = make(map[string]db.Value) + } + memMap[tblName][intfKey] = value + } + } + /* Handle PortSpeed config */ if intfObj.Ethernet.Config.PortSpeed != 0 { res_map := make(map[string]string) @@ -793,6 +946,26 @@ var DbToYang_intf_eth_port_config_xfmr SubTreeXfmrDbToYang = func(inParams XfmrP } var errStr string + if get_cfg_obj || targetUriPath == "/openconfig-interfaces:interfaces/interface/openconfig-if-ethernet:ethernet/config/openconfig-if-aggregate:aggregate-id" { + is_id_populated := false + intf_lagId, _ := retrievePortChannelAssociatedWithIntf(&inParams, &ifName) + if intf_lagId != nil { + if strings.HasPrefix(*intf_lagId, "PortChannel") { + intfObj.Ethernet.Config.AggregateId = intf_lagId + is_id_populated = true + } + } + if !is_id_populated { + errStr = "aggregate-id not set" + } + + // subscribe for aggregate-id needs "Resource not found" for delete notification + if (targetUriPath == "/openconfig-interfaces:interfaces/interface/openconfig-if-ethernet:ethernet/config/openconfig-if-aggregate:aggregate-id") && (!is_id_populated) { + err = tlerr.NotFoundError{Format: "Resource not found"} + return err + } + } + if entry.IsPopulated() { if get_cfg_obj || targetUriPath == "/openconfig-interfaces:interfaces/interface/openconfig-if-ethernet:ethernet/config/auto-negotiate" { autoNeg, ok := entry.Field[PORT_AUTONEG] @@ -943,6 +1116,35 @@ var DbToYang_intf_eth_port_speed_xfmr FieldXfmrDbtoYang = func(inParams XfmrPara return result, err } +var DbToYang_intf_eth_aggr_id_xfmr = func(inParams XfmrParams) (map[string]interface{}, error) { + var err error + result := make(map[string]interface{}) + + log.Info("DbToYang_intf_eth_aggr_id_xfmr, interface name ", inParams.key) + + intfType, _, ierr := getIntfTypeByName(inParams.key) + if intfType == IntfTypeUnset || ierr != nil { + log.Info("DbToYang_intf_eth_aggr_id_xfmr - Invalid interface type IntfTypeUnset") + return result, errors.New("Invalid interface type IntfTypeUnset") + } + + if IntfTypeEthernet != intfType { + return result, nil + } + + intf_lagId, _ := retrievePortChannelAssociatedWithIntf(&inParams, &inParams.key) + if intf_lagId != nil { + lagPrefix := "PortChannel" + if strings.HasPrefix(*intf_lagId, lagPrefix) { + result["aggregate-id"] = intf_lagId + } + } + + log.Infof("DbToYang_intf_eth_aggr_id_xfmr result %v", result) + + return result, err +} + func getIntfCountersTblKey(d *db.DB, ifKey string) (string, error) { var oid string @@ -1945,7 +2147,7 @@ var DbToYang_intf_ip_addr_xfmr SubTreeXfmrDbToYang = func(inParams XfmrParams) e return nil } - intfTypeList := [1]E_InterfaceType{IntfTypeEthernet} + intfTypeList := [2]E_InterfaceType{IntfTypeEthernet, IntfTypePortChannel} // Get IP from all configDb table interfaces for i := 0; i < len(intfTypeList); i++ { @@ -2642,7 +2844,8 @@ var Subscribe_intf_ip_addr_xfmr = func(inParams XfmrSubscInParams) (XfmrSubscOut if tableName != "" { result.dbDataMap = RedisDbSubscribeMap{db.ConfigDB: {tableName: {keyName: {}}}} } else { - result.dbDataMap = RedisDbSubscribeMap{db.ConfigDB: {"INTERFACE": {keyName: {}}}} + result.dbDataMap = RedisDbSubscribeMap{db.ConfigDB: {"INTERFACE": {keyName: {}}, + "PORTCHANNEL_INTERFACE": {keyName: {}}}} } } else if targetUriPath == addressStatePath { keyName = ifKey + ":" + ipKey @@ -2697,7 +2900,8 @@ var DbToYangPath_intf_ip_path_xfmr PathXfmrDbToYangFunc = func(params XfmrDbToYg params.ygPathKeys[ifRoot+"/name"] = ifParts[0] - if params.tblName == "INTERFACE" || params.tblName == "INTF_TABLE" { + if params.tblName == "INTERFACE" || params.tblName == "INTF_TABLE" || + params.tblName == "PORTCHANNEL_INTERFACE" { addrPath := "/openconfig-if-ip:ipv4/addresses/address/ip" @@ -2892,3 +3096,52 @@ var DbToYang_ipv6_enabled_xfmr FieldXfmrDbtoYang = func(inParams XfmrParams) (ma } return res_map, nil } + +func getMemTableNameByDBId(intftbl IntfTblData, curDb db.DBNum) (string, error) { + + var tblName string + + switch curDb { + case db.ConfigDB: + tblName = intftbl.cfgDb.memberTN + case db.ApplDB: + tblName = intftbl.appDb.memberTN + case db.StateDB: + tblName = intftbl.stateDb.memberTN + default: + tblName = intftbl.cfgDb.memberTN + } + + return tblName, nil +} + +func retrievePortChannelAssociatedWithIntf(inParams *XfmrParams, ifName *string) (*string, error) { + var err error + + if strings.HasPrefix(*ifName, ETHERNET) { + intTbl := IntfTypeTblMap[IntfTypePortChannel] + tblName, _ := getMemTableNameByDBId(intTbl, inParams.curDb) + var lagStr string + + lagKeys, err := inParams.d.GetKeysByPattern(&db.TableSpec{Name: tblName}, "*"+*ifName) + /* Find the port-channel the given ifname is part of */ + if err != nil { + return nil, err + } + var flag bool = false + for i := range lagKeys { + if *ifName == lagKeys[i].Get(1) { + flag = true + lagStr = lagKeys[i].Get(0) + log.Info("Given interface part of PortChannel: ", lagStr) + break + } + } + if !flag { + log.Info("Given Interface not part of any PortChannel") + return nil, err + } + return &lagStr, err + } + return nil, err +}