Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

TLDR: Tracing has been added for A1 Policy Management Service. By default is disabled. To enable it change the flag in the application.yaml (More informations and tests at point 4)

...

 

Code Block
languageyml
themeMidnight
otel:
  sdk:
    disabled: false

or have an envaronment variable 
Code Block
languageyml
themeMidnight
ONAP_SDK_DISABLED=false
ONAP_TRACING_

...

SOUTHBOUND=true

...

https://opentelemetry.io/docs/zero-code/java/spring-boot-starter/out-of-the-box-instrumentation/#spring-webflux-autoconfiguration

Opentelemetry documentation provides a bean to mutate the default WebClient builder and to add tracing filters.

In our case the AsyncRestClient manually builds a WebClient for every asynchronous request.

The challenge was to add the tracing filters to this non-Spring class.

1.Adding OpenTelemetry Bean

    @Bean
    public OpenTelemetry openTelemetry() {
        return AutoConfiguredOpenTelemetrySdk.initialize().getOpenTelemetrySdk();
    }

...




Tracing Test

a) A docker compose with a1pms, a1-osc-simulator, and jaeger that acts as a collector and exporter

...

version: '3.7'
services:
  a1_policy_management:
    container_name: a1-pms
    image: onap/ccsdk-oran-a1policymanagementservice:1.7.0-SNAPSHOT
    ports:
      - "8433:8433"
      - "8081:8081"
    volumes:
      - ./application_configuration.json.nosdnc:/opt/app/policy-agent/

...

2. Adding filters into AsyncRestClient directly and not into builder bean, but the AutoConfiguredOpenTelemetrySdk uses by default parameters such as localhost:4317 to export grpc, so we opted for using the application.yaml parameters to build the exporters beans.

AsyncRestClient.java
		...
   data/application_configuration.json:ro
    networks:
      - jaeger-example
    depends_on:
      - jaeger
    environment:
     OpenTelemetry openTelemetry- ONAP_SDK_DISABLED= AutoConfiguredOpenTelemetrySdk.initialize().getOpenTelemetrySdk();
false
- ONAP_TRACING_SOUTHBOUND=true
var webfluxTelemetry = SpringWebfluxTelemetry.builder(openTelemetry).build(); - ONAP_OTEL_SAMPLER_JAEGER_REMOTE_ENDPOINT=http://jaeger:14250 return WebClient.builder() // ... - ONAP_OTEL_EXPORTER_ENDPOINT=http://jaeger:4317 - ONAP_OTEL_EXPORTER_PROTOCOL=grpc .filters(webfluxTelemetry::addClientTracingFilter) - ONAP_OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=grpc a1-sim-OSC: image: "nexus3.o-ran-sc.org:10002/o-ran-sc/a1-simulator:2.1.0" .build();

3. Context Provider class to use get the ApplicationContext into Non-Spring Components

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class ApplicationContextProvider implements ApplicationContextAware {container_name: a1-sim-OSC
    ports:
      - "30001:8085"
      - "30002:8185"
    environment:
    private static ApplicationContext context;- A1_VERSION=OSC_2.1.0
 
    @Override
 - REMOTE_HOSTS_LOGGING=1
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {- ALLOW_HTTP=true
    networks:
      - jaeger-example

 context = applicationContext;
jaeger:
     }image: jaegertracing/all-in-one:latest
 
    public static ApplicationContext getApplicationContext() {container_name: jaeger
    ports:
      - "16686:16686"
  return context;
    }
}

And then use var context = ApplicationContextProvider.getApplicationContext().getBean(OtelConfig.class); In the non Spring class, and if tracing is enabled to add the tracing filters.

4. Testing methods

a) A docker compose with a1pms, a1-osc-simulator, and jaeger that acts as a collector and exporter

version: '3.7'
services:
  a1_policy_management:
    container_name: a1-pms
    image: onap/ccsdk-oran-a1policymanagementservice:1.7.0-SNAPSHOT
    ports- "14250:14250"
      - "14268:14268"
      - "4317:4317"
      - "4318:4318"
    environment:
      - "8433:8433"LOG_LEVEL=debug
      - "8081:8081"COLLECTOR_OTLP_ENABLED=true
    volumesnetworks:
      - ./application_configuration.json.nosdnc:/opt/app/policy-agent/data/application_configuration.json:ro
    networks:
      -jaeger-example

networks:
  jaeger-example:
    depends_ondriver:
      - jaeger
    environment: bridge

...

b) The application_configuration.json.nosdnc in the same folder

{
    "description":"Application configuration",
      - ONAP_TRACING_ENABLED=true"config":{
      - ONAP_OTEL_SAMPLER_JAEGER_REMOTE_ENDPOINT=http://jaeger:14250 "ric":[
      - ONAP_OTEL_EXPORTER_ENDPOINT=http://jaeger:4317
    {
          - ONAP_OTEL_EXPORTER_PROTOCOL=grpc   "name":"ric1",
      - ONAP_OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=grpc

         "baseUrl":"https://a1-sim-OSC:8185/",
    image: "nexus3.o-ran-sc.org:10002/o-ran-sc/a1-simulator:2.1.0"
    container_name: a1-sim-OSC
    ports"managedElementIds":[
      - "30001:8085"
         - "30002:8185"kista_1",
    environment:
         - A1_VERSION=OSC_2.1.0   "kista_2"
      - REMOTE_HOSTS_LOGGING=1
      - ALLOW_HTTP=true]
    networks:
      - jaeger-example}

  jaeger:
    image: jaegertracing/all-in-one:latest ]
    container_name: jaeger
    ports:
      - "16686:16686"
      - "14250:14250"
      - "14268:14268"
      - "4317:4317"
      - "4318:4318"
    environment:
      - LOG_LEVEL=debug
      - COLLECTOR_OTLP_ENABLED=true
    networks:
      - jaeger-example

networks:
  jaeger-example:
    driver: bridge

b) The application_configuration.json.nosdnc in the same folder

}
 }

...

c) Creating a PolicyType in the simulator

curl -v -X 'PUT' \
   'http://localhost:30001/a1-p/policytypes/1' \
   -H 'accept: application/json' \
   -H 'Content-Type: application/json' \
   -d '{
    "name":"pt1",
    "description":"pt1 policy type",
    "policy_type_id":1,
    "create_schema":{
       "$schema":"http://json-schema.org/draft-07/schema#",
       "title":"OSC_Type1_1.0.0",
       "description":"Type 1 policy type",
       "type":"object",{
    "description":"Application configuration",
    "config":{
       "ricproperties":[{
          "scope":{
             "nametype":"ric1object",
             "baseUrlproperties":"https://a1-sim-OSC:8185/",{
                "managedElementIdsueId":[{
                "kista_1",   "type":"string"
                "kista_2"
},
                ]"qosId":{
          }
         ]
"type":"string"
       }
 }

c) Creating a PolicyType in the simulator

curl -v -X 'PUT' \
   'http://localhost:30001/a1-p/policytypes/1' \ }
   -H 'accept: application/json' \
      -H 'Content-Type: application/json' \
 },
       -d  '{
    "nameadditionalProperties":"pt1"false,
    "description":"pt1 policy type",
         "policy_type_idrequired":1,[
     "create_schema":{
       "$schema":"http://json-schema.org/draft-07/schema#    "ueId",
       "title":"OSC_Type1_1.0.0",
       "description":"Type 1 policy type"qosId",
       "type":"object",
       "properties":{]
          "scope},
          "qosObjectives":{
             "type":"object",
             "properties":{
                "ueIdpriorityLevel":{
                   "type":"stringnumber"
                },
             },
       "qosId":{
      "additionalProperties":false,
             "typerequired":"string"[
                }"priorityLevel"
             },]
          }
   "additionalProperties":false,
    },
         "requiredadditionalProperties":[false,
                "ueId",
     "required":[
           "qosId"
             ]
          }scope",
          "qosObjectives":{
       ]
      "type":"object",
             "properties":{
                "priorityLevel":{
           }
}'

...

d) Creating a policy in a1-pms, after the policy type is successfully registered (curl http://localhost:8081/a1-policy/v2/policy-types)

curl -v -X 'PUT' \
  'http://localhost:8081/a1-policy/v2/policies' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "ric_id": "ric1",
  "policy_id": "aa8feaa88d944d919ef0e83f2172a51002",
  "transient": false,
  "service_id": "controlpanel",
    "policy_data": {
        "scope": {
            "typeueId": "numberue5100",
            "qosId": "qos5100"
        },
        "qosObjectives": {
             },
"priorityLevel": 5100.0
              "additionalProperties":false,}
    },
  "status_notification_uri": "http://callback-receiver:8090/callbacks/test",
  "policytype_id": "1"
}'

...

e) http://localhost:16686/ Load Jaeger UI, a1-pms traces, and a sample of the last call would be:

Image Added


Steps:


Adding Telemetry to a1policymanagementservice The application uses the WebClient from SpringWebflux to contact from the northbound interface a southbound interface (for the latter a A1-OSC simulator has been used).

https://opentelemetry.io/docs/zero-code/java/spring-boot-starter/out-of-the-box-instrumentation/#spring-webflux-autoconfiguration

Opentelemetry documentation provides a bean to mutate the default WebClient builder and to add tracing filters.

In our case the AsyncRestClient manually builds a WebClient for every asynchronous request.

The challenge was to add the tracing filters to this non-Spring class.

1.Adding OpenTelemetry Bean

    @Bean
    public OpenTelemetry        "required":[
                "priorityLevel"
    openTelemetry() {
         ]
          }
       },
       "additionalProperties":false,
       "required":[return AutoConfiguredOpenTelemetrySdk.initialize().getOpenTelemetrySdk();
    }

Introduced circular dependency openTelemetryConfig defined in URL [jar:file:/opt/app/policy-agent/a1-policy-management-service.jar!/BOOT-INF/classes!/org/onap/ccsdk/oran/a1policymanagementservice/configuration/OpenTelemetryConfig.class

2. Adding filters into AsyncRestClient directly and not into builder bean, but the AutoConfiguredOpenTelemetrySdk uses by default parameters such as localhost:4317 to export grpc, so we opted for using the application.yaml parameters to build the exporters beans.

AsyncRestClient.java
		...
        OpenTelemetry openTelemetry "scope",
          "qosObjectives"= AutoConfiguredOpenTelemetrySdk.initialize().getOpenTelemetrySdk();
       ]
 var   }
}'

d) Creating a policy in a1-pms, after the policy type is successfully registered (curl http://localhost:8081/a1-policy/v2/policy-types)

webfluxTelemetry = SpringWebfluxTelemetry.builder(openTelemetry).build();
        return WebClient.builder() //
				...
                .filters(webfluxTelemetry::addClientTracingFilter)
                .build();

3. Context Provider class to use get the ApplicationContext into Non-Spring Components

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class ApplicationContextProvider implements ApplicationContextAwarecurl -v -X 'PUT' \
  'http://localhost:8081/a1-policy/v2/policies' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "ric_id": "ric1",
  "policy_id": "aa8feaa88d944d919ef0e83f2172a51002",
  "transient": false,
  "service_id": "controlpanel",
    "policy_data": {
        "scope": {
    private static       "ueId": "ue5100",
            "qosId": "qos5100"
    ApplicationContext context;
 
    },@Override
    public void setApplicationContext(ApplicationContext applicationContext) throws "qosObjectives":BeansException {
        context = applicationContext;
   "priorityLevel": 5100.0 }
 
    public static ApplicationContext getApplicationContext() }{
    },
    "status_notification_uri": "http://callback-receiver:8090/callbacks/test",
  "policytype_id": "1"
}'
return context;
    }
}

And then use var context = ApplicationContextProvider.getApplicationContext().getBean(OtelConfig.class); In the non Spring class, and if tracing is enabled to add the tracing filters.e) http://localhost:16686/ Load Jaeger UI, a1-pms traces, and a sample of the last call would be:
Image Removed



NOTES:

1.Using the ObservationRegistryCustomizer would still track /actuator manual calls, but it was kept in to kept UnitTests running

...