Skip to content

Commit

Permalink
AppInterface enhancements for subscription (sonic-net#67)
Browse files Browse the repository at this point in the history
* AppInterface enhancements for subscription

Added new translateSubscribe() and processSubscribe() functions to
appInterface as per HLD sonic-net/SONiC#1287

Also added stub implementation of these functions to all existing app
modules. It blindly treats all paths as non-db. Basic subscription
features, without on_change, can be verified with this mapping.

Signed-off-by: Sachin Holla <[email protected]>

* Update error types, test case styles, nil checks, etc

* Remove tlerr import alias in yanglib_app

* Non-pointer params in transleSubscribe and processSubscribe

* Reorg old & new version comprision logic in EntryCompare func

* appInterface method cleanup in acl_app

---------

Signed-off-by: Sachin Holla <[email protected]>
  • Loading branch information
sachinholla authored Apr 25, 2023
1 parent 22f82b4 commit 6ce18c6
Show file tree
Hide file tree
Showing 15 changed files with 836 additions and 273 deletions.
112 changes: 18 additions & 94 deletions translib/acl_app.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ package translib

import (
"bytes"
"errors"
"fmt"
"reflect"
"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"
Expand Down Expand Up @@ -127,109 +127,27 @@ func (app *AclApp) getAppRootObject() *ocbinds.OpenconfigAcl_Acl {
}

func (app *AclApp) translateCreate(d *db.DB) ([]db.WatchKeys, error) {
var err error
var keys []db.WatchKeys
log.Info("translateCreate:acl:path =", app.pathInfo.Template)

keys, err = app.translateCRUCommon(d, CREATE)
return keys, err
return app.translateCRUCommon(d, CREATE)
}

func (app *AclApp) translateUpdate(d *db.DB) ([]db.WatchKeys, error) {
var err error
var keys []db.WatchKeys
log.Info("translateUpdate:acl:path =", app.pathInfo.Template)

keys, err = app.translateCRUCommon(d, UPDATE)
return keys, err
return app.translateCRUCommon(d, UPDATE)
}

func (app *AclApp) translateReplace(d *db.DB) ([]db.WatchKeys, error) {
var err error
var keys []db.WatchKeys
log.Info("translateReplace:acl:path =", app.pathInfo.Template)

keys, err = app.translateCRUCommon(d, REPLACE)
return keys, err
return app.translateCRUCommon(d, REPLACE)
}

func (app *AclApp) translateDelete(d *db.DB) ([]db.WatchKeys, error) {
var err error
var keys []db.WatchKeys
log.Info("translateDelete:acl:path =", app.pathInfo.Template)

return keys, err
return nil, nil
}

func (app *AclApp) translateGet(dbs [db.MaxDB]*db.DB) error {
var err error
log.Info("translateGet:acl:path =", app.pathInfo.Template)
return err
return nil
}

func (app *AclApp) translateAction(dbs [db.MaxDB]*db.DB) error {
err := errors.New("Not supported")
return err
}

func (app *AclApp) translateSubscribe(dbs [db.MaxDB]*db.DB, path string) (*notificationOpts, *notificationInfo, error) {
pathInfo := NewPathInfo(path)
notifInfo := notificationInfo{dbno: db.ConfigDB}
notSupported := tlerr.NotSupportedError{
Format: "Subscribe not supported", Path: path}

if isSubtreeRequest(pathInfo.Template, "/openconfig-acl:acl/acl-sets") {
// Subscribing to top level ACL record is not supported. It requires listening
// to 2 tables (ACL and ACL_RULE); TransLib does not support it yet
if pathInfo.HasSuffix("/acl-sets") ||
pathInfo.HasSuffix("/acl-set") ||
pathInfo.HasSuffix("/acl-set{}{}") {
log.Errorf("Subscribe not supported for top level ACL %s", pathInfo.Template)
return nil, nil, notSupported
}

t, err := getAclTypeOCEnumFromName(pathInfo.Var("type"))
if err != nil {
return nil, nil, err
}

aclkey := getAclKeyStrFromOCKey(pathInfo.Var("name"), t)

if strings.Contains(pathInfo.Template, "/acl-entry{}") {
// Subscribe for one rule
rulekey := "RULE_" + pathInfo.Var("sequence-id")
notifInfo.table = db.TableSpec{Name: RULE_TABLE}
notifInfo.key = asKey(aclkey, rulekey)
notifInfo.needCache = !pathInfo.HasSuffix("/acl-entry{}")

} else if pathInfo.HasSuffix("/acl-entries") || pathInfo.HasSuffix("/acl-entry") {
// Subscribe for all rules of an ACL
notifInfo.table = db.TableSpec{Name: RULE_TABLE}
notifInfo.key = asKey(aclkey, "*")

} else {
// Subscibe for ACL fields only
notifInfo.table = db.TableSpec{Name: ACL_TABLE}
notifInfo.key = asKey(aclkey)
notifInfo.needCache = true
}

} else if isSubtreeRequest(pathInfo.Template, "/openconfig-acl:acl/interfaces") {
// Right now interface binding config is maintained within ACL
// table itself. Multiple ACLs can be bound to one intf; one
// inname can occur in multiple ACL entries. So we cannot map
// interface binding xpaths to specific ACL table entry keys.
// For now subscribe for full ACL table!!
notifInfo.table = db.TableSpec{Name: ACL_TABLE}
notifInfo.key = asKey("*")
notifInfo.needCache = true

} else {
log.Errorf("Unknown path %s", pathInfo.Template)
return nil, nil, notSupported
}

return nil, &notifInfo, nil
return tlerr.NotSupported("unsupported")
}

func (app *AclApp) processCreate(d *db.DB) (SetResponse, error) {
Expand Down Expand Up @@ -295,10 +213,7 @@ func (app *AclApp) processGet(dbs [db.MaxDB]*db.DB) (GetResponse, error) {
}

func (app *AclApp) processAction(dbs [db.MaxDB]*db.DB) (ActionResponse, error) {
var resp ActionResponse
err := errors.New("Not implemented")

return resp, err
return ActionResponse{}, tlerr.New("not implemented")
}

func (app *AclApp) translateCRUCommon(d *db.DB, opcode int) ([]db.WatchKeys, error) {
Expand Down Expand Up @@ -1716,10 +1631,19 @@ func getAclKeyStrFromOCKey(aclname string, acltype ocbinds.E_OpenconfigAcl_ACL_T
return aclN + "_" + aclT
}

/* Check if targetUriPath is child (subtree) of nodePath
/*
Check if targetUriPath is child (subtree) of nodePath
The return value can be used to decide if subtrees needs
to visited to fill the data or not.
*/
func isSubtreeRequest(targetUriPath string, nodePath string) bool {
return strings.HasPrefix(targetUriPath, nodePath)
}

func (app *AclApp) translateSubscribe(req translateSubRequest) (translateSubResponse, error) {
return emptySubscribeResponse(req.path)
}

func (app *AclApp) processSubscribe(req processSubRequest) (processSubResponse, error) {
return processSubResponse{}, tlerr.New("not implemented")
}
9 changes: 7 additions & 2 deletions translib/api_tests_app.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
// //
////////////////////////////////////////////////////////////////////////////////

//go:build test
// +build test

package translib
Expand Down Expand Up @@ -101,8 +102,12 @@ func (app *apiTests) translateAction(dbs [db.MaxDB]*db.DB) error {
return nil
}

func (app *apiTests) translateSubscribe(dbs [db.MaxDB]*db.DB, path string) (*notificationOpts, *notificationInfo, error) {
return nil, nil, nil
func (app *apiTests) translateSubscribe(req translateSubRequest) (translateSubResponse, error) {
return emptySubscribeResponse(req.path)
}

func (app *apiTests) processSubscribe(req processSubRequest) (processSubResponse, error) {
return processSubResponse{}, tlerr.New("not implemented")
}

func (app *apiTests) processCreate(d *db.DB) (SetResponse, error) {
Expand Down
48 changes: 25 additions & 23 deletions translib/app_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// //
// 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. //
// 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. //
// //
Expand All @@ -31,22 +31,23 @@ package translib

import (
"errors"
log "github.com/golang/glog"
"github.com/openconfig/ygot/ygot"
"reflect"
"strings"

"github.com/Azure/sonic-mgmt-common/translib/db"
log "github.com/golang/glog"
"github.com/openconfig/ygot/ygot"
)

//Structure containing app module information
// Structure containing app module information
type appInfo struct {
appType reflect.Type
ygotRootType reflect.Type
isNative bool
tablesToWatch []*db.TableSpec
}

//Structure containing the app data coming from translib infra
// Structure containing the app data coming from translib infra
type appData struct {
path string
payload []byte
Expand All @@ -59,23 +60,23 @@ type appData struct {
// These include RESTCONF query parameters like - depth, fields etc.
type appOptions struct {

// depth limits subtree levels in the response data.
// 0 indicates unlimited depth.
// Valid for GET API only.
depth uint
// depth limits subtree levels in the response data.
// 0 indicates unlimited depth.
// Valid for GET API only.
depth uint

// deleteEmptyEntry indicates if the db entry should be deleted upon
// deletion of last field. This is a non standard option.
deleteEmptyEntry bool
// deleteEmptyEntry indicates if the db entry should be deleted upon
// deletion of last field. This is a non standard option.
deleteEmptyEntry bool
}

//map containing the base path to app module info
// map containing the base path to app module info
var appMap map[string]*appInfo

//array containing all the supported models
// array containing all the supported models
var models []ModelData

//Interface for all App Modules
// Interface for all App Modules
type appInterface interface {
initialize(data appData)
translateCreate(d *db.DB) ([]db.WatchKeys, error)
Expand All @@ -84,16 +85,17 @@ type appInterface interface {
translateDelete(d *db.DB) ([]db.WatchKeys, error)
translateGet(dbs [db.MaxDB]*db.DB) error
translateAction(dbs [db.MaxDB]*db.DB) error
translateSubscribe(dbs [db.MaxDB]*db.DB, path string) (*notificationOpts, *notificationInfo, error)
translateSubscribe(req translateSubRequest) (translateSubResponse, error)
processCreate(d *db.DB) (SetResponse, error)
processUpdate(d *db.DB) (SetResponse, error)
processReplace(d *db.DB) (SetResponse, error)
processDelete(d *db.DB) (SetResponse, error)
processGet(dbs [db.MaxDB]*db.DB) (GetResponse, error)
processAction(dbs [db.MaxDB]*db.DB) (ActionResponse, error)
processSubscribe(req processSubRequest) (processSubResponse, error)
}

//App modules will use this function to register with App interface during boot up
// App modules will use this function to register with App interface during boot up
func register(path string, info *appInfo) error {
var err error
log.Info("Registering for path =", path)
Expand All @@ -114,7 +116,7 @@ func register(path string, info *appInfo) error {
return err
}

//Adds the model information to the supported models array
// Adds the model information to the supported models array
func addModel(model *ModelData) error {
var err error

Expand All @@ -124,7 +126,7 @@ func addModel(model *ModelData) error {
return err
}

//Translib infra will use this function get the app info for a given path
// Translib infra will use this function get the app info for a given path
func getAppModuleInfo(path string) (*appInfo, error) {
log.Info("getAppModule called for path =", path)

Expand All @@ -135,23 +137,23 @@ func getAppModuleInfo(path string) (*appInfo, error) {

log.Info("found the entry in the map for path =", pattern)

return app, nil
return app, nil
}

/* If no specific app registered fallback to default/common app */
log.Infof("No app module registered for path %s hence fallback to default/common app", path)
app := appMap["*"]

return app, nil
return app, nil
}

//Get all the supported models
// Get all the supported models
func getModels() []ModelData {

return models
}

//Creates a new app from the appType and returns it as an appInterface
// Creates a new app from the appType and returns it as an appInterface
func getAppInterface(appType reflect.Type) (appInterface, error) {
var err error
appInstance := reflect.New(appType)
Expand Down
57 changes: 5 additions & 52 deletions translib/common_app.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,59 +129,12 @@ func (app *CommonApp) translateGet(dbs [db.MaxDB]*db.DB) error {
return err
}

func (app *CommonApp) translateSubscribe(dbs [db.MaxDB]*db.DB, path string) (*notificationOpts, *notificationInfo, error) {
var err error
var subscDt transformer.XfmrTranslateSubscribeInfo
var notifInfo notificationInfo
var notifOpts notificationOpts
txCache := new(sync.Map)
err = tlerr.NotSupportedError{Format: "Subscribe not supported", Path: path}

log.Info("tranlateSubscribe:path", path)
subscDt, err = transformer.XlateTranslateSubscribe(path, dbs, txCache)
if subscDt.PType == transformer.OnChange {
notifOpts.pType = OnChange
} else {
notifOpts.pType = Sample
}
notifOpts.mInterval = subscDt.MinInterval
notifOpts.isOnChangeSupported = subscDt.OnChange
if err != nil {
log.Infof("returning: notificationOpts - %v, nil, error - %v", notifOpts, err)
return &notifOpts, nil, err
}
if subscDt.DbDataMap == nil {
log.Infof("DB data is nil so returning: notificationOpts - %v, nil, error - %v", notifOpts, err)
return &notifOpts, nil, err
} else {
for dbNo, dbDt := range(subscDt.DbDataMap) {
if (len(dbDt) == 0) { //ideally all tables for a given uri should be from same DB
continue
}
log.Infof("Adding to notifInfo, Db Data - %v for DB No - %v", dbDt, dbNo)
notifInfo.dbno = dbNo
// in future there will be, multi-table in a DB, support from translib, for now its just single table
for tblNm, tblDt := range(dbDt) {
notifInfo.table = db.TableSpec{Name:tblNm}
if (len(tblDt) == 1) {
for tblKy := range(tblDt) {
notifInfo.key = asKey(tblKy)
notifInfo.needCache = subscDt.NeedCache
}
} else {
if (len(tblDt) > 1) {
log.Warningf("More than one DB key found for subscription path - %v", path)
} else {
log.Warningf("No DB key found for subscription path - %v", path)
}
return &notifOpts, nil, err
}
func (app *CommonApp) translateSubscribe(req translateSubRequest) (translateSubResponse, error) {
return emptySubscribeResponse(req.path)
}

}
}
}
log.Infof("For path - %v, returning: notifOpts - %v, notifInfo - %v, error - nil", path, notifOpts, notifInfo)
return &notifOpts, &notifInfo, nil
func (app *CommonApp) processSubscribe(req processSubRequest) (processSubResponse, error) {
return processSubResponse{}, tlerr.New("not implemented")
}

func (app *CommonApp) translateAction(dbs [db.MaxDB]*db.DB) error {
Expand Down
Loading

0 comments on commit 6ce18c6

Please sign in to comment.