From 71b3577038a803c158a76c3ad627c0cdd56f9db3 Mon Sep 17 00:00:00 2001 From: kanchanavelusamy Date: Tue, 23 Sep 2025 17:58:23 +0000 Subject: [PATCH 1/2] Add gNSI Authz, Certz, Pathz and Credentialz YANG models This PR introduces the new YANG models for gNSI Authz, Certz, Pathz and Credentialz. And, updates the list to include the new models. --- config/transformer/models_list | 4 + models/yang/gnsi-authz.yang | 155 ++++++++++++++++ models/yang/gnsi-certz.yang | 187 +++++++++++++++++++ models/yang/gnsi-credentialz.yang | 299 ++++++++++++++++++++++++++++++ models/yang/gnsi-pathz.yang | 298 +++++++++++++++++++++++++++++ 5 files changed, 943 insertions(+) create mode 100644 models/yang/gnsi-authz.yang create mode 100644 models/yang/gnsi-certz.yang create mode 100644 models/yang/gnsi-credentialz.yang create mode 100644 models/yang/gnsi-pathz.yang diff --git a/config/transformer/models_list b/config/transformer/models_list index 89ba79e9c..133a3bfa3 100644 --- a/config/transformer/models_list +++ b/config/transformer/models_list @@ -11,3 +11,7 @@ openconfig-if-aggregate.yang openconfig-mclag.yang openconfig-mclag-annot.yang openconfig-vlan.yang +gnsi-authz.yang +gnsi-pathz.yang +gnsi-certz.yang +gnsi-credentialz.yang diff --git a/models/yang/gnsi-authz.yang b/models/yang/gnsi-authz.yang new file mode 100644 index 000000000..cc1a3337d --- /dev/null +++ b/models/yang/gnsi-authz.yang @@ -0,0 +1,155 @@ +module gnsi-authz { + yang-version 1.1; + namespace "https://github.com/openconfig/gnsi/authz/yang"; + prefix gnsi-authz; + import openconfig-system { + prefix oc-sys; + } + import openconfig-system-grpc { + prefix oc-sys-grpc; + } + import openconfig-types { + prefix oc-types; + } + import openconfig-yang-types { + prefix oc-yang; + } + organization + "Google LLC"; + contact + "Google LLC"; + description + "This module provides a data model for the metadata of the gRPC + authorization policies installed on a networking device."; + revision 2022-10-30 { + description + "Adds success/failure counters."; + reference "0.3.0"; + } + revision 2022-08-01 { + description + "Single authz policy."; + reference "0.2.0"; + } + revision 2022-01-17 { + description + "Initial revision."; + reference "0.1.0"; + } + typedef version { + type string; + description + "The version ID of the gRPC authorization policy as provided by + the gRPC Authorization Policy Manager when the policy was pushed. + This leaf persists through a reboot."; + } + typedef created-on { + type oc-types:timeticks64; + description + "The creation time of the gRPC authorization policy as reported by + the gRPC Authorization Policy manager when the policy was pushed + to the device. This value is reported as nanoseconds since epoch + (January 1st, 1970 00:00:00 GMT). This leaf persists through + a reboot."; + } + // gRPC server authorization policy related definitions. + grouping counters { + description + "A collection of counters that were collected by the gNSI.authz + module while evaluating access to a RPC."; + leaf access-rejects { + type oc-yang:counter64; + description + "The total number of times the gNSI.authz module denied access + to a RPC."; + } + leaf last-access-reject { + type oc-types:timeticks64; + description + "A timestamp of the last time the gNSI.authz denied access to + a RPC."; + } + leaf access-accepts { + type oc-yang:counter64; + description + "The total number of times the gNSI.authz module allowed access + to a RPC."; + } + leaf last-access-accept { + type oc-types:timeticks64; + description + "A timestamp of the last time the gNSI.authz allowed access to + a RPC."; + } + } + grouping grpc-server-user-authz-policy-success-failure-counters { + description + "A collection of counters collected by the gNSI.authz module."; + container rpcs { + description + "A collection of counters collected by the gNSI.authz module + for each RPC separately."; + list rpc { + description + "A collection of counters collected by the gNSI.authz module + for a RPC identified by the `name`."; + key name; + leaf name { + type leafref { + path "../state/name"; + } + description + "The name of the RPC the counters were collected for."; + } + container state { + leaf name { + type string; + description + "The name of the RPC the counters were collected + for."; + } + uses counters; + } + } + } + } + grouping grpc-server-authz-policy-success-failure-counters { + description + "A collection of counters collected by the gNSI.authz module."; + container authz-policy-counters { + description + "A collection of counters collected by the gNSI.authz module."; + config false; + uses grpc-server-user-authz-policy-success-failure-counters; + } + } + grouping grpc-server-authz-policy-state { + description + "gNMI server's gRPC authorization policy freshness-related data."; + leaf grpc-authz-policy-version { + type version; + description + "The version of the gRPC authorization policy that is used by + this system."; + } + leaf grpc-authz-policy-created-on { + type created-on; + description + "The timestamp of the moment when the gRPC authorization policy + that is currently used by this system was created."; + } + } + // Augments section. + augment "/oc-sys:system/oc-sys:aaa/oc-sys:authorization/" + + "oc-sys:state" { + description + "A system's gRPC authorization policy freshness information."; + uses grpc-server-authz-policy-state; + } + augment "/oc-sys:system/oc-sys-grpc:grpc-servers/oc-sys-grpc:grpc-server" { + description + "Counters collected while evaluating access to a gRPC server using + the gNSI.authz authorization policy."; + uses grpc-server-authz-policy-success-failure-counters; + } +} diff --git a/models/yang/gnsi-certz.yang b/models/yang/gnsi-certz.yang new file mode 100644 index 000000000..fcc36375e --- /dev/null +++ b/models/yang/gnsi-certz.yang @@ -0,0 +1,187 @@ +module gnsi-certz { + yang-version 1.1; + namespace "https://github.com/openconfig/gnsi/certz/yang"; + prefix gnsi-certz; + + import openconfig-system { + prefix oc-sys; + } + import openconfig-system-grpc { + prefix oc-sys-grpc; + } + import openconfig-types { + prefix oc-types; + } + import openconfig-yang-types { + prefix oc-yang; + } + organization + "Google LLC"; + + contact + "Google LLC"; + + description + "This module provides a data model for the metadata of gRPC credentials + installed on a networking device."; + + revision 2023-02-13 { + description + "rename access/reject counters"; + reference "0.5.0"; + } + + revision 2023-08-24 { + description + "Adds ssl-profile-id leaf"; + reference "0.4.0"; + } + + revision 2023-05-10 { + description + "Adds authentication policy freshness information."; + reference "0.3.0"; + } + + revision 2022-10-30 { + description + "Adds success/failure counters."; + reference "0.2.0"; + } + + revision 2022-09-20 { + description + "Initial revision."; + reference "0.1.0"; + } + + typedef version { + type string; + description + "The version ID of the credential as provided by the credential + manager when the credential was pushed. This leaf persists through + a reboot."; + } + + typedef created-on { + type oc-types:timeticks64; + description + "The creation time of the credential as reported by the credential + manager when the credential was pushed to the device. This value is + reported as nanoseconds since epoch (January 1st, 1970 00:00:00 GMT). + This leaf persists through a reboot."; + } + // gRPC server related definitions. + // Success/failure counters. + grouping counters { + description + "A collection of counters that were collected while attempting + to establish connections to the gRPC server."; + + container counters { + config false; + description + "A collection of counters that were collected by the gRPC during + the authentication process."; + + leaf connection-rejects { + type oc-yang:counter64; + description + "The total number of times that gRPC clients have failed + in establishing a connection to the server."; + } + leaf last-connection-reject { + type oc-types:timeticks64; + description + "A timestamp of the last time a gRPC client failed + in establishing a connection to the server."; + } + leaf connection-accepts { + type oc-yang:counter64; + description + "The total number of times that gRPC clients have succeeded + in establishing a connection to the server."; + } + leaf last-connection-accept { + type oc-types:timeticks64; + description + "A timestamp of the last time a gRPC client succeeded + in establishing a connection to the server."; + } + } + } + + grouping grpc-server-credentials-state { + description + "gRPC server credentials freshness-related data."; + + leaf certificate-version { + type version; + description + "The version of the certificate (and associated + private key) that is used by this gRPC server."; + } + leaf certificate-created-on { + type created-on; + description + "The timestamp of the moment when the certificate + (and associated private key) that is currently used + by this gRPC server was created."; + } + leaf ca-trust-bundle-version { + type version; + description + "The version of the bundle of the Certificate + Authority certificates a.k.a. trust bundle used by + this gRPC server."; + } + leaf ca-trust-bundle-created-on { + type created-on; + description + "The timestamp of the moment when the bundle of + the Certificate Authority certificates (a.k.a. + trust bundle) was created."; + } + leaf certificate-revocation-list-bundle-version { + type version; + description + "The version of the Certificate Revocation List bundle used by + this gRPC server."; + } + leaf certificate-revocation-list-bundle-created-on { + type created-on; + description + "The timestamp of the moment when the Certificate Revocation + List bundle was created."; + } + leaf authentication-policy-version { + type version; + description + "The version of the authentication policy that is used by + this gRPC server."; + } + leaf authentication-policy-created-on { + type created-on; + description + "The timestamp of the moment when the authentication policy + that is currently used by this gRPC server was created."; + } + leaf ssl-profile-id { + type string; + description + "The ID of this gRPC server's SSL profile + as used by the gNSI Certz service"; + } + } + + // Augments section. + + augment "/oc-sys:system/oc-sys-grpc:grpc-servers/oc-sys-grpc:grpc-server/" + + "oc-sys-grpc:state" { + description + "A gRPC server credentials freshness information."; + + uses grpc-server-credentials-state; + uses counters; + } +} diff --git a/models/yang/gnsi-credentialz.yang b/models/yang/gnsi-credentialz.yang new file mode 100644 index 000000000..56f319e64 --- /dev/null +++ b/models/yang/gnsi-credentialz.yang @@ -0,0 +1,299 @@ +module gnsi-credentialz { + yang-version 1.1; + namespace "https://github.com/openconfig/gnsi/credentialz/yang"; + prefix gnsi-credz; + + import openconfig-system { + prefix oc-sys; + } + import openconfig-types { + prefix oc-types; + } + import openconfig-yang-types { + prefix oc-yang; + } + organization + "Google LLC"; + + contact + "Google LLC"; + + description + "This module provides a data model for the metadata of SSH and console + credentials installed on a networking device."; + + revision 2024-01-05 { + description + "Fix typo in YANG leaves"; + reference + "0.5.0"; + } + + revision 2023-10-03 { + description + "Added state leaves for admin-user"; + reference + "0.4.0"; + } + + revision 2023-08-18 { + description + "Fixed the canonical order of config field."; + reference + "0.3.0"; + } + + revision 2022-10-30 { + description + "Adds success/failure counters."; + reference + "0.2.0"; + } + + revision 2022-08-22 { + description + "Initial revision."; + reference + "0.1.0"; + } + + typedef version { + type string; + description + "The version ID of the credential as provided by the credential + manager when the credential was pushed. This leaf persists through + a reboot."; + } + + typedef created-on { + type oc-types:timeticks64; + description + "The creation time of the credential as reported by the credential + manager when the credential was pushed to the device. This value is + reported as nanoseconds since epoch (January 1st, 1970 00:00:00 GMT). + This leaf persists through a reboot."; + } + + // SSH server related definitions. + grouping ssh-server-credentials-version { + description + "SSH server credentials freshness-related data."; + + leaf active-trusted-user-ca-keys-version { + type version; + description + "The version of the Certificate Authority keys."; + } + + leaf active-trusted-user-ca-keys-created-on { + type created-on; + description + "The timestamp of the moment when the trusted user CA keys + were created."; + } + + leaf active-host-certificate-version { + type version; + description + "The version of the host certificate."; + } + + leaf active-host-certificate-created-on { + type created-on; + description + "The timestamp of the moment when the host certificate + was created."; + } + + leaf active-host-key-version { + type version; + description + "The version of the host public key."; + } + + leaf active-host-key-created-on { + type created-on; + description + "The timestamp of the moment when the host key was + created."; + } + } + + // Success/failure counters. + grouping counters { + description + "A collection of counters that were collected while evaluating + access to the target."; + + container counters { + config false; + description + "A collection of counters collected while authorizing users + accessing the target."; + leaf access-rejects { + type oc-yang:counter64; + description + "The total number of times access to the target has been + denied."; + } + leaf last-access-reject { + type oc-types:timeticks64; + description + "A timestamp of the last time access to the target has been + denied."; + } + leaf access-accepts { + type oc-yang:counter64; + description + "The total number of times access to the target has been + allowed."; + } + leaf last-access-accept { + type oc-types:timeticks64; + description + "A timestamp of the last time access to the target has been + allowed."; + } + } + } + + // GLOME related definitions. + + grouping glome-key-version { + description + "Version identifier for the configured GLOME key."; + + leaf active-glome-key-version { + type version; + description + "The version of the GLOME key."; + } + + leaf active-glome-key-created-on { + type created-on; + description + "The timestamp of the moment when the GLOME key + was created."; + } + } + + // System role SSH related definitions. + grouping user-ssh-credentials-version { + description + "System role credentials freshness-related data."; + + leaf authorized-principals-list-version { + type version; + description + "The version of the list of authorized principals currently + associated with this system role."; + } + + leaf authorized-principals-list-created-on { + type created-on; + description + "The timestamp of the moment the currently used list of + authorized principals has been created."; + } + + leaf authorized-keys-list-version { + type version; + description + "The version of the list of authorized keys that is currently + associated with this system role."; + } + + leaf authorized-keys-list-created-on { + type created-on; + description + "The timestamp of the moment the currently used list of + authorized keys has been created."; + } + } + + grouping console-config-state { + description + "Console-related configuration and state."; + container console { + description + "Console-related configuration and state."; + + container config { + description + "Console-related configuration."; + } + + container state { + config false; + description + "Console-related state."; + + uses counters; + + leaf enabled { + type boolean; + description + "Whether GLOME is enabled or not."; + } + } + } + } + // System role console related definitions. + grouping user-console-credentials-version { + description + "System role credentials freshness-related data."; + + leaf password-version { + type version; + description + "The version of the password that is currently used to + authenticate this user account."; + } + + leaf password-created-on { + type created-on; + description + "The timestamp of the moment the currently used password has + been created."; + } + } + + // Augments section. + augment "/oc-sys:system" { + description + "Console credentials freshness data."; + + uses console-config-state; + } + augment "/oc-sys:system/oc-sys:ssh-server/oc-sys:state" { + description + "SSH server credentials freshness data."; + + uses ssh-server-credentials-version; + uses counters; + } + augment "/oc-sys:system/oc-sys:aaa/oc-sys:authentication/oc-sys:users/" + + "oc-sys:user/oc-sys:state" { + description + "A system role credentials freshness information."; + + uses user-console-credentials-version; + uses user-ssh-credentials-version; + } + augment "/oc-sys:system/oc-sys:aaa/oc-sys:authentication/oc-sys:admin-user/" + + "oc-sys:state" { + description + "A system role credentials freshness information."; + + uses user-console-credentials-version; + uses user-ssh-credentials-version; + } + deviation "/oc-sys:system/oc-sys:aaa/oc-sys:authentication/oc-sys:users/" + + "oc-sys:user/oc-sys:config/oc-sys:ssh-key" { + deviate not-supported; + } + deviation "/oc-sys:system/oc-sys:aaa/oc-sys:authentication/oc-sys:users/" + + "oc-sys:user/oc-sys:state/oc-sys:ssh-key" { + deviate not-supported; + } +} diff --git a/models/yang/gnsi-pathz.yang b/models/yang/gnsi-pathz.yang new file mode 100644 index 000000000..ca9fa9239 --- /dev/null +++ b/models/yang/gnsi-pathz.yang @@ -0,0 +1,298 @@ +module gnsi-pathz { + yang-version 1.1; + namespace "https://github.com/openconfig/gnsi/pathz/yang"; + prefix gnsi-pathz; + + import openconfig-system { + prefix oc-sys; + } + import openconfig-system-grpc { + prefix oc-sys-grpc; + } + import openconfig-types { + prefix oc-types; + } + import openconfig-yang-types { + prefix oc-yang; + } + + organization + "Google LLC"; + + contact + "Google LLC"; + + description + "This module provides a data model for the metadata of + OpenConfig-path-based authorization policies installed on a networking + device."; + + revision 2022-10-30 { + description + "Adds success/failure counters."; + reference "0.2.0"; + } + + revision 2022-01-17 { + description + "Initial revision."; + reference "0.1.0"; + } + + typedef version { + type string; + description + "The version ID of the OpenConfig-path-based authorization policy + as provided by the OpenConfig-path-based Authorization Policy + Manager when the policy was pushed. This leaf persists through + a reboot."; + } + + typedef created-on { + type oc-types:timeticks64; + description + "The creation time of the OpenConfig-path-based authorization policy + as reported by the OpenConfig-path-based Authorization Policy + manager when the policy was pushed to the device. This value is + reported as nanoseconds since epoch (January 1st, 1970 00:00:00 GMT). + This leaf persists through a reboot."; + } + + // gRPC server related definitions. + grouping counters { + description + "A collection of counters that were collected by the gNSI.pathz + module while evaluating access to an OpenConfig path."; + + leaf access-rejects { + type oc-yang:counter64; + description + "The total number of times the gNSI.pathz module denied access + to an OpenConfig path."; + } + leaf last-access-reject { + type oc-types:timeticks64; + description + "A timestamp of the last time the gNSI.pathz denied access to + an OpenConfig path"; + } + leaf access-accepts { + type oc-yang:counter64; + description + "The total number of times the gNSI.pathz module allowed access + to an OpenConfig path."; + } + leaf last-access-accept { + type oc-types:timeticks64; + description + "A timestamp of the last time the gNSI.pathz allowed access to + an OpenConfig path"; + } + } + + grouping gnmi-pathz-policy-success-failure-counters { + description + "A collection of counters collected by the gNSI.pathz module."; + + container gnmi-pathz-policy-counters { + config false; + + uses gnmi-pathz-policy-xpath-success-failure-counters; + } + } + + grouping gnmi-pathz-policy-xpath-success-failure-counters { + description + "A collection of per-OpenConfig path counters."; + + container paths { + description + "A collection of per-OpenConfig path counters."; + + list path { + key xpath; + leaf xpath { + type leafref { + path "../state/xpath"; + } + description + "A OpenConfig schema path (xpath) the counter were + collected for."; + } + container state { + leaf xpath { + type string; + description + "A OpenConfig schema path (xpath) the counter were + collected for."; + } + container reads { + description + "The counter were collected while + performing a read operation on the + `xpath`."; + uses counters; + } + container writes { + description + "The counter were collected while + performing a write operation on the + `xpath`."; + uses counters; + } + } + } + } + } + + grouping grpc-server-gnmi-pathz-policy-state { + description + "gNMI server OpenConfig-path-based authorization policy + freshness-related data."; + + leaf gnmi-pathz-policy-version { + type version; + description + "The version of the OpenConfig-path-based authorization policy + that is used by this gNMI server."; + } + leaf gnmi-pathz-policy-created-on { + type created-on; + description + "The timestamp of the moment when the OpenConfig-path-based + authorization policy that is currently used by this gNMI server + was created."; + } + } + + grouping gnmi-pathz-policy-state { + description + "Operational state data for a gNMI OpenConfig-path-based + authorization policy."; + leaf instance { + type enumeration { + enum ACTIVE { + value 1; + description + "The policy that is currently used by the gNMI service + to authorize access."; + } + enum SANDBOX { + value 2; + description + "The most recent policy that has been uploaded during + the Rotation() RPC. If there is no Rotate() RPC in + progress, then referring to this instance of the policy + will result in an error."; + } + } + description + "The instance identifier of the gNMI OpenConfig-path-based + authorization policy."; + } + leaf version { + type version; + description + "The version of the gNMI OpenConfig-path-based authorization + policy."; + } + leaf created-on { + type created-on; + description + "The timestamp of the moment when the policy was + created."; + } + } + + grouping gnmi-pathz-policies { + description + "Collection of OpenConfig-path-based authorization policies that + have been installed on the device using the gNSI OpenConfig-path- + based authorization policy management service. + Each OpenConfig-path-based authorization policy listed here is + identified by its status (either ACTIVE or SANDBOX) and has its + version and creation date/time listed."; + + container policies { + config false; + description + "Information about freshness of an OpenConfig-path-based + authorization policy that have been installed + on the device using the gNSI OpenConfig-path-based + authorization policy management service."; + + list policy { + key instance; + ordered-by system; + description + "Information about the OpenConfig-path-based authorization + policy that is identified by the `instance`."; + leaf instance { + type leafref { + path "../state/instance"; + } + description + "The ID of the OpenConfig-path-based authorization + policy."; + } + container state { + description + "Operational state data for an OpenConfig-path-based + authorization policies."; + + uses gnmi-pathz-policy-state; + } + } + } + } + + grouping system-gnmi-pathz-policies { + description + "Collection of OpenConfig-path-based authorization policies that + have been installed on the device using the gNSI OpenConfig-path- + based authorization policy management service. + Each policy listed here is identified by its status (either ACTIVE + or SANDBOX) and has its version and creation date/time listed."; + + container gnmi-pathz-policies { + config false; + description + "Collection of OpenConfig-path-based authorization policies that + have been installed on the device using the gNSI OpenConfig- + path-based authorization policy management service. + Each policy listed here is identified by its status (either + ACTIVE or SANDBOX) and has its version and creation date/time + listed."; + + uses gnmi-pathz-policies; + } + } + + // Augments section. + + augment "/oc-sys:system" { + description + "Collection of OpenConfig-path-based authorization policies that + have been installed on the device using the gNSI OpenConfig-path- + based authorization policy management service. + Each policy listed here is identified by its status (either ACTIVE + or SANDBOX) and has its version and creation date/time listed."; + + uses system-gnmi-pathz-policies; + } + augment "/oc-sys:system/oc-sys-grpc:grpc-servers/oc-sys-grpc:grpc-server/" + + "oc-sys-grpc:state" { + description + "A gNMI server OpenConfig-path-based authorization policy freshness + information."; + + uses grpc-server-gnmi-pathz-policy-state; + } + augment "/oc-sys:system/oc-sys-grpc:grpc-servers/oc-sys-grpc:grpc-server" { + description + "A gNMI server OpenConfig-path-based authorization policy + success/failure counters."; + + uses gnmi-pathz-policy-success-failure-counters; + } +} From b9e4436832c16b140044998598431a1e87d91646 Mon Sep 17 00:00:00 2001 From: kanchanavelusamy Date: Tue, 23 Sep 2025 20:44:54 +0000 Subject: [PATCH 2/2] Implement translib backend logic for the gNSI Authz This commit adds the necessary transformer code to read authorization policy counters and related state from the Redis databases. --- config/transformer/models_list | 1 + .../annotations/openconfig-system-annot.yang | 25 ++ translib/db/db_redis_opts.go | 2 +- translib/db/rcm.go | 196 +++++++++ translib/tlerr/tlerr.go | 31 ++ translib/transformer/subscribe_req_xlate.go | 2 +- translib/transformer/xfmr_interface.go | 9 +- translib/transformer/xfmr_system.go | 412 ++++++++++++++++++ 8 files changed, 672 insertions(+), 6 deletions(-) create mode 100644 models/yang/annotations/openconfig-system-annot.yang create mode 100644 translib/db/rcm.go create mode 100644 translib/transformer/xfmr_system.go diff --git a/config/transformer/models_list b/config/transformer/models_list index 133a3bfa3..d615223a0 100644 --- a/config/transformer/models_list +++ b/config/transformer/models_list @@ -15,3 +15,4 @@ gnsi-authz.yang gnsi-pathz.yang gnsi-certz.yang gnsi-credentialz.yang +openconfig-system-annot.yang diff --git a/models/yang/annotations/openconfig-system-annot.yang b/models/yang/annotations/openconfig-system-annot.yang new file mode 100644 index 000000000..8c249a7ad --- /dev/null +++ b/models/yang/annotations/openconfig-system-annot.yang @@ -0,0 +1,25 @@ +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:aaa/oc-sys:authorization/oc-sys:state { +      deviate add { +        sonic-ext:db-name "STATE_DB"; +        sonic-ext:subtree-transformer "authz_policy_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/db/db_redis_opts.go b/translib/db/db_redis_opts.go index 023fd6072..27aa1193e 100644 --- a/translib/db/db_redis_opts.go +++ b/translib/db/db_redis_opts.go @@ -144,7 +144,7 @@ func adjustRedisOpts(dbOpt *Options) *redis.Options { return &redisOpts } -func init() { +func initializeRedisOpts() { flag.StringVar(&goRedisOpts, "go_redis_opts", "", "Options for go-redis") } diff --git a/translib/db/rcm.go b/translib/db/rcm.go new file mode 100644 index 000000000..6d0bba320 --- /dev/null +++ b/translib/db/rcm.go @@ -0,0 +1,196 @@ +package db + +import ( + "flag" + "fmt" + "sync" + "sync/atomic" + + "github.com/go-redis/redis/v7" + log "github.com/golang/glog" +) + +var usePools = flag.Bool("use_connection_pools", true, "use connection pools for Redis Clients") + +const ( + POOL_SIZE = 25 +) + +var rcm *redisClientManager +var initMu = &sync.Mutex{} + +type redisClientManager struct { + // clients holds one Redis Client for each DBNum + clients [MaxDB + 1]*redis.Client + mu *sync.RWMutex + curTransactionalClients atomic.Int32 + totalPoolClientsRequested atomic.Uint64 + totalTransactionalClientsRequested atomic.Uint64 +} + +type RedisCounters struct { + CurTransactionalClients uint32 // The number of transactional clients currently opened. + TotalPoolClientsRequested uint64 // The total number of Redis Clients using a connection pool requested. + TotalTransactionalClientsRequested uint64 // The total number of Transactional Redis Clients requested. + PoolStatsPerDB map[string]*redis.PoolStats // The pool counters for each Redis Client in the cache. +} + +func init() { + initializeRedisOpts() + initializeRedisClientManager() +} + +func initializeRedisClientManager() { + initMu.Lock() + defer initMu.Unlock() + if rcm != nil { + return + } + rcm = &redisClientManager{ + clients: [MaxDB + 1]*redis.Client{}, + mu: &sync.RWMutex{}, + curTransactionalClients: atomic.Int32{}, + totalPoolClientsRequested: atomic.Uint64{}, + totalTransactionalClientsRequested: atomic.Uint64{}, + } + rcm.mu.Lock() + defer rcm.mu.Unlock() + for dbNum := DBNum(0); dbNum < MaxDB; dbNum++ { + if len(getDBInstName(dbNum)) == 0 { + continue + } + // Create a Redis Client for each database. + rcm.clients[int(dbNum)] = createRedisClient(dbNum, POOL_SIZE) + } +} + +func createRedisClient(db DBNum, poolSize int) *redis.Client { + opts := adjustRedisOpts(&Options{DBNo: db}) + opts.PoolSize = poolSize + client := redis.NewClient(opts) + if _, err := client.Ping().Result(); err != nil { + log.V(0).Infof("RCM error during Redis Client creation for DBNum=%v: %v", db, err) + } + return client +} + +func createRedisClientWithOpts(opts *redis.Options) *redis.Client { + client := redis.NewClient(opts) + if _, err := client.Ping().Result(); err != nil { + log.V(0).Infof("RCM error during Redis Client creation for DBNum=%v: %v", opts.DB, err) + } + return client +} + +func getClient(db DBNum) *redis.Client { + rcm.mu.RLock() + defer rcm.mu.RUnlock() + return rcm.clients[int(db)] +} + +// RedisClient will return a Redis Client that can be used for non-transactional Redis operations. +// The client returned by this function is shared among many DB readers/writers and uses +// a connection pool. For transactional Redis operations, please use GetRedisClientForTransaction(). +func RedisClient(db DBNum) *redis.Client { + if rcm == nil { + initializeRedisClientManager() + } + if !(*usePools) { // Connection Pooling is disabled. + return TransactionalRedisClient(db) + } + if len(getDBInstName(db)) == 0 { + log.V(0).Infof("Invalid DBNum requested: %v", db) + return nil + } + rcm.totalPoolClientsRequested.Add(1) + rc := getClient(db) + if rc == nil { + log.V(0).Infof("RCM Redis client for DBNum=%v is nil!", db) + rcm.mu.Lock() + defer rcm.mu.Unlock() + if rc = rcm.clients[int(db)]; rc != nil { + return rc + } + rc = createRedisClient(db, POOL_SIZE) + rcm.clients[int(db)] = rc + } + return rc +} + +// TransactionalRedisClient will create and return a unique Redis client. This client can be used +// for transactional operations. These operations include MULTI, PSUBSCRIBE (PubSub), and SCAN. This +// client must be closed using CloseRedisClient when it is no longer needed. +func TransactionalRedisClient(db DBNum) *redis.Client { + if rcm == nil { + initializeRedisClientManager() + } + if len(getDBInstName(db)) == 0 { + log.V(0).Infof("Invalid DBNum requested: %v", db) + return nil + } + rcm.totalTransactionalClientsRequested.Add(1) + client := createRedisClient(db, 1) + rcm.curTransactionalClients.Add(1) + return client +} + +func TransactionalRedisClientWithOpts(opts *redis.Options) *redis.Client { + if rcm == nil { + initializeRedisClientManager() + } + rcm.totalTransactionalClientsRequested.Add(1) + opts.PoolSize = 1 + client := createRedisClientWithOpts(opts) + rcm.curTransactionalClients.Add(1) + return client +} + +// CloseUniqueRedisClient will close the Redis client that is passed in. +func CloseRedisClient(rc *redis.Client) error { + if rcm == nil { + return fmt.Errorf("RCM is nil when trying to close Redis Client: %v", rc) + } + if rc == nil { + return nil + } + // Closing a Redis Client with a connection pool is a no-op because these clients need to stay open. + if !IsTransactionalClient(rc) { + return nil + } + if err := rc.Close(); err != nil { + return err + } + rcm.curTransactionalClients.Add(-1) + return nil +} + +// IsTransactionalClient returns true if rc is a transactional client and false otherwise. +func IsTransactionalClient(rc *redis.Client) bool { + if rc == nil { + return false + } + return rc.Options().PoolSize == 1 +} + +// RedisClientManagerCounters returns the counters stored in the RCM. +func RedisClientManagerCounters() *RedisCounters { + if rcm == nil { + initializeRedisClientManager() + } + counters := &RedisCounters{ + CurTransactionalClients: uint32(rcm.curTransactionalClients.Load()), + TotalPoolClientsRequested: rcm.totalPoolClientsRequested.Load(), + TotalTransactionalClientsRequested: rcm.totalTransactionalClientsRequested.Load(), + PoolStatsPerDB: map[string]*redis.PoolStats{}, + } + rcm.mu.RLock() + defer rcm.mu.RUnlock() + for db, client := range rcm.clients { + dbName := getDBInstName(DBNum(db)) + if dbName == "" || client == nil { + continue + } + counters.PoolStatsPerDB[dbName] = client.PoolStats() + } + return counters +} diff --git a/translib/tlerr/tlerr.go b/translib/tlerr/tlerr.go index 93bdbeb05..5e6834414 100644 --- a/translib/tlerr/tlerr.go +++ b/translib/tlerr/tlerr.go @@ -31,7 +31,9 @@ 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" @@ -190,3 +192,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/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..bc11d3574 --- /dev/null +++ b/translib/transformer/xfmr_system.go @@ -0,0 +1,412 @@ +package transformer + +import ( + "errors" + "fmt" + "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" + ygot "github.com/openconfig/ygot/ygot" +) + +const ( + AUTHZ_TBL = "AUTHZ_TABLE" + ACCEPTS = "permitted" + REJECTS = "denied" + GNXI_ID = "gnxi" + cntResult = "cntResult" + tsResult = "tsResult" + + /** Credential Tables **/ + CREDENTIALS_TBL = "CREDENTIALS" + CRED_AUTHZ_TBL = "CREDENTIALS|AUTHZ_POLICY" + CERT_TBL = "CREDENTIALS|CERT" + + /** 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" +) + +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_authz_policy_xfmr", DbToYang_authz_policy_xfmr) + XlateFuncBind("Subscribe_authz_policy_xfmr", Subscribe_authz_policy_xfmr) +} + +type certData struct { + version string + created uint64 +} + +type grpcState struct { + name string + certVersion string + certCreated uint64 + caVersion string + caCreated uint64 + crlVersion string + crlCreated uint64 + authPolVersion string + authPolCreated uint64 + pathzVersion string + pathzCreated uint64 + profileId string +} + +func getAppRootObject(inParams XfmrParams) *ocbinds.OpenconfigSystem_System { + deviceObj := (*inParams.ygRoot).(*ocbinds.Device) + return deviceObj.System +} + +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_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") + 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} + } + + 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] + 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) + } + } + + 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 + + // 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 +} + +func getAuthzPolicyCounter(authzTables db.Table, server string, rpcString string) map[string]map[string]*uint64 { + cntMap := make(map[string]*uint64) + tsMap := make(map[string]*uint64) + + 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 +} + +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 fanthom key. Ignore it. + continue + } + ret = append(ret, key.Comp[2]) + } + log.V(3).Infof("keys: %v", ret) + return ret, nil +}