From 89d95a1e5458d4d91df369cb539182f735427fee Mon Sep 17 00:00:00 2001 From: anand-kumar-subramanian <51383315+anand-kumar-subramanian@users.noreply.github.com> Date: Fri, 26 Jun 2020 14:39:38 -0700 Subject: [PATCH] Translib phase I changes (#11) * Translib changes phase I * Translib changes phase I * Resolved the review comments * Returning nil for the error parameter --- translib/app_interface.go | 78 ++-- translib/subscribe.go | 5 +- translib/translib.go | 751 +++++++++++++++++++++----------------- translib/version.go | 39 ++ 4 files changed, 495 insertions(+), 378 deletions(-) create mode 100644 translib/version.go diff --git a/translib/app_interface.go b/translib/app_interface.go index 3874937fb28f..998a2cf1e03f 100644 --- a/translib/app_interface.go +++ b/translib/app_interface.go @@ -18,7 +18,7 @@ //////////////////////////////////////////////////////////////////////////////// /* -Package translib defines the interface for all the app modules +Package translib defines the interface for all the app modules It exposes register function for all the app modules to register @@ -31,19 +31,19 @@ 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 type appInfo struct { - appType reflect.Type - ygotRootType reflect.Type - isNative bool - tablesToWatch []*db.TableSpec + appType reflect.Type + ygotRootType reflect.Type + isNative bool + tablesToWatch []*db.TableSpec } //Structure containing the app data coming from translib infra @@ -52,6 +52,17 @@ type appData struct { payload []byte ygotRoot *ygot.GoStruct ygotTarget *interface{} + appOptions +} + +// appOptions holds additional options for appInterface APIs. +// 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 } //map containing the base path to app module info @@ -79,13 +90,13 @@ type appInterface interface { //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) + log.Info("Registering for path =", path) if appMap == nil { appMap = make(map[string]*appInfo) } - if _, ok := appMap[path]; ok == false { + if _, ok := appMap[path]; !ok { appMap[path] = info @@ -107,24 +118,8 @@ func addModel(model *ModelData) error { return err } -//App modules can use this function to unregister itself from the app interface -func unregister(path string) error { - var err error - log.Info("Unregister for path =", path) - - _, ok := appMap[path] - - if ok { - log.Info("deleting for path =", path) - delete(appMap, path) - } - - return err -} - //Translib infra will use this function get the app info for a given path func getAppModuleInfo(path string) (*appInfo, error) { - var err error log.Info("getAppModule called for path =", path) for pattern, app := range appMap { @@ -134,17 +129,14 @@ func getAppModuleInfo(path string) (*appInfo, error) { log.Info("found the entry in the map for path =", pattern) - return app, err + return app, nil } - errStr := "Unsupported path=" + path - - err = errors.New(errStr) - log.Error(errStr) - - var app *appInfo + /* 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, err + return app, nil } //Get all the supported models @@ -155,16 +147,16 @@ func getModels() []ModelData { //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) - app, ok := appInstance.Interface().(appInterface) - - if !ok { - err = errors.New("Invalid appType") - log.Fatal("Appmodule does not confirm to appInterface method conventions for appType=", appType) - } else { - log.Info("cast to appInterface worked", app) - } + var err error + appInstance := reflect.New(appType) + app, ok := appInstance.Interface().(appInterface) + + if !ok { + err = errors.New("Invalid appType") + log.Fatal("Appmodule does not confirm to appInterface method conventions for appType=", appType) + } else { + log.Info("cast to appInterface worked", app) + } return app, err } diff --git a/translib/subscribe.go b/translib/subscribe.go index 4759900939c0..12468711989c 100644 --- a/translib/subscribe.go +++ b/translib/subscribe.go @@ -199,7 +199,8 @@ func startSubscribe(sInfo *subscribeInfo, dbNotificationMap map[db.DBNum][]*noti stopMap[sInfo.stop] = sInfo for dbno, nInfoArr := range dbNotificationMap { - opt := getDBOptions(dbno) + isWriteDisabled := true + opt := getDBOptions(dbno, isWriteDisabled) err = startDBSubscribe(opt, nInfoArr, sInfo) if err != nil { @@ -238,7 +239,7 @@ func getJson (nInfo *notificationInfo) ([]byte, error) { path := nInfo.path appInfo := nInfo.appInfo - err := appInitialize(app, appInfo, path, nil, GET) + err := appInitialize(app, appInfo, path, nil, nil, GET) if err != nil { return payload, err diff --git a/translib/translib.go b/translib/translib.go index bc68b0234db2..5cf4719eeb7e 100644 --- a/translib/translib.go +++ b/translib/translib.go @@ -22,7 +22,7 @@ Package translib implements APIs like Create, Get, Subscribe etc. to be consumed by the north bound management server implementations -This package take care of translating the incoming requests to +This package takes care of translating the incoming requests to Redis ABNF format and persisting them in the Redis DB. @@ -34,114 +34,161 @@ This package can also talk to non-DB clients. package translib import ( - //"errors" - "sync" - "github.com/Azure/sonic-mgmt-common/translib/db" - "github.com/Workiva/go-datastructures/queue" - log "github.com/golang/glog" - "github.com/Azure/sonic-mgmt-common/translib/tlerr" + "sync" + "github.com/Azure/sonic-mgmt-common/translib/db" + "github.com/Azure/sonic-mgmt-common/translib/tlerr" + "github.com/Workiva/go-datastructures/queue" + log "github.com/golang/glog" ) //Write lock for all write operations to be synchronized var writeMutex = &sync.Mutex{} -//minimum global interval for subscribe in secs +//Interval value for interval based subscription needs to be within the min and max +//minimum global interval for interval based subscribe in secs var minSubsInterval = 20 -var maxSubsInterval = 60 +//minimum global interval for interval based subscribe in secs +var maxSubsInterval = 600 type ErrSource int -const( +const ( ProtoErr ErrSource = iota AppErr ) -type SetRequest struct{ - Path string - Payload []byte +type UserRoles struct { + Name string + Roles []string } -type SetResponse struct{ - ErrSrc ErrSource +type SetRequest struct { + Path string + Payload []byte + User UserRoles + AuthEnabled bool + ClientVersion Version } -type GetRequest struct{ - Path string +type SetResponse struct { + ErrSrc ErrSource + Err error } -type GetResponse struct{ - Payload []byte - ErrSrc ErrSource +type GetRequest struct { + Path string + User UserRoles + AuthEnabled bool + ClientVersion Version + + // Depth limits the depth of data subtree in the response + // payload. Default value 0 indicates there is no limit. + Depth uint +} + +type GetResponse struct { + Payload []byte + ErrSrc ErrSource +} + +type ActionRequest struct { + Path string + Payload []byte + User UserRoles + AuthEnabled bool + ClientVersion Version +} + +type ActionResponse struct { + Payload []byte + ErrSrc ErrSource } -type SubscribeResponse struct{ - Path string +type SubscribeRequest struct { + Paths []string + Q *queue.PriorityQueue + Stop chan struct{} + User UserRoles + AuthEnabled bool + ClientVersion Version +} + +type SubscribeResponse struct { + Path string Payload []byte - Timestamp int64 + Timestamp int64 SyncComplete bool IsTerminated bool } type NotificationType int -const( - Sample NotificationType = iota - OnChange +const ( + Sample NotificationType = iota + OnChange ) -type IsSubscribeResponse struct{ - Path string - IsOnChangeSupported bool - MinInterval int - Err error - PreferredType NotificationType +type IsSubscribeRequest struct { + Paths []string + User UserRoles + AuthEnabled bool + ClientVersion Version +} + +type IsSubscribeResponse struct { + Path string + IsOnChangeSupported bool + MinInterval int + Err error + PreferredType NotificationType } -type ModelData struct{ - Name string - Org string - Ver string +type ModelData struct { + Name string + Org string + Ver string } type notificationOpts struct { - mInterval int - pType NotificationType // for TARGET_DEFINED + isOnChangeSupported bool + mInterval int + pType NotificationType // for TARGET_DEFINED } //initializes logging and app modules func init() { - log.Flush() + log.Flush() } -//Creates entries in the redis DB pertaining to the path and payload -func Create(req SetRequest) (SetResponse, error){ +//Create - Creates entries in the redis DB pertaining to the path and payload +func Create(req SetRequest) (SetResponse, error) { var keys []db.WatchKeys var resp SetResponse + path := req.Path + payload := req.Payload - path := req.Path - payload := req.Payload - - log.Info("Create request received with path =", path) - log.Info("Create request received with payload =", string(payload)) + log.Info("Create request received with path =", path) + log.Info("Create request received with payload =", string(payload)) - app, appInfo, err := getAppModule(path) + app, appInfo, err := getAppModule(path, req.ClientVersion) if err != nil { resp.ErrSrc = ProtoErr - return resp, err + return resp, err } - err = appInitialize(app, appInfo, path, &payload, CREATE) + err = appInitialize(app, appInfo, path, &payload, nil, CREATE) - if err != nil { + if err != nil { resp.ErrSrc = AppErr return resp, err - } + } writeMutex.Lock() defer writeMutex.Unlock() - d, err := db.NewDB(getDBOptions(db.ConfigDB)) + isWriteDisabled := false + d, err := db.NewDB(getDBOptions(db.ConfigDB, isWriteDisabled)) if err != nil { resp.ErrSrc = ProtoErr @@ -150,355 +197,364 @@ func Create(req SetRequest) (SetResponse, error){ defer d.DeleteDB() - keys, err = (*app).translateCreate(d) + keys, err = (*app).translateCreate(d) if err != nil { resp.ErrSrc = AppErr - return resp, err + return resp, err } err = d.StartTx(keys, appInfo.tablesToWatch) if err != nil { resp.ErrSrc = AppErr - return resp, err + return resp, err } - resp, err = (*app).processCreate (d) + resp, err = (*app).processCreate(d) - if err != nil { + if err != nil { d.AbortTx() resp.ErrSrc = AppErr - return resp, err - } + return resp, err + } err = d.CommitTx() - if err != nil { - resp.ErrSrc = AppErr - } + if err != nil { + resp.ErrSrc = AppErr + } - return resp, err + return resp, err } -//Updates entries in the redis DB pertaining to the path and payload -func Update(req SetRequest) (SetResponse, error){ - var keys []db.WatchKeys +//Update - Updates entries in the redis DB pertaining to the path and payload +func Update(req SetRequest) (SetResponse, error) { + var keys []db.WatchKeys var resp SetResponse + path := req.Path + payload := req.Payload - path := req.Path - payload := req.Payload - - log.Info("Update request received with path =", path) - log.Info("Update request received with payload =", string(payload)) + log.Info("Update request received with path =", path) + log.Info("Update request received with payload =", string(payload)) - app, appInfo, err := getAppModule(path) + app, appInfo, err := getAppModule(path, req.ClientVersion) - if err != nil { + if err != nil { resp.ErrSrc = ProtoErr - return resp, err - } + return resp, err + } - err = appInitialize(app, appInfo, path, &payload, UPDATE) + err = appInitialize(app, appInfo, path, &payload, nil, UPDATE) - if err != nil { - resp.ErrSrc = AppErr - return resp, err - } + if err != nil { + resp.ErrSrc = AppErr + return resp, err + } - writeMutex.Lock() + writeMutex.Lock() defer writeMutex.Unlock() - d, err := db.NewDB(getDBOptions(db.ConfigDB)) + isWriteDisabled := false + d, err := db.NewDB(getDBOptions(db.ConfigDB, isWriteDisabled)) - if err != nil { - resp.ErrSrc = ProtoErr - return resp, err - } + if err != nil { + resp.ErrSrc = ProtoErr + return resp, err + } defer d.DeleteDB() - keys, err = (*app).translateUpdate(d) + keys, err = (*app).translateUpdate(d) - if err != nil { + if err != nil { resp.ErrSrc = AppErr - return resp, err - } + return resp, err + } - err = d.StartTx(keys, appInfo.tablesToWatch) + err = d.StartTx(keys, appInfo.tablesToWatch) - if err != nil { + if err != nil { resp.ErrSrc = AppErr - return resp, err - } + return resp, err + } - resp, err = (*app).processUpdate (d) + resp, err = (*app).processUpdate(d) - if err != nil { - d.AbortTx() + if err != nil { + d.AbortTx() resp.ErrSrc = AppErr - return resp, err - } + return resp, err + } - err = d.CommitTx() + err = d.CommitTx() - if err != nil { - resp.ErrSrc = AppErr - } + if err != nil { + resp.ErrSrc = AppErr + } - return resp, err + return resp, err } -//Replaces entries in the redis DB pertaining to the path and payload -func Replace(req SetRequest) (SetResponse, error){ - var err error - var keys []db.WatchKeys +//Replace - Replaces entries in the redis DB pertaining to the path and payload +func Replace(req SetRequest) (SetResponse, error) { + var err error + var keys []db.WatchKeys var resp SetResponse + path := req.Path + payload := req.Payload - path := req.Path - payload := req.Payload - - log.Info("Replace request received with path =", path) - log.Info("Replace request received with payload =", string(payload)) + log.Info("Replace request received with path =", path) + log.Info("Replace request received with payload =", string(payload)) - app, appInfo, err := getAppModule(path) + app, appInfo, err := getAppModule(path, req.ClientVersion) - if err != nil { + if err != nil { resp.ErrSrc = ProtoErr - return resp, err - } + return resp, err + } - err = appInitialize(app, appInfo, path, &payload, REPLACE) + err = appInitialize(app, appInfo, path, &payload, nil, REPLACE) - if err != nil { - resp.ErrSrc = AppErr - return resp, err - } + if err != nil { + resp.ErrSrc = AppErr + return resp, err + } - writeMutex.Lock() + writeMutex.Lock() defer writeMutex.Unlock() - d, err := db.NewDB(getDBOptions(db.ConfigDB)) + isWriteDisabled := false + d, err := db.NewDB(getDBOptions(db.ConfigDB, isWriteDisabled)) - if err != nil { - resp.ErrSrc = ProtoErr - return resp, err - } + if err != nil { + resp.ErrSrc = ProtoErr + return resp, err + } defer d.DeleteDB() - keys, err = (*app).translateReplace(d) + keys, err = (*app).translateReplace(d) - if err != nil { + if err != nil { resp.ErrSrc = AppErr - return resp, err - } + return resp, err + } - err = d.StartTx(keys, appInfo.tablesToWatch) + err = d.StartTx(keys, appInfo.tablesToWatch) - if err != nil { + if err != nil { resp.ErrSrc = AppErr - return resp, err - } + return resp, err + } - resp, err = (*app).processReplace (d) + resp, err = (*app).processReplace(d) - if err != nil { - d.AbortTx() + if err != nil { + d.AbortTx() resp.ErrSrc = AppErr - return resp, err - } + return resp, err + } - err = d.CommitTx() + err = d.CommitTx() if err != nil { resp.ErrSrc = AppErr - } + } - return resp, err + return resp, err } -//Deletes entries in the redis DB pertaining to the path -func Delete(req SetRequest) (SetResponse, error){ - var err error - var keys []db.WatchKeys +//Delete - Deletes entries in the redis DB pertaining to the path +func Delete(req SetRequest) (SetResponse, error) { + var err error + var keys []db.WatchKeys var resp SetResponse + path := req.Path - path := req.Path - - log.Info("Delete request received with path =", path) + log.Info("Delete request received with path =", path) - app, appInfo, err := getAppModule(path) + app, appInfo, err := getAppModule(path, req.ClientVersion) - if err != nil { + if err != nil { resp.ErrSrc = ProtoErr - return resp, err - } + return resp, err + } - err = appInitialize(app, appInfo, path, nil, DELETE) + err = appInitialize(app, appInfo, path, nil, nil, DELETE) - if err != nil { - resp.ErrSrc = AppErr - return resp, err - } + if err != nil { + resp.ErrSrc = AppErr + return resp, err + } - writeMutex.Lock() + writeMutex.Lock() defer writeMutex.Unlock() - d, err := db.NewDB(getDBOptions(db.ConfigDB)) + isWriteDisabled := false + d, err := db.NewDB(getDBOptions(db.ConfigDB, isWriteDisabled)) - if err != nil { - resp.ErrSrc = ProtoErr - return resp, err - } + if err != nil { + resp.ErrSrc = ProtoErr + return resp, err + } defer d.DeleteDB() - keys, err = (*app).translateDelete(d) + keys, err = (*app).translateDelete(d) - if err != nil { + if err != nil { resp.ErrSrc = AppErr - return resp, err - } + return resp, err + } - err = d.StartTx(keys, appInfo.tablesToWatch) + err = d.StartTx(keys, appInfo.tablesToWatch) - if err != nil { + if err != nil { resp.ErrSrc = AppErr - return resp, err - } + return resp, err + } - resp, err = (*app).processDelete(d) + resp, err = (*app).processDelete(d) - if err != nil { - d.AbortTx() + if err != nil { + d.AbortTx() resp.ErrSrc = AppErr - return resp, err - } + return resp, err + } - err = d.CommitTx() + err = d.CommitTx() - if err != nil { - resp.ErrSrc = AppErr - } + if err != nil { + resp.ErrSrc = AppErr + } return resp, err } -//Gets data from the redis DB and converts it to northbound format -func Get(req GetRequest) (GetResponse, error){ - var payload []byte +//Get - Gets data from the redis DB and converts it to northbound format +func Get(req GetRequest) (GetResponse, error) { + var payload []byte var resp GetResponse - path := req.Path - log.Info("Received Get request for path = ",path) + log.Info("Received Get request for path = ", path) - app, appInfo, err := getAppModule(path) + app, appInfo, err := getAppModule(path, req.ClientVersion) - if err != nil { - resp = GetResponse{Payload:payload, ErrSrc:ProtoErr} - return resp, err - } + if err != nil { + resp = GetResponse{Payload: payload, ErrSrc: ProtoErr} + return resp, err + } - err = appInitialize(app, appInfo, path, nil, GET) + opts := appOptions{ depth: req.Depth } + err = appInitialize(app, appInfo, path, nil, &opts, GET) - if err != nil { - resp = GetResponse{Payload:payload, ErrSrc:AppErr} + if err != nil { + resp = GetResponse{Payload: payload, ErrSrc: AppErr} return resp, err } - dbs, err := getAllDbs() + isGetCase := true + dbs, err := getAllDbs(isGetCase) if err != nil { - resp = GetResponse{Payload:payload, ErrSrc:ProtoErr} - return resp, err + resp = GetResponse{Payload: payload, ErrSrc: ProtoErr} + return resp, err } defer closeAllDbs(dbs[:]) - err = (*app).translateGet (dbs) + err = (*app).translateGet(dbs) if err != nil { - resp = GetResponse{Payload:payload, ErrSrc:AppErr} - return resp, err + resp = GetResponse{Payload: payload, ErrSrc: AppErr} + return resp, err } - resp, err = (*app).processGet(dbs) + resp, err = (*app).processGet(dbs) - return resp, err + return resp, err } -//Subscribes to the paths requested and sends notifications when the data changes in DB -func Subscribe(paths []string, q *queue.PriorityQueue, stop chan struct{}) ([]*IsSubscribeResponse, error) { - var err error +func Action(req ActionRequest) (ActionResponse, error) { + var resp ActionResponse + var err error + + return resp, err +} + +//Subscribe - Subscribes to the paths requested and sends notifications when the data changes in DB +func Subscribe(req SubscribeRequest) ([]*IsSubscribeResponse, error) { + var err error var sErr error - //err = errors.New("Not implemented") + + paths := req.Paths + q := req.Q + stop := req.Stop dbNotificationMap := make(map[db.DBNum][]*notificationInfo) - resp := make ([]*IsSubscribeResponse, len(paths)) + resp := make([]*IsSubscribeResponse, len(paths)) - for i, _ := range resp { - resp[i] = &IsSubscribeResponse{Path: paths[i], - IsOnChangeSupported: false, - MinInterval: minSubsInterval, - PreferredType:Sample, - Err:nil} - } + for i := range resp { + resp[i] = &IsSubscribeResponse{Path: paths[i], + IsOnChangeSupported: false, + MinInterval: minSubsInterval, + PreferredType: Sample, + Err: nil} + } - dbs, err := getAllDbs() + isGetCase := true + dbs, err := getAllDbs(isGetCase) - if err != nil { - return resp, err - } + if err != nil { + return resp, err + } //Do NOT close the DBs here as we need to use them during subscribe notification - for i, path := range paths { + for i, path := range paths { - app, appInfo, err := getAppModule(path) + app, appInfo, err := getAppModule(path, req.ClientVersion) - if err != nil { + if err != nil { - if sErr == nil { + if sErr == nil { sErr = err } resp[i].Err = err continue - } - - nOpts, nInfo, errApp := (*app).translateSubscribe (dbs, path) + } - if errApp != nil { - resp[i].Err = errApp + nOpts, nInfo, errApp := (*app).translateSubscribe(dbs, path) - if sErr == nil { - sErr = errApp + if nOpts != nil { + if nOpts.mInterval != 0 { + if ((nOpts.mInterval >= minSubsInterval) && (nOpts.mInterval <= maxSubsInterval)) { + resp[i].MinInterval = nOpts.mInterval + } else if (nOpts.mInterval < minSubsInterval) { + resp[i].MinInterval = minSubsInterval + } else { + resp[i].MinInterval = maxSubsInterval + } } - resp[i].MinInterval = maxSubsInterval - - if nOpts != nil { - if nOpts.mInterval != 0 { - resp[i].MinInterval = nOpts.mInterval - } - - resp[i].PreferredType = nOpts.pType - } - - continue - } else { + resp[i].IsOnChangeSupported = nOpts.isOnChangeSupported + resp[i].PreferredType = nOpts.pType + } - if nOpts != nil { - if nOpts.mInterval != 0 { - resp[i].MinInterval = nOpts.mInterval - } + if errApp != nil { + resp[i].Err = errApp - resp[i].PreferredType = nOpts.pType + if sErr == nil { + sErr = errApp } + continue + } else { + if nInfo == nil { sErr = tlerr.NotSupportedError{ Format: "Subscribe not supported", Path: path} @@ -506,15 +562,13 @@ func Subscribe(paths []string, q *queue.PriorityQueue, stop chan struct{}) ([]*I continue } - resp[i].IsOnChangeSupported = true - nInfo.path = path nInfo.app = app nInfo.appInfo = appInfo nInfo.dbs = dbs dbNotificationMap[nInfo.dbno] = append(dbNotificationMap[nInfo.dbno], nInfo) - } + } } @@ -524,67 +578,76 @@ func Subscribe(paths []string, q *queue.PriorityQueue, stop chan struct{}) ([]*I return resp, sErr } - sInfo := &subscribeInfo {syncDone:false, - q:q, - stop:stop} + sInfo := &subscribeInfo{syncDone: false, + q: q, + stop: stop} sErr = startSubscribe(sInfo, dbNotificationMap) return resp, sErr } -//Check if subscribe is supported on the given paths -func IsSubscribeSupported(paths []string) ([]*IsSubscribeResponse, error) { +//IsSubscribeSupported - Check if subscribe is supported on the given paths +func IsSubscribeSupported(req IsSubscribeRequest) ([]*IsSubscribeResponse, error) { - resp := make ([]*IsSubscribeResponse, len(paths)) + paths := req.Paths + resp := make([]*IsSubscribeResponse, len(paths)) - for i, _ := range resp { - resp[i] = &IsSubscribeResponse{Path: paths[i], - IsOnChangeSupported: false, - MinInterval: minSubsInterval, - PreferredType:Sample, - Err:nil} + for i := range resp { + resp[i] = &IsSubscribeResponse{Path: paths[i], + IsOnChangeSupported: false, + MinInterval: minSubsInterval, + PreferredType: Sample, + Err: nil} } - dbs, err := getAllDbs() + isGetCase := true + dbs, err := getAllDbs(isGetCase) - if err != nil { - return resp, err - } + if err != nil { + return resp, err + } defer closeAllDbs(dbs[:]) for i, path := range paths { - app, _, err := getAppModule(path) + app, _, err := getAppModule(path, req.ClientVersion) if err != nil { resp[i].Err = err continue } - nOpts, _, errApp := (*app).translateSubscribe (dbs, path) + nOpts, _, errApp := (*app).translateSubscribe(dbs, path) + + if nOpts != nil { + if nOpts.mInterval != 0 { + if ((nOpts.mInterval >= minSubsInterval) && (nOpts.mInterval <= maxSubsInterval)) { + resp[i].MinInterval = nOpts.mInterval + } else if (nOpts.mInterval < minSubsInterval) { + resp[i].MinInterval = minSubsInterval + } else { + resp[i].MinInterval = maxSubsInterval + } + } + + resp[i].IsOnChangeSupported = nOpts.isOnChangeSupported + resp[i].PreferredType = nOpts.pType + } if errApp != nil { resp[i].Err = errApp err = errApp - continue - } else { - resp[i].IsOnChangeSupported= true - if nOpts != nil { - if nOpts.mInterval != 0 { - resp[i].MinInterval = nOpts.mInterval - } - resp[i].PreferredType = nOpts.pType - } + continue } } return resp, err } -//Gets all the models supported by Translib +//GetModels - Gets all the models supported by Translib func GetModels() ([]ModelData, error) { var err error @@ -592,20 +655,27 @@ func GetModels() ([]ModelData, error) { } //Creates connection will all the redis DBs. To be used for get request -func getAllDbs() ([db.MaxDB]*db.DB, error) { +func getAllDbs(isGetCase bool) ([db.MaxDB]*db.DB, error) { var dbs [db.MaxDB]*db.DB - var err error + var err error + var isWriteDisabled bool + + if isGetCase { + isWriteDisabled = true + } else { + isWriteDisabled = false + } //Create Application DB connection - dbs[db.ApplDB], err = db.NewDB(getDBOptions(db.ApplDB)) + dbs[db.ApplDB], err = db.NewDB(getDBOptions(db.ApplDB, isWriteDisabled)) if err != nil { closeAllDbs(dbs[:]) return dbs, err } - //Create ASIC DB connection - dbs[db.AsicDB], err = db.NewDB(getDBOptions(db.AsicDB)) + //Create ASIC DB connection + dbs[db.AsicDB], err = db.NewDB(getDBOptions(db.AsicDB, isWriteDisabled)) if err != nil { closeAllDbs(dbs[:]) @@ -613,7 +683,7 @@ func getAllDbs() ([db.MaxDB]*db.DB, error) { } //Create Counter DB connection - dbs[db.CountersDB], err = db.NewDB(getDBOptions(db.CountersDB)) + dbs[db.CountersDB], err = db.NewDB(getDBOptions(db.CountersDB, isWriteDisabled)) if err != nil { closeAllDbs(dbs[:]) @@ -621,23 +691,31 @@ func getAllDbs() ([db.MaxDB]*db.DB, error) { } //Create Log Level DB connection - dbs[db.LogLevelDB], err = db.NewDB(getDBOptions(db.LogLevelDB)) + dbs[db.LogLevelDB], err = db.NewDB(getDBOptions(db.LogLevelDB, isWriteDisabled)) if err != nil { closeAllDbs(dbs[:]) return dbs, err } + isWriteDisabled = true + //Create Config DB connection - dbs[db.ConfigDB], err = db.NewDB(getDBOptions(db.ConfigDB)) + dbs[db.ConfigDB], err = db.NewDB(getDBOptions(db.ConfigDB, isWriteDisabled)) if err != nil { closeAllDbs(dbs[:]) return dbs, err } + if isGetCase { + isWriteDisabled = true + } else { + isWriteDisabled = false + } + //Create Flex Counter DB connection - dbs[db.FlexCounterDB], err = db.NewDB(getDBOptions(db.FlexCounterDB)) + dbs[db.FlexCounterDB], err = db.NewDB(getDBOptions(db.FlexCounterDB, isWriteDisabled)) if err != nil { closeAllDbs(dbs[:]) @@ -645,7 +723,7 @@ func getAllDbs() ([db.MaxDB]*db.DB, error) { } //Create State DB connection - dbs[db.StateDB], err = db.NewDB(getDBOptions(db.StateDB)) + dbs[db.StateDB], err = db.NewDB(getDBOptions(db.StateDB, isWriteDisabled)) if err != nil { closeAllDbs(dbs[:]) @@ -665,7 +743,7 @@ func closeAllDbs(dbs []*db.DB) { } } -// Implement Compare method for priority queue for SubscribeResponse struct +// Compare - Implement Compare method for priority queue for SubscribeResponse struct func (val SubscribeResponse) Compare(other queue.Item) int { o := other.(*SubscribeResponse) if val.Timestamp > o.Timestamp { @@ -676,70 +754,77 @@ func (val SubscribeResponse) Compare(other queue.Item) int { return -1 } -func getDBOptions(dbNo db.DBNum) db.Options { +func getDBOptions(dbNo db.DBNum, isWriteDisabled bool) db.Options { var opt db.Options switch dbNo { - case db.ApplDB, db.CountersDB: - opt = getDBOptionsWithSeparator(dbNo, "", ":", ":") - break - case db.FlexCounterDB, db.AsicDB, db.LogLevelDB, db.ConfigDB, db.StateDB: - opt = getDBOptionsWithSeparator(dbNo, "", "|", "|") - break + case db.ApplDB, db.CountersDB, db.AsicDB: + opt = getDBOptionsWithSeparator(dbNo, "", ":", ":", isWriteDisabled) + case db.FlexCounterDB, db.LogLevelDB, db.ConfigDB, db.StateDB: + opt = getDBOptionsWithSeparator(dbNo, "", "|", "|", isWriteDisabled) } return opt } -func getDBOptionsWithSeparator(dbNo db.DBNum, initIndicator string, tableSeparator string, keySeparator string) db.Options { - return(db.Options { - DBNo : dbNo, - InitIndicator : initIndicator, - TableNameSeparator: tableSeparator, - KeySeparator : keySeparator, - }) +func getDBOptionsWithSeparator(dbNo db.DBNum, initIndicator string, tableSeparator string, keySeparator string, isWriteDisabled bool) db.Options { + return (db.Options{ + DBNo: dbNo, + InitIndicator: initIndicator, + TableNameSeparator: tableSeparator, + KeySeparator: keySeparator, + //IsWriteDisabled: isWriteDisabled, //Will be enabled once the DB access layer changes are checked in + }) } -func getAppModule (path string) (*appInterface, *appInfo, error) { +func getAppModule(path string, clientVer Version) (*appInterface, *appInfo, error) { var app appInterface - aInfo, err := getAppModuleInfo(path) + aInfo, err := getAppModuleInfo(path) - if err != nil { - return nil, aInfo, err - } + if err != nil { + return nil, aInfo, err + } - app, err = getAppInterface(aInfo.appType) + app, err = getAppInterface(aInfo.appType) - if err != nil { - return nil, aInfo, err - } + if err != nil { + return nil, aInfo, err + } return &app, aInfo, err } -func appInitialize (app *appInterface, appInfo *appInfo, path string, payload *[]byte, opCode int) error { - var err error - var input []byte +func appInitialize(app *appInterface, appInfo *appInfo, path string, payload *[]byte, opts *appOptions, opCode int) error { + var err error + var input []byte - if payload != nil { - input = *payload - } + if payload != nil { + input = *payload + } - if appInfo.isNative { - log.Info("Native MSFT format") - data := appData{path: path, payload: input} - (*app).initialize(data) - } else { - ygotStruct, ygotTarget, err := getRequestBinder (&path, payload, opCode, &(appInfo.ygotRootType)).unMarshall() - if err != nil { - log.Info("Error in request binding: ", err) - return err - } + if appInfo.isNative { + log.Info("Native MSFT format") + data := appData{path: path, payload: input} + data.setOptions(opts) + (*app).initialize(data) + } else { + ygotStruct, ygotTarget, err := getRequestBinder(&path, payload, opCode, &(appInfo.ygotRootType)).unMarshall() + if err != nil { + log.Info("Error in request binding: ", err) + return err + } - data := appData{path: path, payload: input, ygotRoot: ygotStruct, ygotTarget: ygotTarget} - (*app).initialize(data) - } + data := appData{path: path, payload: input, ygotRoot: ygotStruct, ygotTarget: ygotTarget} + data.setOptions(opts) + (*app).initialize(data) + } - return err + return err +} + +func (data *appData) setOptions(opts *appOptions) { + if opts != nil { + data.appOptions = *opts + } } diff --git a/translib/version.go b/translib/version.go new file mode 100644 index 000000000000..c066cfcba19a --- /dev/null +++ b/translib/version.go @@ -0,0 +1,39 @@ +package translib + +import ( + "fmt" +) + +// theYangBundleVersion indicates the current yang bundle version. +// It is read from the version.xml config file. +var theYangBundleVersion Version + +// theYangBaseVersion indicates the minimum yang bundle version +// supported by translib APIs. This will be usually 1 major version +// below current ynag bundle version {theYangBundleVersion.Major-1, 0, 0} +var theYangBaseVersion Version + +// Version represents the semantic version number in Major.Minor.Patch +// format. +type Version struct { + Major uint32 // Major version number + Minor uint32 // Minor version number + Patch uint32 // Patch number +} + +// NewVersion creates a Version object from given version string +func NewVersion(s string) (Version, error) { + var v Version + err := v.Set(s) + return v, err +} + +// Set parses a version string in X.Y.Z into a Version object v. +func (v *Version) Set(s string) error { + _, err := fmt.Sscanf(s, "%d.%d.%d", &v.Major, &v.Minor, &v.Patch) + if err == nil { + err = fmt.Errorf("Invalid version \"%s\"", s) + } + + return err +}