- Developing PTP events consumer applications
- PTP events consumer application reference
- Reference cloud-event-proxy deployment and service CRs
- PTP events available from the cloud-event-proxy sidecar REST API
- Subscribing the consumer application to PTP events
- Getting the current PTP clock status
- Verifying that the PTP events consumer application is receiving events
Developing PTP events consumer applications
When developing consumer applications that make use of Precision Time Protocol (PTP) events on a bare-metal cluster node, you need to deploy your consumer application and a cloud-event-proxy
container in a separate application pod. The cloud-event-proxy
container receives the events from the PTP Operator pod and passes it to the consumer application. The consumer application subscribes to the events posted in the cloud-event-proxy
container by using a REST API.
For more information about deploying PTP events applications, see About the PTP fast event notifications framework.
The following information provides general guidance for developing consumer applications that use PTP events. A complete events consumer application example is outside the scope of this information. |
PTP events consumer application reference
PTP event consumer applications require the following features:
A web service running with a
POST
handler to receive the cloud native PTP events JSON payloadA
createSubscription
function to subscribe to the PTP events producerA
getCurrentState
function to poll the current state of the PTP events producer
The following example Go snippets illustrate these requirements:
Example PTP events consumer server function in Go
func server() {
http.HandleFunc("/event", getEvent)
http.ListenAndServe("localhost:8989", nil)
}
func getEvent(w http.ResponseWriter, req *http.Request) {
defer req.Body.Close()
bodyBytes, err := io.ReadAll(req.Body)
if err != nil {
log.Errorf("error reading event %v", err)
}
e := string(bodyBytes)
if e != "" {
processEvent(bodyBytes)
log.Infof("received event %s", string(bodyBytes))
} else {
w.WriteHeader(http.StatusNoContent)
}
}
Example PTP events createSubscription function in Go
import (
"github.com/redhat-cne/sdk-go/pkg/pubsub"
"github.com/redhat-cne/sdk-go/pkg/types"
v1pubsub "github.com/redhat-cne/sdk-go/v1/pubsub"
)
// Subscribe to PTP events using REST API
s1,_:=createsubscription("/cluster/node/<node_name>/sync/sync-status/os-clock-sync-state") (1)
s2,_:=createsubscription("/cluster/node/<node_name>/sync/ptp-status/ptp-clock-class-change")
s3,_:=createsubscription("/cluster/node/<node_name>/sync/ptp-status/lock-state")
// Create PTP event subscriptions POST
func createSubscription(resourceAddress string) (sub pubsub.PubSub, err error) {
var status int
apiPath:= "/api/ocloudNotifications/v1/"
localAPIAddr:=localhost:8989 // vDU service API address
apiAddr:= "localhost:8089" // event framework API address
subURL := &types.URI{URL: url.URL{Scheme: "http",
Host: apiAddr
Path: fmt.Sprintf("%s%s", apiPath, "subscriptions")}}
endpointURL := &types.URI{URL: url.URL{Scheme: "http",
Host: localAPIAddr,
Path: "event"}}
sub = v1pubsub.NewPubSub(endpointURL, resourceAddress)
var subB []byte
if subB, err = json.Marshal(&sub); err == nil {
rc := restclient.New()
if status, subB = rc.PostWithReturn(subURL, subB); status != http.StatusCreated {
err = fmt.Errorf("error in subscription creation api at %s, returned status %d", subURL, status)
} else {
err = json.Unmarshal(subB, &sub)
}
} else {
err = fmt.Errorf("failed to marshal subscription for %s", resourceAddress)
}
return
}
1 | Replace <node_name> with the FQDN of the node that is generating the PTP events. For example, compute-1.example.com . |
Example PTP events consumer getCurrentState function in Go
//Get PTP event state for the resource
func getCurrentState(resource string) {
//Create publisher
url := &types.URI{URL: url.URL{Scheme: "http",
Host: localhost:8989,
Path: fmt.SPrintf("/api/ocloudNotifications/v1/%s/CurrentState",resource}}
rc := restclient.New()
status, event := rc.Get(url)
if status != http.StatusOK {
log.Errorf("CurrentState:error %d from url %s, %s", status, url.String(), event)
} else {
log.Debugf("Got CurrentState: %s ", event)
}
}
Reference cloud-event-proxy deployment and service CRs
Use the following example cloud-event-proxy
deployment and subscriber service CRs as a reference when deploying your PTP events consumer application.
Use HTTP transport instead of AMQP for PTP and bare-metal events where possible. AMQ Interconnect is EOL from 30 June 2024. Extended life cycle support (ELS) for AMQ Interconnect ends 30 November 2030. For more information see, Red Hat AMQ Interconnect support status. |
Reference cloud-event-proxy deployment with HTTP transport
apiVersion: apps/v1
kind: Deployment
metadata:
name: event-consumer-deployment
namespace: <namespace>
labels:
app: consumer
spec:
replicas: 1
selector:
matchLabels:
app: consumer
template:
metadata:
labels:
app: consumer
spec:
serviceAccountName: sidecar-consumer-sa
containers:
- name: event-subscriber
image: event-subscriber-app
- name: cloud-event-proxy-as-sidecar
image: openshift4/ose-cloud-event-proxy
args:
- "--metrics-addr=127.0.0.1:9091"
- "--store-path=/store"
- "--transport-host=consumer-events-subscription-service.cloud-events.svc.cluster.local:9043"
- "--http-event-publishers=ptp-event-publisher-service-NODE_NAME.openshift-ptp.svc.cluster.local:9043"
- "--api-port=8089"
env:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: NODE_IP
valueFrom:
fieldRef:
fieldPath: status.hostIP
volumeMounts:
- name: pubsubstore
mountPath: /store
ports:
- name: metrics-port
containerPort: 9091
- name: sub-port
containerPort: 9043
volumes:
- name: pubsubstore
emptyDir: {}
Reference cloud-event-proxy deployment with AMQ transport
apiVersion: apps/v1
kind: Deployment
metadata:
name: cloud-event-proxy-sidecar
namespace: cloud-events
labels:
app: cloud-event-proxy
spec:
selector:
matchLabels:
app: cloud-event-proxy
template:
metadata:
labels:
app: cloud-event-proxy
spec:
nodeSelector:
node-role.kubernetes.io/worker: ""
containers:
- name: cloud-event-sidecar
image: openshift4/ose-cloud-event-proxy
args:
- "--metrics-addr=127.0.0.1:9091"
- "--store-path=/store"
- "--transport-host=amqp://router.router.svc.cluster.local"
- "--api-port=8089"
env:
- name: <node_name>
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: <node_ip>
valueFrom:
fieldRef:
fieldPath: status.hostIP
volumeMounts:
- name: pubsubstore
mountPath: /store
ports:
- name: metrics-port
containerPort: 9091
- name: sub-port
containerPort: 9043
volumes:
- name: pubsubstore
emptyDir: {}
Reference cloud-event-proxy subscriber service
apiVersion: v1
kind: Service
metadata:
annotations:
prometheus.io/scrape: "true"
service.alpha.openshift.io/serving-cert-secret-name: sidecar-consumer-secret
name: consumer-events-subscription-service
namespace: cloud-events
labels:
app: consumer-service
spec:
ports:
- name: sub-port
port: 9043
selector:
app: consumer
clusterIP: None
sessionAffinity: None
type: ClusterIP
PTP events available from the cloud-event-proxy sidecar REST API
PTP events consumer applications can poll the PTP events producer for the following PTP timing events.
Resource URI | Description |
---|---|
| Describes the current status of the PTP equipment lock state. Can be in |
| Describes the host operating system clock synchronization state. Can be in |
| Describes the current state of the PTP clock class. |
Subscribing the consumer application to PTP events
Before the PTP events consumer application can poll for events, you need to subscribe the application to the event producer.
Subscribing to PTP lock-state events
To create a subscription for PTP lock-state
events, send a POST
action to the cloud event API at http://localhost:8081/api/ocloudNotifications/v1/subscriptions
with the following payload:
{
"endpointUri": "http://localhost:8989/event",
"resource": "/cluster/node/<node_name>/sync/ptp-status/lock-state",
}
Example response
{
"id": "e23473d9-ba18-4f78-946e-401a0caeff90",
"endpointUri": "http://localhost:8989/event",
"uriLocation": "http://localhost:8089/api/ocloudNotifications/v1/subscriptions/e23473d9-ba18-4f78-946e-401a0caeff90",
"resource": "/cluster/node/<node_name>/sync/ptp-status/lock-state",
}
Subscribing to PTP os-clock-sync-state events
To create a subscription for PTP os-clock-sync-state
events, send a POST
action to the cloud event API at http://localhost:8081/api/ocloudNotifications/v1/subscriptions
with the following payload:
{
"endpointUri": "http://localhost:8989/event",
"resource": "/cluster/node/<node_name>/sync/sync-status/os-clock-sync-state",
}
Example response
{
"id": "e23473d9-ba18-4f78-946e-401a0caeff90",
"endpointUri": "http://localhost:8989/event",
"uriLocation": "http://localhost:8089/api/ocloudNotifications/v1/subscriptions/e23473d9-ba18-4f78-946e-401a0caeff90",
"resource": "/cluster/node/<node_name>/sync/sync-status/os-clock-sync-state",
}
Subscribing to PTP ptp-clock-class-change events
To create a subscription for PTP ptp-clock-class-change
events, send a POST
action to the cloud event API at http://localhost:8081/api/ocloudNotifications/v1/subscriptions
with the following payload:
{
"endpointUri": "http://localhost:8989/event",
"resource": "/cluster/node/<node_name>/sync/ptp-status/ptp-clock-class-change",
}
Example response
{
"id": "e23473d9-ba18-4f78-946e-401a0caeff90",
"endpointUri": "http://localhost:8989/event",
"uriLocation": "http://localhost:8089/api/ocloudNotifications/v1/subscriptions/e23473d9-ba18-4f78-946e-401a0caeff90",
"resource": "/cluster/node/<node_name>/sync/ptp-status/ptp-clock-class-change",
}
Getting the current PTP clock status
To get the current PTP status for the node, send a GET
action to one of the following event REST APIs:
http://localhost:8081/api/ocloudNotifications/v1/cluster/node/<node_name>/sync/ptp-status/lock-state/CurrentState
http://localhost:8081/api/ocloudNotifications/v1/cluster/node/<node_name>/sync/sync-status/os-clock-sync-state/CurrentState
http://localhost:8081/api/ocloudNotifications/v1/cluster/node/<node_name>/sync/ptp-status/ptp-clock-class-change/CurrentState
The response is a cloud native event JSON object. For example:
Example lock-state API response
{
"id": "c1ac3aa5-1195-4786-84f8-da0ea4462921",
"type": "event.sync.ptp-status.ptp-state-change",
"source": "/cluster/node/compute-1.example.com/sync/ptp-status/lock-state",
"dataContentType": "application/json",
"time": "2023-01-10T02:41:57.094981478Z",
"data": {
"version": "v1",
"values": [
{
"resource": "/cluster/node/compute-1.example.com/ens5fx/master",
"dataType": "notification",
"valueType": "enumeration",
"value": "LOCKED"
},
{
"resource": "/cluster/node/compute-1.example.com/ens5fx/master",
"dataType": "metric",
"valueType": "decimal64.3",
"value": "29"
}
]
}
}
Verifying that the PTP events consumer application is receiving events
Verify that the cloud-event-proxy
container in the application pod is receiving PTP events.
Prerequisites
You have installed the OpenShift CLI (
oc
).You have logged in as a user with
cluster-admin
privileges.You have installed and configured the PTP Operator.
Procedure
Get the list of active
linuxptp-daemon
pods. Run the following command:$ oc get pods -n openshift-ptp
Example output
NAME READY STATUS RESTARTS AGE
linuxptp-daemon-2t78p 3/3 Running 0 8h
linuxptp-daemon-k8n88 3/3 Running 0 8h
Access the metrics for the required consumer-side
cloud-event-proxy
container by running the following command:$ oc exec -it <linuxptp-daemon> -n openshift-ptp -c cloud-event-proxy -- curl 127.0.0.1:9091/metrics
where:
<linuxptp-daemon>
Specifies the pod you want to query, for example,
linuxptp-daemon-2t78p
.Example output
# HELP cne_transport_connections_resets Metric to get number of connection resets
# TYPE cne_transport_connections_resets gauge
cne_transport_connection_reset 1
# HELP cne_transport_receiver Metric to get number of receiver created
# TYPE cne_transport_receiver gauge
cne_transport_receiver{address="/cluster/node/compute-1.example.com/ptp",status="active"} 2
cne_transport_receiver{address="/cluster/node/compute-1.example.com/redfish/event",status="active"} 2
# HELP cne_transport_sender Metric to get number of sender created
# TYPE cne_transport_sender gauge
cne_transport_sender{address="/cluster/node/compute-1.example.com/ptp",status="active"} 1
cne_transport_sender{address="/cluster/node/compute-1.example.com/redfish/event",status="active"} 1
# HELP cne_events_ack Metric to get number of events produced
# TYPE cne_events_ack gauge
cne_events_ack{status="success",type="/cluster/node/compute-1.example.com/ptp"} 18
cne_events_ack{status="success",type="/cluster/node/compute-1.example.com/redfish/event"} 18
# HELP cne_events_transport_published Metric to get number of events published by the transport
# TYPE cne_events_transport_published gauge
cne_events_transport_published{address="/cluster/node/compute-1.example.com/ptp",status="failed"} 1
cne_events_transport_published{address="/cluster/node/compute-1.example.com/ptp",status="success"} 18
cne_events_transport_published{address="/cluster/node/compute-1.example.com/redfish/event",status="failed"} 1
cne_events_transport_published{address="/cluster/node/compute-1.example.com/redfish/event",status="success"} 18
# HELP cne_events_transport_received Metric to get number of events received by the transport
# TYPE cne_events_transport_received gauge
cne_events_transport_received{address="/cluster/node/compute-1.example.com/ptp",status="success"} 18
cne_events_transport_received{address="/cluster/node/compute-1.example.com/redfish/event",status="success"} 18
# HELP cne_events_api_published Metric to get number of events published by the rest api
# TYPE cne_events_api_published gauge
cne_events_api_published{address="/cluster/node/compute-1.example.com/ptp",status="success"} 19
cne_events_api_published{address="/cluster/node/compute-1.example.com/redfish/event",status="success"} 19
# HELP cne_events_received Metric to get number of events received
# TYPE cne_events_received gauge
cne_events_received{status="success",type="/cluster/node/compute-1.example.com/ptp"} 18
cne_events_received{status="success",type="/cluster/node/compute-1.example.com/redfish/event"} 18
# HELP promhttp_metric_handler_requests_in_flight Current number of scrapes being served.
# TYPE promhttp_metric_handler_requests_in_flight gauge
promhttp_metric_handler_requests_in_flight 1
# HELP promhttp_metric_handler_requests_total Total number of scrapes by HTTP status code.
# TYPE promhttp_metric_handler_requests_total counter
promhttp_metric_handler_requests_total{code="200"} 4
promhttp_metric_handler_requests_total{code="500"} 0
promhttp_metric_handler_requests_total{code="503"} 0