[PCF] Add metrics support

Expose metrics with labels according to ETSI TS 128 552 V16.13.0 in
PCF by using hash.

The metrics are named respecting the rule:
<generation>_<measurement_object_class>_<measurement_family_name>_<metric_name_as_in_TS_128_552>

Since slice itself is not unique, the plmnid label is exposed in
addition to snssai.

AM policy:
fivegs_pcffunction_pa_policyamassoreq and
fivegs_pcffunction_pa_policyamassosucc do not expose snssai label
since it is not available at the time of exposure.
plmnid is defined during AM policy processing, so not to lose the
difference to ...succ, the basic metric
fivegs_pcffunction_pa_policyamassoreq is preserved.

SM policy:
snssai is defined during SM policy processing, so not to lose the
difference to ...succ, the basic metric
fivegs_pcffunction_pa_policysmassoreq is preserved.

Those 2 basic metrics retain their position but are exposed with empty
labels.
Metrics with labels are called later, when the label values are known.

Exposed metrics example:
-standard counters:
fivegs_pcffunction_pa_policyamassoreq{plmnid=""} 3
fivegs_pcffunction_pa_policyamassoreq{plmnid="99970"} 3
fivegs_pcffunction_pa_policyamassosucc{plmnid="99970"} 3
fivegs_pcffunction_pa_policysmassoreq{plmnid="",snssai=""} 3
fivegs_pcffunction_pa_policysmassoreq{plmnid="99970",snssai="1000009"} 3
fivegs_pcffunction_pa_policysmassosucc{plmnid="99970",snssai="1000009"} 3

-nonstandard gauge (added for controlling purposes -
same metric as existing metric on AMF and SMF):
fivegs_pcffunction_pa_sessionnbr{plmnid="99970",snssai="1000009"} 0
This commit is contained in:
Gaber Stare 2022-08-18 10:20:26 +00:00 committed by Sukchan Lee
parent 40b4c9d1c9
commit 0c2a3b0f39
17 changed files with 452 additions and 1 deletions

View File

@ -200,10 +200,20 @@ tls:
# o Don't use SCP server => App fails if no NRF available.
# delegated: no
#
# <Metrics Server>
#
# o Metrics Server(http://<any address>:9090)
# metrics:
# - addr: 0.0.0.0
# port: 9090
#
pcf:
sbi:
- addr: 127.0.0.13
port: 7777
metrics:
- addr: 127.0.0.13
port: 9090
#
# scp:

View File

@ -263,6 +263,9 @@ pcf:
sbi:
- addr: 127.0.0.13
port: 7777
metrics:
- addr: 127.0.0.13
port: 9090
nssf:
sbi:

View File

@ -228,6 +228,9 @@ pcf:
sbi:
- addr: 127.0.0.13
port: 7777
metrics:
- addr: 127.0.0.13
port: 9090
nssf:
sbi:

View File

@ -224,6 +224,9 @@ pcf:
sbi:
- addr: 127.0.0.13
port: 7777
metrics:
- addr: 127.0.0.13
port: 9090
nssf:
sbi:

View File

@ -231,6 +231,9 @@ pcf:
sbi:
- addr: 127.0.0.13
port: 7777
metrics:
- addr: 127.0.0.13
port: 9090
nssf:
sbi:

View File

@ -234,6 +234,9 @@ pcf:
sbi:
- addr: 127.0.0.13
port: 7777
metrics:
- addr: 127.0.0.13
port: 9090
nssf:
sbi:

View File

@ -939,3 +939,17 @@ void ogs_sbi_free_qos_data(OpenAPI_qos_data_t *QosData)
ogs_free(QosData);
}
char *ogs_sbi_s_nssai_to_string_plain(ogs_s_nssai_t *s_nssai)
{
ogs_assert(s_nssai);
if (s_nssai->sd.v !=
OGS_S_NSSAI_NO_SD_VALUE) {
return ogs_msprintf("%d%06x",
s_nssai->sst,
s_nssai->sd.v);
} else {
return ogs_msprintf("%d",
s_nssai->sst);
}
}

View File

@ -88,6 +88,7 @@ OpenAPI_pcc_rule_t *ogs_sbi_build_pcc_rule(
void ogs_sbi_free_pcc_rule(OpenAPI_pcc_rule_t *PccRule);
OpenAPI_qos_data_t *ogs_sbi_build_qos_data(ogs_pcc_rule_t *pcc_rule);
void ogs_sbi_free_qos_data(OpenAPI_qos_data_t *QosData);
char *ogs_sbi_s_nssai_to_string_plain(ogs_s_nssai_t *s_nssai);
#ifdef __cplusplus
}

View File

@ -121,6 +121,8 @@ int pcf_context_parse_config(void)
/* handle config in sbi library */
} else if (!strcmp(pcf_key, "discovery")) {
/* handle config in sbi library */
} else if (!strcmp(pcf_key, "metrics")) {
/* handle config in metrics library */
} else
ogs_warn("unknown key `%s`", pcf_key);
}
@ -318,6 +320,11 @@ void pcf_sess_remove(pcf_sess_t *sess)
OpenAPI_subscribed_default_qos_free(sess->subscribed_default_qos);
ogs_pool_free(&pcf_sess_pool, sess);
if (sess->s_nssai.sst != 0) {
pcf_metrics_inst_by_slice_add(&sess->pcf_ue->guami.plmn_id,
&sess->s_nssai, PCF_METR_GAUGE_PA_SESSIONNBR, -1);
}
}
void pcf_sess_remove_all(pcf_ue_t *pcf_ue)

View File

@ -26,6 +26,7 @@
#include "ogs-dbi.h"
#include "pcf-sm.h"
#include "metrics.h"
#ifdef __cplusplus
extern "C" {

View File

@ -18,6 +18,7 @@
*/
#include "sbi-path.h"
#include "metrics.h"
static ogs_thread_t *thread;
static void pcf_main(void *data);
@ -28,15 +29,21 @@ int pcf_initialize()
int rv;
ogs_sbi_context_init();
ogs_metrics_context_init();
pcf_context_init();
rv = ogs_sbi_context_parse_config("pcf", "nrf", "scp");
if (rv != OGS_OK) return rv;
rv = ogs_metrics_context_parse_config("pcf");
if (rv != OGS_OK) return rv;
rv = pcf_context_parse_config();
if (rv != OGS_OK) return rv;
rv = pcf_metrics_open();
if (rv != 0) return OGS_ERROR;
rv = ogs_log_config_domain(
ogs_app()->logger.domain, ogs_app()->logger.level);
if (rv != OGS_OK) return rv;
@ -90,6 +97,8 @@ void pcf_terminate(void)
ogs_dbi_final();
pcf_context_final();
pcf_metrics_close();
ogs_sbi_context_final();
}

View File

@ -17,6 +17,7 @@
libpcf_sources = files('''
context.c
metrics.c
event.c
nnrf-handler.c
@ -44,12 +45,14 @@ libpcf_sources = files('''
libpcf = static_library('pcf',
sources : libpcf_sources,
dependencies : [libdbi_dep,
libmetrics_dep,
libsbi_dep],
install : false)
libpcf_dep = declare_dependency(
link_with : libpcf,
dependencies : [libdbi_dep,
libmetrics_dep,
libsbi_dep])
pcf_sources = files('''

320
src/pcf/metrics.c Normal file
View File

@ -0,0 +1,320 @@
#include "ogs-app.h"
#include "context.h"
#include "metrics.h"
typedef struct pcf_metrics_spec_def_s {
unsigned int type;
const char *name;
const char *description;
int initial_val;
unsigned int num_labels;
const char **labels;
} pcf_metrics_spec_def_t;
/* Helper generic functions: */
static int pcf_metrics_init_inst(ogs_metrics_inst_t **inst,
ogs_metrics_spec_t **specs, unsigned int len,
unsigned int num_labels, const char **labels)
{
unsigned int i;
for (i = 0; i < len; i++)
inst[i] = ogs_metrics_inst_new(specs[i], num_labels, labels);
return OGS_OK;
}
static int pcf_metrics_free_inst(ogs_metrics_inst_t **inst,
unsigned int len)
{
unsigned int i;
for (i = 0; i < len; i++)
ogs_metrics_inst_free(inst[i]);
memset(inst, 0, sizeof(inst[0]) * len);
return OGS_OK;
}
static int pcf_metrics_init_spec(ogs_metrics_context_t *ctx,
ogs_metrics_spec_t **dst, pcf_metrics_spec_def_t *src, unsigned int len)
{
unsigned int i;
for (i = 0; i < len; i++) {
dst[i] = ogs_metrics_spec_new(ctx, src[i].type,
src[i].name, src[i].description,
src[i].initial_val, src[i].num_labels, src[i].labels);
}
return OGS_OK;
}
/* GLOBAL */
ogs_metrics_spec_t *pcf_metrics_spec_global[_PCF_METR_GLOB_MAX];
ogs_metrics_inst_t *pcf_metrics_inst_global[_PCF_METR_GLOB_MAX];
pcf_metrics_spec_def_t pcf_metrics_spec_def_global[_PCF_METR_GLOB_MAX] = {
/* Global Counters: */
/* Global Gauges: */
};
static int pcf_metrics_init_inst_global(void)
{
return pcf_metrics_init_inst(pcf_metrics_inst_global,
pcf_metrics_spec_global, _PCF_METR_GLOB_MAX, 0, NULL);
}
static int pcf_metrics_free_inst_global(void)
{
return pcf_metrics_free_inst(pcf_metrics_inst_global, _PCF_METR_GLOB_MAX);
}
/* BY_PLMN */
const char *labels_plmn[] = {
"plmnid"
};
#define PCF_METR_BY_PLMN_CTR_ENTRY(_id, _name, _desc) \
[_id] = { \
.type = OGS_METRICS_METRIC_TYPE_COUNTER, \
.name = _name, \
.description = _desc, \
.num_labels = OGS_ARRAY_SIZE(labels_plmn), \
.labels = labels_plmn, \
},
ogs_metrics_spec_t *pcf_metrics_spec_by_plmn[_PCF_METR_BY_PLMN_MAX];
ogs_hash_t *metrics_hash_by_plmn = NULL; /* hash table for PLMN label */
pcf_metrics_spec_def_t pcf_metrics_spec_def_by_plmn[_PCF_METR_BY_PLMN_MAX] = {
/* Counters: */
PCF_METR_BY_PLMN_CTR_ENTRY(
PCF_METR_CTR_PA_POLICYAMASSOREQ,
"fivegs_pcffunction_pa_policyamassoreq",
"Number of AM policy association requests")
PCF_METR_BY_PLMN_CTR_ENTRY(
PCF_METR_CTR_PA_POLICYAMASSOSUCC,
"fivegs_pcffunction_pa_policyamassosucc",
"Number of successful AM policy associations")
};
void pcf_metrics_init_by_plmn(void);
int pcf_metrics_free_inst_by_plmn(ogs_metrics_inst_t **inst);
typedef struct pcf_metric_key_by_plmn_s {
ogs_plmn_id_t plmn_id;
pcf_metric_type_by_plmn_t t;
} pcf_metric_key_by_plmn_t;
void pcf_metrics_init_by_plmn(void)
{
metrics_hash_by_plmn = ogs_hash_make();
ogs_assert(metrics_hash_by_plmn);
}
void pcf_metrics_inst_by_plmn_add(ogs_plmn_id_t *plmn,
pcf_metric_type_by_plmn_t t, int val)
{
ogs_metrics_inst_t *metrics = NULL;
pcf_metric_key_by_plmn_t *plmn_key;
plmn_key = ogs_calloc(1, sizeof(*plmn_key));
ogs_assert(plmn_key);
if (plmn) {
plmn_key->plmn_id = *plmn;
}
plmn_key->t = t;
metrics = ogs_hash_get(metrics_hash_by_plmn,
plmn_key, sizeof(*plmn_key));
if (!metrics) {
char plmn_id[OGS_PLMNIDSTRLEN] = "";
if (plmn) {
ogs_plmn_id_to_string(plmn, plmn_id);
}
metrics = ogs_metrics_inst_new(pcf_metrics_spec_by_plmn[t],
pcf_metrics_spec_def_by_plmn->num_labels,
(const char *[]){ plmn_id });
ogs_assert(metrics);
ogs_hash_set(metrics_hash_by_plmn,
plmn_key, sizeof(*plmn_key), metrics);
} else {
ogs_free(plmn_key);
}
ogs_metrics_inst_add(metrics, val);
}
int pcf_metrics_free_inst_by_plmn(ogs_metrics_inst_t **inst)
{
return pcf_metrics_free_inst(inst, _PCF_METR_BY_PLMN_MAX);
}
/* BY_SLICE */
const char *labels_slice[] = {
"plmnid",
"snssai"
};
#define PCF_METR_BY_SLICE_CTR_ENTRY(_id, _name, _desc) \
[_id] = { \
.type = OGS_METRICS_METRIC_TYPE_COUNTER, \
.name = _name, \
.description = _desc, \
.num_labels = OGS_ARRAY_SIZE(labels_slice), \
.labels = labels_slice, \
},
#define PCF_METR_BY_SLICE_GAUGE_ENTRY(_id, _name, _desc) \
[_id] = { \
.type = OGS_METRICS_METRIC_TYPE_GAUGE, \
.name = _name, \
.description = _desc, \
.num_labels = OGS_ARRAY_SIZE(labels_slice), \
.labels = labels_slice, \
},
ogs_metrics_spec_t *pcf_metrics_spec_by_slice[_PCF_METR_BY_SLICE_MAX];
ogs_hash_t *metrics_hash_by_slice = NULL; /* hash table for SLICE labels */
pcf_metrics_spec_def_t pcf_metrics_spec_def_by_slice[_PCF_METR_BY_SLICE_MAX] = {
/* Counters: */
PCF_METR_BY_SLICE_CTR_ENTRY(
PCF_METR_CTR_PA_POLICYSMASSOREQ,
"fivegs_pcffunction_pa_policysmassoreq",
"Number of SM policy association requests")
PCF_METR_BY_SLICE_CTR_ENTRY(
PCF_METR_CTR_PA_POLICYSMASSOSUCC,
"fivegs_pcffunction_pa_policysmassosucc",
"Number of successful SM policy associations")
/* Gauges: */
PCF_METR_BY_SLICE_GAUGE_ENTRY(
PCF_METR_GAUGE_PA_SESSIONNBR,
"fivegs_pcffunction_pa_sessionnbr",
"Active Sessions")
};
void pcf_metrics_init_by_slice(void);
int pcf_metrics_free_inst_by_slice(ogs_metrics_inst_t **inst);
typedef struct pcf_metric_key_by_slice_s {
ogs_plmn_id_t plmn_id;
ogs_s_nssai_t snssai;
pcf_metric_type_by_slice_t t;
} pcf_metric_key_by_slice_t;
void pcf_metrics_init_by_slice(void)
{
metrics_hash_by_slice = ogs_hash_make();
ogs_assert(metrics_hash_by_slice);
}
void pcf_metrics_inst_by_slice_add(ogs_plmn_id_t *plmn,
ogs_s_nssai_t *snssai, pcf_metric_type_by_slice_t t, int val)
{
ogs_metrics_inst_t *metrics = NULL;
pcf_metric_key_by_slice_t *slice_key;
slice_key = ogs_calloc(1, sizeof(*slice_key));
ogs_assert(slice_key);
if (plmn) {
slice_key->plmn_id = *plmn;
}
if (snssai) {
slice_key->snssai = *snssai;
} else {
slice_key->snssai.sst = 0;
slice_key->snssai.sd.v = OGS_S_NSSAI_NO_SD_VALUE;
}
slice_key->t = t;
metrics = ogs_hash_get(metrics_hash_by_slice,
slice_key, sizeof(*slice_key));
if (!metrics) {
char plmn_id[OGS_PLMNIDSTRLEN] = "";
char *s_nssai = NULL;
if (plmn) {
ogs_plmn_id_to_string(plmn, plmn_id);
}
if (snssai) {
s_nssai = ogs_sbi_s_nssai_to_string_plain(snssai);
} else {
s_nssai = ogs_strdup("");
}
metrics = ogs_metrics_inst_new(pcf_metrics_spec_by_slice[t],
pcf_metrics_spec_def_by_slice->num_labels,
(const char *[]){ plmn_id, s_nssai });
ogs_assert(metrics);
ogs_hash_set(metrics_hash_by_slice,
slice_key, sizeof(*slice_key), metrics);
if (s_nssai)
ogs_free(s_nssai);
} else {
ogs_free(slice_key);
}
ogs_metrics_inst_add(metrics, val);
}
int pcf_metrics_free_inst_by_slice(ogs_metrics_inst_t **inst)
{
return pcf_metrics_free_inst(inst, _PCF_METR_BY_SLICE_MAX);
}
int pcf_metrics_open(void)
{
ogs_metrics_context_t *ctx = ogs_metrics_self();
ogs_metrics_context_open(ctx);
pcf_metrics_init_spec(ctx, pcf_metrics_spec_global,
pcf_metrics_spec_def_global, _PCF_METR_GLOB_MAX);
pcf_metrics_init_spec(ctx, pcf_metrics_spec_by_plmn,
pcf_metrics_spec_def_by_plmn, _PCF_METR_BY_PLMN_MAX);
pcf_metrics_init_spec(ctx, pcf_metrics_spec_by_slice,
pcf_metrics_spec_def_by_slice, _PCF_METR_BY_SLICE_MAX);
pcf_metrics_init_inst_global();
pcf_metrics_init_by_plmn();
pcf_metrics_init_by_slice();
return 0;
}
int pcf_metrics_close(void)
{
ogs_hash_index_t *hi;
ogs_metrics_context_t *ctx = ogs_metrics_self();
pcf_metrics_free_inst_global();
if (metrics_hash_by_slice) {
for (hi = ogs_hash_first(metrics_hash_by_slice); hi; hi = ogs_hash_next(hi)) {
pcf_metric_key_by_slice_t *key =
(pcf_metric_key_by_slice_t *)ogs_hash_this_key(hi);
//void *val = ogs_hash_this_val(hi);
ogs_hash_set(metrics_hash_by_slice, key, sizeof(*key), NULL);
ogs_free(key);
/* don't free val (metric itself) -
* it will be free'd by ogs_metrics_context_final() */
//ogs_free(val);
}
ogs_hash_destroy(metrics_hash_by_slice);
}
if (metrics_hash_by_plmn) {
for (hi = ogs_hash_first(metrics_hash_by_plmn); hi; hi = ogs_hash_next(hi)) {
pcf_metric_key_by_plmn_t *key =
(pcf_metric_key_by_plmn_t *)ogs_hash_this_key(hi);
//void *val = ogs_hash_this_val(hi);
ogs_hash_set(metrics_hash_by_plmn, key, sizeof(*key), NULL);
ogs_free(key);
/* don't free val (metric ifself) -
* it will be free'd by ogs_metrics_context_final() */
//ogs_free(val);
}
ogs_hash_destroy(metrics_hash_by_plmn);
}
ogs_metrics_context_close(ctx);
return OGS_OK;
}

53
src/pcf/metrics.h Normal file
View File

@ -0,0 +1,53 @@
#ifndef PCF_METRICS_H
#define PCF_METRICS_H
#include "ogs-metrics.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum pcf_metric_type_global_s {
_PCF_METR_GLOB_MAX,
} pcf_metric_type_global_t;
extern ogs_metrics_inst_t *pcf_metrics_inst_global[_PCF_METR_GLOB_MAX];
static inline void pcf_metrics_inst_global_set(pcf_metric_type_global_t t, int val)
{ ogs_metrics_inst_set(pcf_metrics_inst_global[t], val); }
static inline void pcf_metrics_inst_global_add(pcf_metric_type_global_t t, int val)
{ ogs_metrics_inst_add(pcf_metrics_inst_global[t], val); }
static inline void pcf_metrics_inst_global_inc(pcf_metric_type_global_t t)
{ ogs_metrics_inst_inc(pcf_metrics_inst_global[t]); }
static inline void pcf_metrics_inst_global_dec(pcf_metric_type_global_t t)
{ ogs_metrics_inst_dec(pcf_metrics_inst_global[t]); }
/* BY_PLMN */
typedef enum pcf_metric_type_by_plmn_s {
PCF_METR_CTR_PA_POLICYAMASSOREQ = 0,
PCF_METR_CTR_PA_POLICYAMASSOSUCC,
_PCF_METR_BY_PLMN_MAX,
} pcf_metric_type_by_plmn_t;
void pcf_metrics_inst_by_plmn_add(
ogs_plmn_id_t *plmn, pcf_metric_type_by_plmn_t t, int val);
/* BY_SLICE */
typedef enum pcf_metric_type_by_slice_s {
PCF_METR_CTR_PA_POLICYSMASSOREQ = 0,
PCF_METR_CTR_PA_POLICYSMASSOSUCC,
PCF_METR_GAUGE_PA_SESSIONNBR,
_PCF_METR_BY_SLICE_MAX,
} pcf_metric_type_by_slice_t;
void pcf_metrics_inst_by_slice_add(
ogs_plmn_id_t *plmn, ogs_s_nssai_t *snssai,
pcf_metric_type_by_slice_t t, int val);
int pcf_metrics_open(void);
int pcf_metrics_close(void);
#ifdef __cplusplus
}
#endif
#endif /* PCF_METRICS_H */

View File

@ -384,6 +384,9 @@ bool pcf_nbsf_management_handle_register(
if (SmPolicyDecision.supp_feat)
ogs_free(SmPolicyDecision.supp_feat);
pcf_metrics_inst_by_slice_add(&sess->pcf_ue->guami.plmn_id,
&sess->s_nssai, PCF_METR_CTR_PA_POLICYSMASSOSUCC, 1);
ogs_session_data_free(&session_data);
return true;

View File

@ -35,6 +35,8 @@ bool pcf_npcf_am_policy_contrtol_handle_create(pcf_ue_t *pcf_ue,
OpenAPI_uri_scheme_e scheme = OpenAPI_uri_scheme_NULL;
ogs_sockaddr_t *addr = NULL;
pcf_metrics_inst_by_plmn_add(NULL, PCF_METR_CTR_PA_POLICYAMASSOREQ, 1);
ogs_assert(pcf_ue);
ogs_assert(stream);
ogs_assert(message);
@ -138,6 +140,9 @@ bool pcf_npcf_am_policy_contrtol_handle_create(pcf_ue_t *pcf_ue,
ogs_sbi_parse_guami(&pcf_ue->guami, PolicyAssociationRequest->guami);
}
pcf_metrics_inst_by_plmn_add(&pcf_ue->guami.plmn_id,
PCF_METR_CTR_PA_POLICYAMASSOREQ, 1);
if (PolicyAssociationRequest->rat_type)
pcf_ue->rat_type = PolicyAssociationRequest->rat_type;
@ -172,6 +177,8 @@ bool pcf_npcf_smpolicycontrol_handle_create(pcf_sess_t *sess,
OpenAPI_uri_scheme_e scheme = OpenAPI_uri_scheme_NULL;
ogs_sockaddr_t *addr = NULL;
pcf_metrics_inst_by_slice_add(NULL, NULL, PCF_METR_CTR_PA_POLICYSMASSOREQ, 1);
ogs_assert(sess);
pcf_ue = sess->pcf_ue;
ogs_assert(stream);
@ -292,6 +299,11 @@ bool pcf_npcf_smpolicycontrol_handle_create(pcf_sess_t *sess,
sess->s_nssai.sst = sliceInfo->sst;
sess->s_nssai.sd = ogs_s_nssai_sd_from_string(sliceInfo->sd);
pcf_metrics_inst_by_slice_add(&pcf_ue->guami.plmn_id,
&sess->s_nssai, PCF_METR_GAUGE_PA_SESSIONNBR, 1);
pcf_metrics_inst_by_slice_add(&pcf_ue->guami.plmn_id,
&sess->s_nssai, PCF_METR_CTR_PA_POLICYSMASSOREQ, 1);
if (SmPolicyContextData->subs_sess_ambr)
sess->subscribed_sess_ambr = OpenAPI_ambr_copy(
sess->subscribed_sess_ambr, SmPolicyContextData->subs_sess_ambr);

View File

@ -147,6 +147,9 @@ bool pcf_nudr_dr_handle_query_am_data(
ogs_subscription_data_free(&subscription_data);
pcf_metrics_inst_by_plmn_add(&pcf_ue->guami.plmn_id,
PCF_METR_CTR_PA_POLICYAMASSOSUCC, 1);
return true;
DEFAULT