diff --git a/models/yang/annotations/openconfig-system-annot.yang b/models/yang/annotations/openconfig-system-annot.yang new file mode 100644 index 000000000..7b9accc14 --- /dev/null +++ b/models/yang/annotations/openconfig-system-annot.yang @@ -0,0 +1,26 @@ +module openconfig-system-annot { + + yang-version "1"; + + namespace "http://openconfig.net/yang/openconfig-system-annot"; + prefix "oc-sys-annot"; + + import openconfig-system { prefix oc-sys; } + import sonic-extensions {prefix sonic-ext; } + import openconfig-system-grpc { prefix oc-sys-grpc; } + + deviation /oc-sys:system/oc-sys:ssh-server/oc-sys:state { + deviate add { + sonic-ext:db-name "STATE_DB"; + sonic-ext:subtree-transformer "ssh_server_state_xfmr"; + } + } + + deviation /oc-sys:system/oc-sys-grpc:grpc-servers { + deviate add { + sonic-ext:key-transformer "grpc_server_key_xfmr"; + sonic-ext:subtree-transformer "grpc_server_xfmr"; + } + } +} + diff --git a/translib/transformer/subscribe_req_xlate.go b/translib/transformer/subscribe_req_xlate.go index bc2a8cf13..27ab34136 100644 --- a/translib/transformer/subscribe_req_xlate.go +++ b/translib/transformer/subscribe_req_xlate.go @@ -478,7 +478,7 @@ func (pathXltr *subscribePathXlator) handleSubtreeNodeXlate() error { log.Info(pathXltr.subReq.reqLogId, "handleSubtreeNodeXlate: handleSubtreeNodeXlate: uriSubtree: ", uriSubtree) } - subInParam := XfmrSubscInParams{uriSubtree, pathXltr.subReq.dbs, make(RedisDbMap), TRANSLATE_SUBSCRIBE} + subInParam := XfmrSubscInParams{uri: uriSubtree, requestURI: pathXltr.subReq.reqUri, dbs: pathXltr.subReq.dbs, dbDataMap: make(RedisDbMap), subscProc: TRANSLATE_SUBSCRIBE} subOutPram, subErr := xfmrSubscSubtreeHandler(subInParam, ygXpathInfo.xfmrFunc) if log.V(dbLgLvl) { diff --git a/translib/transformer/xfmr_interface.go b/translib/transformer/xfmr_interface.go index 5994a7956..ad48ca770 100644 --- a/translib/transformer/xfmr_interface.go +++ b/translib/transformer/xfmr_interface.go @@ -76,10 +76,11 @@ type notificationOpts struct { // XfmrSubscInParams represents input to subscribe subtree callbacks - request uri, DBs info access-pointers, DB info for request uri and subscription process type from translib. type XfmrSubscInParams struct { - uri string - dbs [db.MaxDB]*db.DB - dbDataMap RedisDbMap - subscProc SubscProcType + uri string + requestURI string + dbs [db.MaxDB]*db.DB + dbDataMap RedisDbMap + subscProc SubscProcType } // XfmrSubscOutParams represents output from subscribe subtree callback - DB data for request uri, Need cache, OnChange, subscription preference and interval. diff --git a/translib/transformer/xfmr_system.go b/translib/transformer/xfmr_system.go new file mode 100644 index 000000000..b2bec4ae5 --- /dev/null +++ b/translib/transformer/xfmr_system.go @@ -0,0 +1,301 @@ +package transformer + +import ( + "errors" + "fmt" + "strconv" + "strings" + + "github.com/Azure/sonic-mgmt-common/translib/db" + "github.com/Azure/sonic-mgmt-common/translib/ocbinds" + log "github.com/golang/glog" + ygot "github.com/openconfig/ygot/ygot" +) + +const ( + GNXI_ID = "gnxi" + + /** Credential Tables **/ + CREDENTIALS_TBL = "CREDENTIALS" + CRED_PATHZ_TBL = "CREDENTIALS|PATHZ_POLICY" + CERT_TBL = "CREDENTIALS|CERT" +) + +type sshState struct { + caKeys certData + hostCert certData + hostKey certData + counters accessCounters +} + +type accessCounters struct { + accessRejects uint64 + lastAccessReject uint64 + accessAccepts uint64 + lastAccessAccept uint64 +} + +type certData struct { + version string + created uint64 +} + +func init() { + XlateFuncBind("DbToYang_grpc_server_xfmr", DbToYang_grpc_server_xfmr) + XlateFuncBind("Subscribe_grpc_server_xfmr", Subscribe_grpc_server_xfmr) + 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) +} + +type grpcState struct { + name string + certVersion string + certCreated uint64 + caVersion string + caCreated uint64 + crlVersion string + crlCreated uint64 + authPolVersion string + authPolCreated uint64 + profileId string + pathzVersion string + pathzCreated uint64 +} + +func getAppRootObject(inParams XfmrParams) *ocbinds.OpenconfigSystem_System { + deviceObj := (*inParams.ygRoot).(*ocbinds.Device) + return deviceObj.System +} + +func getAllKeys(sdb *db.DB, tblName string) ([]string, error) { + tbl, err := sdb.GetTable(&db.TableSpec{Name: tblName}) + if err != nil { + return nil, fmt.Errorf("Can't get table: %v, err: %v", tblName, err) + } + log.V(3).Infof("tbl: %v", tbl) + keys, err := tbl.GetKeys() + if err != nil { + return nil, fmt.Errorf("Can't get keys from %v, err: %v", tblName, err) + } + log.V(3).Infof("tbl keys: %v", keys) + ret := []string{} + for _, key := range keys { + if len(key.Comp) != 3 { + // This is a phantom key. Ignore it. + continue + } + ret = append(ret, key.Comp[2]) + } + log.V(3).Infof("keys: %v", ret) + return ret, nil +} + +var Subscribe_ssh_server_state_xfmr SubTreeXfmrSubscribe = func(inParams XfmrSubscInParams) (XfmrSubscOutParams, error) { + log.V(3).Infof("Subscribe_ssh_server_state_xfmr:%s", inParams.requestURI) + + return XfmrSubscOutParams{ + dbDataMap: RedisDbSubscribeMap{ + db.StateDB: {"CREDENTIALS": {"SSH_HOST": {}}}}, + onChange: OnchangeEnable, + nOpts: ¬ificationOpts{mInterval: 0, pType: OnChange}, + }, nil +} + +var DbToYang_ssh_server_state_xfmr SubTreeXfmrDbToYang = func(inParams XfmrParams) error { + var state sshState + + table, err := inParams.dbs[inParams.curDb].GetEntry(&db.TableSpec{Name: "CREDENTIALS"}, db.Key{Comp: []string{"SSH_HOST"}}) + if err != nil { + log.V(3).Infof("Failed to read from StateDB: %v", inParams.table) + return err + } + + state.caKeys.version = table.Get("ca_keys_version") + time := table.Get("ca_keys_created_on") + if state.caKeys.created, err = strconv.ParseUint(time, 10, 64); err != nil && time != "" { + log.V(0).Infof("Couldn't find ca_keys_created_on: %v", err) + } + state.hostKey.version = table.Get("host_key_version") + time = table.Get("host_key_created_on") + if state.hostKey.created, err = strconv.ParseUint(time, 10, 64); err != nil && time != "" { + log.V(0).Infof("Couldn't find host_key_created_on: %v", err) + } + state.hostCert.version = table.Get("host_cert_version") + time = table.Get("host_cert_created_on") + if state.hostCert.created, err = strconv.ParseUint(time, 10, 64); err != nil && time != "" { + log.V(0).Infof("Couldn't find host_cert_created_on: %v", err) + } + accepts := table.Get("access_accepts") + if state.counters.accessAccepts, err = strconv.ParseUint(accepts, 10, 64); err != nil && accepts != "" { + log.V(0).Infof("Couldn't find access_accepts: %v", err) + } + lastAccept := table.Get("last_access_accept") + if state.counters.lastAccessAccept, err = strconv.ParseUint(lastAccept, 10, 64); err != nil && lastAccept != "" { + log.V(0).Infof("Couldn't find last_access_accept: %v", err) + } + rejects := table.Get("access_rejects") + if state.counters.accessRejects, err = strconv.ParseUint(rejects, 10, 64); err != nil && rejects != "" { + log.V(0).Infof("Couldn't find access_rejects: %v", err) + } + lastReject := table.Get("last_access_reject") + if state.counters.lastAccessReject, err = strconv.ParseUint(lastReject, 10, 64); err != nil && lastReject != "" { + log.V(0).Infof("Couldn't find last_access_reject: %v", err) + } + + sysObj := getAppRootObject(inParams) + ygot.BuildEmptyTree(sysObj.SshServer.State) + + sysObj.SshServer.State.ActiveTrustedUserCaKeysCreatedOn = &state.caKeys.created + sysObj.SshServer.State.ActiveTrustedUserCaKeysVersion = &state.caKeys.version + sysObj.SshServer.State.ActiveHostCertificateCreatedOn = &state.hostKey.created + sysObj.SshServer.State.ActiveHostCertificateVersion = &state.hostKey.version + sysObj.SshServer.State.ActiveHostKeyCreatedOn = &state.hostCert.created + sysObj.SshServer.State.ActiveHostKeyVersion = &state.hostCert.version + sysObj.SshServer.State.Counters.AccessAccepts = &state.counters.accessAccepts + 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 Subscribe_grpc_server_xfmr SubTreeXfmrSubscribe = func(inParams XfmrSubscInParams) (XfmrSubscOutParams, error) { + pathInfo := NewPathInfo(inParams.uri) + serverName := pathInfo.Var("name") + targetUriPath, err := getYangPathFromUri(pathInfo.Path) + if err != nil { + return XfmrSubscOutParams{}, err + } + log.V(3).Infof("Subscribe_grpc_server_xfmr: targetUriPath: %s name: %s", targetUriPath, serverName) + + var result XfmrSubscOutParams + if serverName == "" { + result.dbDataMap = RedisDbSubscribeMap{ + db.StateDB: map[string]map[string]map[string]string{ + CREDENTIALS_TBL: { + "CERT|gnxi": {}, + }, + }, + } + } else { + result = XfmrSubscOutParams{ + dbDataMap: RedisDbSubscribeMap{ + db.StateDB: map[string]map[string]map[string]string{ + CREDENTIALS_TBL: { + "CERT|gnxi": {}, + }, + }}, + } + } + + if !strings.HasPrefix(targetUriPath, "/openconfig-system:system/grpc-servers/grpc-server/gnsi-pathz:gnmi-pathz-policy-counters") { + result.onChange = OnchangeEnable + result.nOpts = ¬ificationOpts{mInterval: 0, pType: OnChange} + } else { + + // For counters, configure nOpts to enable sampling on path. + 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")} + if log.V(3) { + log.Info("SubtreeXfmrFunc - Uri SYS AUTH: ", inParams.uri) + log.Info("names:", serverNames) + } + stateDb := inParams.dbs[db.StateDB] + if stateDb == nil { + return errors.New("DbToYang_grpc_server_xfmr stateDb is nil!") + } + if len(serverNames) == 0 || len(serverNames[0]) == 0 { + var err error + if serverNames, err = getAllKeys(stateDb, CERT_TBL); err != nil { + return err + } + } + sysObj := getAppRootObject(inParams) + ygot.BuildEmptyTree(sysObj) + ygot.BuildEmptyTree(sysObj.GrpcServers) + + for _, serverName := range serverNames { + log.V(3).Info("serverName: ", serverName) + var state grpcState + state.name = serverName + + certzID := GNXI_ID + certTable, err := stateDb.GetEntry(&db.TableSpec{Name: CERT_TBL}, db.Key{Comp: []string{certzID}}) + if err != nil { + log.V(0).Infof("Failed to read from StateDB %v | %v err: %v", CERT_TBL, certzID, err) + } else { + state.certVersion = certTable.Get("certificate_version") + state.caVersion = certTable.Get("ca_trust_bundle_version") + state.crlVersion = certTable.Get("certificate_revocation_list_bundle_version") + state.authPolVersion = certTable.Get("authentication_policy_version") + state.profileId = certTable.Get("ssl_profile_id") + time := certTable.Get("certificate_created_on") + if state.certCreated, err = strconv.ParseUint(time, 10, 64); err != nil && time != "" { + log.V(0).Infof("Cannot convert `certificate_created_on` for %v, err: %v", certzID, err) + } + time = certTable.Get("ca_trust_bundle_created_on") + if state.caCreated, err = strconv.ParseUint(time, 10, 64); err != nil && time != "" { + log.V(0).Infof("Cannot convert `ca_trust_bundle_created_on` for %v, err: %v", certzID, err) + } + time = certTable.Get("certificate_revocation_list_bundle_created_on") + if state.crlCreated, err = strconv.ParseUint(time, 10, 64); err != nil && time != "" { + log.V(0).Infof("Cannot convert `certificate_revocation_list_bundle_created_on` for %v, err: %v", certzID, err) + } + time = certTable.Get("authentication_policy_created_on") + if state.authPolCreated, err = strconv.ParseUint(time, 10, 64); err != nil && time != "" { + log.V(0).Infof("Cannot convert `authentication_policy_created_on` for %v, err: %v", certzID, err) + } + } + + pathzTable, err := stateDb.GetEntry(&db.TableSpec{Name: CRED_PATHZ_TBL}, db.Key{Comp: []string{"ACTIVE"}}) + if err != nil { + log.V(0).Infof("Failed to read from StateDB %v, err: %v", CRED_PATHZ_TBL, err) + } else { + state.pathzVersion = pathzTable.Get("pathz_version") + if timeStr := pathzTable.Get("pathz_created_on"); timeStr != "" { + if state.pathzCreated, err = strconv.ParseUint(timeStr, 10, 64); err != nil { + log.V(0).Infof("Cannot convert `pathz_created_on` for %v, err: %v", serverName, err) + } + } + } + + serverObj, ok := sysObj.GrpcServers.GrpcServer[serverName] + if !ok { + serverObj, err = sysObj.GrpcServers.NewGrpcServer(serverName) + if err != nil { + log.V(0).Infof("sysObj.GrpcServers.NewGrpcServer(%v) failed: %v", serverName, err) + continue + } + } + ygot.BuildEmptyTree(serverObj) + serverObj.State.Name = &state.name + serverObj.State.CaTrustBundleVersion = &state.caVersion + serverObj.State.CaTrustBundleCreatedOn = &state.caCreated + serverObj.State.CertificateVersion = &state.certVersion + serverObj.State.CertificateCreatedOn = &state.certCreated + serverObj.State.CertificateRevocationListBundleCreatedOn = &state.crlCreated + serverObj.State.CertificateRevocationListBundleVersion = &state.crlVersion + serverObj.State.AuthenticationPolicyVersion = &state.authPolVersion + serverObj.State.SslProfileId = &state.profileId + serverObj.State.AuthenticationPolicyCreatedOn = &state.authPolCreated + serverObj.State.GnmiPathzPolicyCreatedOn = &state.pathzCreated + serverObj.State.GnmiPathzPolicyVersion = &state.pathzVersion + + } + + 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 +}