diff --git a/config/transformer/models_list b/config/transformer/models_list index 133a3bfa3..32f43e52e 100644 --- a/config/transformer/models_list +++ b/config/transformer/models_list @@ -1,15 +1,16 @@ #List yang models transformer need to load -openconfig-acl.yang openconfig-acl-annot.yang -openconfig-sampling-sflow.yang -openconfig-sampling-sflow-annot.yang +openconfig-acl.yang +openconfig-if-aggregate.yang openconfig-if-ethernet.yang -openconfig-interfaces.yang -openconfig-interfaces-annot.yang openconfig-if-ip.yang -openconfig-if-aggregate.yang -openconfig-mclag.yang +openconfig-interfaces-annot.yang +openconfig-interfaces.yang openconfig-mclag-annot.yang +openconfig-mclag.yang +openconfig-sampling-sflow-annot.yang +openconfig-sampling-sflow.yang +openconfig-system-annot.yang openconfig-vlan.yang gnsi-authz.yang gnsi-pathz.yang diff --git a/translib/tlerr/tlerr.go b/translib/tlerr/tlerr.go index 93bdbeb05..fa2b1d1c8 100644 --- a/translib/tlerr/tlerr.go +++ b/translib/tlerr/tlerr.go @@ -30,12 +30,11 @@ Hence, it cannot occur here. package tlerr import ( - // "fmt" + "errors" "github.com/Azure/sonic-mgmt-common/cvl" + "github.com/golang/glog" "golang.org/x/text/language" "golang.org/x/text/message" - // "errors" - // "strings" ) var p *message.Printer @@ -190,3 +189,32 @@ type TranslibBusy struct { func (e TranslibBusy) Error() string { return p.Sprintf("Translib Busy") } + +func IsTranslibRedisClientEntryNotExist(err error) bool { + switch err.(type) { + case *TranslibRedisClientEntryNotExist, TranslibRedisClientEntryNotExist: + return true + } + return false +} + +// isDBEntryNotExistError returns `true` if `err` is (or wraps around) an error +// of type `TranslibRedisClientEntryNotExist`. +func isDBEntryNotExistError(err error) bool { + if IsTranslibRedisClientEntryNotExist(err) { + return true + } + pdberr := &TranslibRedisClientEntryNotExist{} + return errors.As(err, &TranslibRedisClientEntryNotExist{}) || errors.As(err, &pdberr) +} + +// ErrorSeverity based on `err` calculates the VLOG level. +func ErrorSeverity(err error) glog.Level { + if err == nil { + return 3 + } + if isDBEntryNotExistError(err) { + return 3 + } + return 0 +} diff --git a/translib/transformer/xfmr_system.go b/translib/transformer/xfmr_system.go index b2bec4ae5..0e0ff52ec 100644 --- a/translib/transformer/xfmr_system.go +++ b/translib/transformer/xfmr_system.go @@ -8,6 +8,7 @@ import ( "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" ygot "github.com/openconfig/ygot/ygot" ) @@ -18,7 +19,31 @@ const ( /** Credential Tables **/ CREDENTIALS_TBL = "CREDENTIALS" CRED_PATHZ_TBL = "CREDENTIALS|PATHZ_POLICY" + CRED_AUTHZ_TBL = "CREDENTIALS|AUTHZ_POLICY" CERT_TBL = "CREDENTIALS|CERT" + AUTHZ_TBL = "AUTHZ_TABLE" + ACCEPTS = "permitted" + REJECTS = "denied" + cntResult = "cntResult" + tsResult = "tsResult" + + /** System Root paths **/ + SYSTEM_ROOT = "/openconfig-system:system" + + /** Pathz paths **/ + GRPC_OC_SERVERS = SYSTEM_ROOT + "/openconfig-system-grpc:grpc-servers" + GRPC_SERVERS = SYSTEM_ROOT + "/grpc-servers" + GRPC_SERVER = GRPC_OC_SERVERS + "/grpc-server" + + /** Authz paths **/ + AUTHZ_POLICY_COUNTERS = GRPC_SERVER + "/authz-policy-counters" + ALL_AUTHZ = AUTHZ_POLICY_COUNTERS + "/rpcs" + SINGLE_AUTHZ = ALL_AUTHZ + "/rpc" + AUTHZ_STATE = SINGLE_AUTHZ + "/state" + AUTHZ_SUCCESS = AUTHZ_STATE + "/access-accepts" + AUTHZ_SUCCESS_TIMESTAMP = AUTHZ_STATE + "/last-access-accept" + AUTHZ_FAILED = AUTHZ_STATE + "/access-rejects" + AUTHZ_FAILED_TIMESTAMP = AUTHZ_STATE + "/last-access-reject" ) type sshState struct { @@ -46,6 +71,8 @@ func init() { XlateFuncBind("DbToYang_grpc_server_key_xfmr", DbToYang_grpc_server_key_xfmr) XlateFuncBind("DbToYang_ssh_server_state_xfmr", DbToYang_ssh_server_state_xfmr) XlateFuncBind("Subscribe_ssh_server_state_xfmr", Subscribe_ssh_server_state_xfmr) + XlateFuncBind("DbToYang_authz_policy_xfmr", DbToYang_authz_policy_xfmr) + XlateFuncBind("Subscribe_authz_policy_xfmr", Subscribe_authz_policy_xfmr) } type grpcState struct { @@ -101,6 +128,15 @@ var Subscribe_ssh_server_state_xfmr SubTreeXfmrSubscribe = func(inParams XfmrSub nOpts: ¬ificationOpts{mInterval: 0, pType: OnChange}, }, nil } +var Subscribe_authz_policy_xfmr SubTreeXfmrSubscribe = func(inParams XfmrSubscInParams) (XfmrSubscOutParams, error) { + log.V(3).Infof("Subscribe_authz_policy_xfmr:%s", inParams.requestURI) + return XfmrSubscOutParams{ + dbDataMap: RedisDbSubscribeMap{ + db.StateDB: {"CREDENTIALS": {"AUTHZ_POLICY|gnxi": {}}}}, + onChange: OnchangeEnable, + nOpts: ¬ificationOpts{mInterval: 0, pType: OnChange}, + }, nil +} var DbToYang_ssh_server_state_xfmr SubTreeXfmrDbToYang = func(inParams XfmrParams) error { var state sshState @@ -156,10 +192,38 @@ var DbToYang_ssh_server_state_xfmr SubTreeXfmrDbToYang = func(inParams XfmrParam sysObj.SshServer.State.Counters.AccessRejects = &state.counters.accessRejects sysObj.SshServer.State.Counters.LastAccessAccept = &state.counters.lastAccessAccept sysObj.SshServer.State.Counters.LastAccessReject = &state.counters.lastAccessReject + return nil +} +var DbToYang_authz_policy_xfmr SubTreeXfmrDbToYang = func(inParams XfmrParams) error { + var state certData + + table, err := inParams.dbs[inParams.curDb].GetEntry(&db.TableSpec{Name: CRED_AUTHZ_TBL}, db.Key{Comp: []string{GNXI_ID}}) + if err != nil { + log.V(3).Infof("Failed to read from StateDB: %v", inParams.table) + return err + } + + state.version = table.Get("authz_version") + time := table.Get("authz_created_on") + if state.created, err = strconv.ParseUint(time, 10, 64); err != nil && time != "" { + log.V(3).Infof("Couldn't find authz_created_on: %v", err) + } + + sysObj := getAppRootObject(inParams) + ygot.BuildEmptyTree(sysObj.Aaa.Authorization.State) + + sysObj.Aaa.Authorization.State.GrpcAuthzPolicyCreatedOn = &state.created + sysObj.Aaa.Authorization.State.GrpcAuthzPolicyVersion = &state.version return nil } +var DbToYang_grpc_server_key_xfmr KeyXfmrDbToYang = func(inParams XfmrParams) (map[string]interface{}, error) { + log.V(3).Info("DbToYang_grpc_server_key_xfmr root, uri: ", inParams.ygRoot, inParams.uri) + + return map[string]interface{}{"name": NewPathInfo(inParams.uri).Var("name")}, nil +} + var Subscribe_grpc_server_xfmr SubTreeXfmrSubscribe = func(inParams XfmrSubscInParams) (XfmrSubscOutParams, error) { pathInfo := NewPathInfo(inParams.uri) serverName := pathInfo.Var("name") @@ -198,14 +262,20 @@ var Subscribe_grpc_server_xfmr SubTreeXfmrSubscribe = func(inParams XfmrSubscInP result.onChange = OnchangeEnable result.nOpts = ¬ificationOpts{mInterval: 60, pType: Sample} } + return result, nil } var DbToYang_grpc_server_xfmr SubTreeXfmrDbToYang = func(inParams XfmrParams) error { pathInfo := NewPathInfo(inParams.uri) serverNames := []string{pathInfo.Var("name")} + targetUriPath, err := getYangPathFromUri(pathInfo.Path) + if err != nil { + log.V(0).Infof("Error Parsing Uri Path, err: %v", err) + } if log.V(3) { log.Info("SubtreeXfmrFunc - Uri SYS AUTH: ", inParams.uri) + log.Info("TARGET URI PATH SYS AUTH:", targetUriPath) log.Info("names:", serverNames) } stateDb := inParams.dbs[db.StateDB] @@ -289,13 +359,156 @@ var DbToYang_grpc_server_xfmr SubTreeXfmrDbToYang = func(inParams XfmrParams) er serverObj.State.GnmiPathzPolicyCreatedOn = &state.pathzCreated serverObj.State.GnmiPathzPolicyVersion = &state.pathzVersion - } + // Authz counter + authzTables, err := stateDb.GetTable(&db.TableSpec{Name: AUTHZ_TBL}) + if err != nil { + log.V(tlerr.ErrorSeverity(err)).Infof("getAuthzPolicyCounter failed to get AUTHZ_TBL, err: %v", err) + return err + } + + rpcString := pathInfo.Var("name#2") + rpcStrings := []string{rpcString} + + if rpcString == "" || rpcString == "*" { + rpcStrings = []string{} + rpcStrings, err = getAllRpcs(authzTables, serverName) + if err != nil { + log.V(tlerr.ErrorSeverity(err)).Infof("Failed get all authz rpcs, err: %v", err) + return err + } + } + + ygot.BuildEmptyTree(serverObj.AuthzPolicyCounters) + for _, rpcString := range rpcStrings { + service, rpc, err := getServiceRpc(rpcString) + if err != nil { + log.V(0).Infof("invalid RPC method %s", rpcString) + continue + } + + authzPolicyData := getAuthzPolicyCounter(authzTables, serverName, rpcString) + rpcObj, ok := serverObj.AuthzPolicyCounters.Rpcs.Rpc[rpcString] + if !ok { + rpcObj, err = serverObj.AuthzPolicyCounters.Rpcs.NewRpc(rpcString) + if err != nil { + log.V(0).Infof("serverObj.AuthzPolicyCounters.Rpcs.NewRpc(%v) failed: %v", rpcString, err) + continue + } + } + ygot.BuildEmptyTree(rpcObj) + + // If targetUriPath is a parent AUTHZ_STATE, i.e.root path, all counters and timestamps should be returned + allAuthzCounter := strings.HasPrefix(AUTHZ_STATE, targetUriPath) || targetUriPath == GRPC_OC_SERVERS + tmpCnt := make(map[string]*uint64) + tmpTs := make(map[string]*uint64) + if cnt, ok := authzPolicyData[cntResult]; ok { + tmpCnt = cnt + } + if ts, ok := authzPolicyData[tsResult]; ok { + tmpTs = ts + } + // Handle root paths here. + if allAuthzCounter { + ygot.BuildEmptyTree(rpcObj.State) + rpcObj.State.AccessAccepts = tmpCnt["*|"+serverName+"|"+service+"|"+rpc+"|"+ACCEPTS] + rpcObj.State.LastAccessAccept = tmpTs["*|"+serverName+"|"+service+"|"+rpc+"|"+ACCEPTS] + rpcObj.State.AccessRejects = tmpCnt["*|"+serverName+"|"+service+"|"+rpc+"|"+REJECTS] + rpcObj.State.LastAccessReject = tmpTs["*|"+serverName+"|"+service+"|"+rpc+"|"+REJECTS] + + } else { + // Handle leaf paths here. + switch targetUriPath { + case AUTHZ_SUCCESS: + rpcObj.State.AccessAccepts = tmpCnt["*|"+serverName+"|"+service+"|"+rpc+"|"+ACCEPTS] + case AUTHZ_SUCCESS_TIMESTAMP: + rpcObj.State.LastAccessAccept = tmpTs["*|"+serverName+"|"+service+"|"+rpc+"|"+ACCEPTS] + case AUTHZ_FAILED: + rpcObj.State.AccessRejects = tmpCnt["*|"+serverName+"|"+service+"|"+rpc+"|"+REJECTS] + case AUTHZ_FAILED_TIMESTAMP: + rpcObj.State.LastAccessReject = tmpTs["*|"+serverName+"|"+service+"|"+rpc+"|"+REJECTS] + } + } + } + } return nil } -var DbToYang_grpc_server_key_xfmr KeyXfmrDbToYang = func(inParams XfmrParams) (map[string]interface{}, error) { - log.V(3).Info("DbToYang_grpc_server_key_xfmr root, uri: ", inParams.ygRoot, inParams.uri) +func getAuthzPolicyCounter(authzTables db.Table, server string, rpcString string) map[string]map[string]*uint64 { + cntMap := make(map[string]*uint64) + tsMap := make(map[string]*uint64) - return map[string]interface{}{"name": NewPathInfo(inParams.uri).Var("name")}, nil + for _, oper := range []string{ACCEPTS, REJECTS} { + var service string + var rpc string + service, rpc, err := getServiceRpc(rpcString) + if err != nil { + log.V(0).Infof("invalid RPC method %s", rpcString) + continue + } + + pattern := "*|" + server + "|" + service + "|" + rpc + "|" + oper + key := db.NewKey(server, service, rpc, oper) + + // Sum the data collected + value, err := authzTables.GetEntry(*key) + if err != nil { + log.V(tlerr.ErrorSeverity(err)).Infof("Cannot get value from %v table for %v, err: %v", AUTHZ_TBL, key, err) + continue + } + + c := value.Get("count") + if c != "" { + if dbCnt, err := strconv.ParseUint(c, 10, 64); err == nil { + cntMap[pattern] = &dbCnt + } else { + log.V(tlerr.ErrorSeverity(err)).Infof("Failed to convert counters from DB for authz, err: %v", err) + } + } + + ts := value.Get("timestamp") + if ts != "" { + if dbTs, err := strconv.ParseUint(ts, 10, 64); err == nil { + tsMap[pattern] = &dbTs + } else { + log.V(tlerr.ErrorSeverity(err)).Infof("Failed to convert timestamp for counters from DB for authz, err: %v", err) + } + } + } + return map[string]map[string]*uint64{cntResult: cntMap, tsResult: tsMap} +} + +func getServiceRpc(rpcString string) (string, string, error) { + strs := strings.Split(rpcString, "/") + if len(strs) == 3 { + return strs[1], strs[2], nil + } + + return "", "", errors.New("invalid RPC method " + rpcString) +} + +func getAllRpcs(authzTables db.Table, server string) ([]string, error) { + var res []string + check := make(map[string]bool) + authzTableKeys, err := authzTables.GetKeys() + if err != nil { + log.V(tlerr.ErrorSeverity(err)).Infof("Cannot get all keys from %v table, err: %v", AUTHZ_TBL, err) + return []string{}, err + } + for _, authzTableKey := range authzTableKeys { + if len(authzTableKey.Comp) != 4 { + log.V(3).Infof("invalid number of Comps for authzTableKey %v.", authzTableKey) + continue + } + if authzTableKey.Comp[0] != server { + continue + } + key := "/" + authzTableKey.Comp[1] + "/" + authzTableKey.Comp[2] + if val, ok := check[key]; !ok || !val { + res = append(res, key) + check[key] = true + } + } + + return res, nil }