Add support for httpGet liveness probes

Updates the locationservice, notificationservice and notificationclient
containers to support ipv6 httpGet liveness probes.

notificationservice-base and notificationservice-basev2:
- Adds health.py which starts a simple http server that runs within the
  daemon. The k8s httpGet liveness probe can query this endpoint to
  verify that the service is running
- Update the daemonset template and values to provide the required info
  for initalizing the new endpoint

locationservice-base:
- Remove unused portions of the locationservice_start.sh config map.
  The location-query-server.py and location-announce.py were never
  active and are not required
- Add locationservice_start.py in order to start the locationservice
  pecan WSGI application with either an ipv4 or ipv6 socket
- Use existing pecan endpoint to respond to liveness probes

notificationclient-base:
- Add notificationclient_start.py to start the notificationclient
  pecan WSGI application with either an ipv4 or ipv6 socket
- Use existing pecan endpoint to respond to liveness probes

Daemonset:
- Add required ip and port environment variables to support liveness
  probes on each container
- Add a conditional section for enabling liveness probes. Disabled by
  default but can be enabled via helm overrides by setting "liveness:
  True"

Misc:
- Re-organized python imports in affected files
- Incremented helm chart version to 2.0.1

Test-plan:
Pass: Verify application build and install
Pass: Verify containers build correctly
Pass: Deploy ptp-notification and verify basic sanity (v1 and v2 get,
subscribe, delete, list)
Pass: Enable httpGet liveness probes for each container and verify
operation
Pass: Verify application removal

Story: 2011090
Task: 49851

Signed-off-by: Cole Walker <cole.walker@windriver.com>
Change-Id: I4671c7f8c67c4869a6d5e3b384eae66d8c57a284
This commit is contained in:
Cole Walker 2024-04-05 12:40:53 -04:00
parent d38115c8ed
commit 37c15ea4fb
15 changed files with 421 additions and 197 deletions

2
.gitignore vendored
View File

@ -2,3 +2,5 @@
api-ref/build api-ref/build
doc/build doc/build
venv/ venv/
__pycache__/
*.pyc

View File

@ -1,5 +1,5 @@
# #
# Copyright (c) 2021 Wind River Systems, Inc. # Copyright (c) 2021-2024 Wind River Systems, Inc.
# #
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
# #
@ -7,4 +7,4 @@ apiVersion: v1
appVersion: "2.0" appVersion: "2.0"
description: A Helm chart to deploy PTP Notification Service description: A Helm chart to deploy PTP Notification Service
name: ptp-notification name: ptp-notification
version: 2.0.0 version: 2.0.1

View File

@ -1,5 +1,5 @@
# #
# Copyright (c) 2021 Wind River Systems, Inc. # Copyright (c) 2021-2024 Wind River Systems, Inc.
# #
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
# #
@ -18,124 +18,6 @@
# pip3 install oslo-config # pip3 install oslo-config
# pip3 install oslo-messaging # pip3 install oslo-messaging
cat <<EOF>/root/location-query-server.py
#!/usr/bin/python3
# -*- coding: UTF-8 -*-
import os
import json
import time # 引入time模块
import oslo_messaging
from oslo_config import cfg
THIS_NODE_NAME = os.environ.get("THIS_NODE_NAME",'controller-1')
THIS_POD_IP = os.environ.get("THIS_POD_IP",'127.0.0.1')
THIS_NAMESPACE = os.environ.get("THIS_NAMESPACE",'notification')
rabbituser = os.environ.get("REGISTRATION_USER",'admin')
rabbitpasswd = os.environ.get("REGISTRATION_PASS",'admin')
rabbitip = "registration.{0}.svc.cluster.local".format(THIS_NAMESPACE)
rabbitport = os.environ.get("REGISTRATION_PORT",'5672')
# 'rabbit://admin:admin@[127.0.0.1]:5672/'
# 'rabbit://admin:admin@[::1]:5672/'
rabbitendpoint = "rabbit://{0}:{1}@[{2}]:{3}".format(
rabbituser, rabbitpasswd, rabbitip, rabbitport)
class LocationInfoEndpoint(object):
target = oslo_messaging.Target(namespace='notification', version='1.0')
def __init__(self, server):
self.server = server
def QueryLocation(self, ctx, rpc_kwargs):
print ("QueryLocation called %s" %rpc_kwargs)
LocationInfo = {
'NodeName': THIS_NODE_NAME,
'PodIP': THIS_POD_IP,
'ResourceTypes': ['PTP'],
'Timestamp': time.time()
}
return LocationInfo
oslo_messaging.set_transport_defaults('notification_exchange')
transport = oslo_messaging.get_rpc_transport(cfg.CONF, url=rabbitendpoint)
target = oslo_messaging.Target(topic='LocationQuery', server="LocationService-{0}".format(THIS_NODE_NAME))
endpoints = [LocationInfoEndpoint(None)]
server = oslo_messaging.get_rpc_server(transport, target, endpoints,
executor=None)
# oslo_messaging.server.ExecutorLoadFailure:
# Failed to load executor "blocking": Executor should be None or 'eventlet' and 'threading'
server.start()
print("LocationService-{0} starts".format(THIS_NODE_NAME))
server.wait()
EOF
cat <<EOF>/root/location-announce.py
#!/usr/bin/python3
# -*- coding: UTF-8 -*-
import os
import json
import time # 引入time模块
import oslo_messaging
from oslo_config import cfg
from webob.exc import HTTPException, HTTPNotFound, HTTPBadRequest, HTTPClientError, HTTPServerError
THIS_NODE_NAME = os.environ.get("THIS_NODE_NAME",'controller-1')
THIS_POD_IP = os.environ.get("THIS_POD_IP",'127.0.0.1')
THIS_NAMESPACE = os.environ.get("THIS_NAMESPACE",'notification')
rabbituser = os.environ.get("REGISTRATION_USER",'admin')
rabbitpasswd = os.environ.get("REGISTRATION_PASS",'admin')
rabbitip = "registration.{0}.svc.cluster.local".format(THIS_NAMESPACE)
rabbitport = os.environ.get("REGISTRATION_PORT",'5672')
rabbitendpoint = "rabbit://{0}:{1}@[{2}]:{3}".format(
rabbituser, rabbitpasswd, rabbitip, rabbitport)
oslo_messaging.set_transport_defaults('notification_exchange')
transport = oslo_messaging.get_rpc_transport(cfg.CONF, url=rabbitendpoint)
location_topic='LocationListener-{0}'.format(THIS_NODE_NAME),
target = oslo_messaging.Target(
topic=location_topic,
fanout=True,
version='1.0', namespace='notification')
client = oslo_messaging.get_rpc_client(transport, target)
LocationInfo = {
'NodeName': THIS_NODE_NAME,
'PodIP': THIS_POD_IP,
'ResourceTypes': ['PTP'],
'Timestamp': time.time()
}
while True:
try:
client.cast({}, 'NotifyLocation', location_info=LocationInfo)
print("Announce location info:{0}@Topic:{1}".format(LocationInfo, location_topic))
except HTTPNotFound as ex:
print("Failed to publish location due to not found: {0}".format(str(ex)))
continue
except Exception as ex:
print("Failed to publish location due to: {0}".format(str(ex)))
continue
else:
break
EOF
echo "done"
# python3 /root/location-query-server.py &
# python3 /root/location-announce.py
cat <<EOF>/root/notification_control.py cat <<EOF>/root/notification_control.py
import os import os
import time import time

View File

@ -1,5 +1,5 @@
# #
# Copyright (c) 2021-2023 Wind River Systems, Inc. # Copyright (c) 2021-2024 Wind River Systems, Inc.
# #
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
# #
@ -88,10 +88,27 @@ spec:
value: "{{ .Values.notification.endpoint.pass }}" value: "{{ .Values.notification.endpoint.pass }}"
- name: NOTIFICATIONSERVICE_PORT - name: NOTIFICATIONSERVICE_PORT
value: "{{ .Values.notification.endpoint.port }}" value: "{{ .Values.notification.endpoint.port }}"
- name: LOCATION_SERVICE_HOST
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: LOCATION_SERVICE_PORT
value: "{{ .Values.location.endpoint.port }}"
- name: LOGGING_LEVEL - name: LOGGING_LEVEL
value: "{{ .Values.location.log_level }}" value: "{{ .Values.location.log_level }}"
command: ["python3", "/opt/locationservice/locationservice_start.py"]
command: ["/bin/bash", "/mnt/locationservice_start.sh"] {{- if .Values.location.endpoint.liveness }}
livenessProbe:
failureThreshold: 3
httpGet:
path: /health
port: {{ .Values.location.endpoint.port }}
scheme: HTTP
initialDelaySeconds: 60
periodSeconds: 3
successThreshold: 1
timeoutSeconds: 3
{{ end }}
volumeMounts: volumeMounts:
- name: scripts - name: scripts
mountPath: /mnt mountPath: /mnt
@ -136,6 +153,12 @@ spec:
value: "5672" value: "5672"
- name: REGISTRATION_HOST - name: REGISTRATION_HOST
value: "registration.{{.Values.global.namespace}}.svc.cluster.local" value: "registration.{{.Values.global.namespace}}.svc.cluster.local"
- name: HEALTH_API_HOST
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: HEALTH_API_PORT
value: "{{ .Values.ptptrackingv2.endpoint.port }}"
- name: PTP4L_SERVICE_NAME - name: PTP4L_SERVICE_NAME
value: "{{ .Values.ptptrackingv2.ptp4lServiceName }}" value: "{{ .Values.ptptrackingv2.ptp4lServiceName }}"
- name: PTP4L_CLOCK_CLASS_LOCKED_LIST - name: PTP4L_CLOCK_CLASS_LOCKED_LIST
@ -153,6 +176,18 @@ spec:
- name: CONTROL_TIMEOUT - name: CONTROL_TIMEOUT
value: "{{ .Values.ptptrackingv2.control_timeout }}" value: "{{ .Values.ptptrackingv2.control_timeout }}"
command: ["python3", "/mnt/ptptracking_start_v2.py"] command: ["python3", "/mnt/ptptracking_start_v2.py"]
{{- if .Values.ptptrackingv2.endpoint.liveness }}
livenessProbe:
failureThreshold: 3
httpGet:
path: /health
port: {{ .Values.ptptrackingv2.endpoint.port }}
scheme: HTTP
initialDelaySeconds: 60
periodSeconds: 3
successThreshold: 1
timeoutSeconds: 3
{{ end }}
securityContext: securityContext:
privileged: true privileged: true
capabilities: capabilities:
@ -225,6 +260,12 @@ spec:
value: "5672" value: "5672"
- name: REGISTRATION_HOST - name: REGISTRATION_HOST
value: "registration.{{.Values.global.namespace}}.svc.cluster.local" value: "registration.{{.Values.global.namespace}}.svc.cluster.local"
- name: HEALTH_API_HOST
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: HEALTH_API_PORT
value: "{{ .Values.ptptracking.endpoint.port }}"
- name: PTP4L_SERVICE_NAME - name: PTP4L_SERVICE_NAME
value: "{{ .Values.ptptracking.ptp4lServiceName }}" value: "{{ .Values.ptptracking.ptp4lServiceName }}"
- name: PTP4L_CLOCK_CLASS_LOCKED_LIST - name: PTP4L_CLOCK_CLASS_LOCKED_LIST
@ -238,6 +279,18 @@ spec:
- name: LOGGING_LEVEL - name: LOGGING_LEVEL
value: "{{ .Values.ptptracking.logging_level }}" value: "{{ .Values.ptptracking.logging_level }}"
command: ["python3", "/mnt/ptptracking_start.py"] command: ["python3", "/mnt/ptptracking_start.py"]
{{- if .Values.ptptracking.endpoint.liveness }}
livenessProbe:
failureThreshold: 3
httpGet:
path: /health
port: {{ .Values.ptptracking.endpoint.port }}
scheme: HTTP
initialDelaySeconds: 60
periodSeconds: 3
successThreshold: 1
timeoutSeconds: 3
{{ end }}
securityContext: securityContext:
privileged: true privileged: true
capabilities: capabilities:

View File

@ -63,6 +63,9 @@ notification:
location: location:
log_level: INFO log_level: INFO
endpoint:
port: 8080
liveness: False
image: image:
repository: starlingx/locationservice-base repository: starlingx/locationservice-base
tag: stx.9.0-v2.2.0 tag: stx.9.0-v2.2.0
@ -77,6 +80,9 @@ ptptracking:
phc2sysServiceName: phc2sys-legacy phc2sysServiceName: phc2sys-legacy
phc2sysComSocket: False phc2sysComSocket: False
logging_level: INFO logging_level: INFO
endpoint:
port: 8081
liveness: False
image: image:
repository: starlingx/notificationservice-base repository: starlingx/notificationservice-base
tag: stx.9.0-v2.2.0 tag: stx.9.0-v2.2.0
@ -96,6 +102,9 @@ ptptrackingv2:
phc2sysToleranceThreshold: 1000 phc2sysToleranceThreshold: 1000
ts2phcServiceName: True ts2phcServiceName: True
log_level: INFO log_level: INFO
endpoint:
port: 8082
liveness: False
image: image:
repository: starlingx/notificationservice-base-v2 repository: starlingx/notificationservice-base-v2
tag: stx.9.0-v2.2.0 tag: stx.9.0-v2.2.0

View File

@ -1,5 +1,5 @@
# #
# Copyright (c) 2021 Wind River Systems, Inc. # Copyright (c) 2021-2024 Wind River Systems, Inc.
# #
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
# #
@ -11,12 +11,13 @@ server = {
} }
# Pecan Application Configurations # Pecan Application Configurations
# Ensure debug = False as per Pecan documentation
app = { app = {
'root': 'apiserver.controllers.root.RootController', 'root': 'apiserver.controllers.root.RootController',
'modules': ['apiserver'], 'modules': ['apiserver'],
'static_root': '%(confdir)s/public', 'static_root': '%(confdir)s/public',
'template_path': '%(confdir)s/apiserver/templates', 'template_path': '%(confdir)s/apiserver/templates',
'debug': True, 'debug': False,
'errors': { 'errors': {
404: '/error/404', 404: '/error/404',
'__force_dict__': True '__force_dict__': True

View File

@ -0,0 +1,49 @@
#
# Copyright (c) 2024 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import logging
import os
import socket
from wsgiref import simple_server
from locationservicesdk.common.helpers import log_helper
from netaddr import IPAddress
from pecan.deploy import deploy
LOG = logging.getLogger(__name__)
log_helper.config_logger(LOG)
LOCATION_SERVICE_HOST = os.environ.get(
"LOCATION_SERVICE_HOST", '127.0.0.1')
LOCATION_SERVICE_PORT = int(
os.environ.get("LOCATION_SERVICE_PORT", '8080'))
def get_address_family(ip_string):
"""
Get the family for the given ip address string.
"""
ip_address = IPAddress(ip_string)
if ip_address.version == 6:
return socket.AF_INET6
else:
return socket.AF_INET
def main():
simple_server.WSGIServer.address_family = get_address_family(
LOCATION_SERVICE_HOST)
application = deploy('/opt/locationservice/config.py')
with simple_server.make_server(LOCATION_SERVICE_HOST,
LOCATION_SERVICE_PORT,
application) as httpd:
LOG.info("locationservice_start.py: Starting locationservice")
httpd.serve_forever()
if __name__ == "__main__":
main()

View File

@ -1,22 +1,21 @@
# #
# Copyright (c) 2021 Wind River Systems, Inc. # Copyright (c) 2021-2024 Wind River Systems, Inc.
# #
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
# #
import os
import json import json
import time
import oslo_messaging
from oslo_config import cfg
from locationservicesdk.client.base import BrokerClientBase
import logging import logging
import os
import time
import oslo_messaging
from locationservicesdk.client.base import BrokerClientBase
from locationservicesdk.common.helpers import log_helper
from oslo_config import cfg
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
from locationservicesdk.common.helpers import log_helper
log_helper.config_logger(LOG) log_helper.config_logger(LOG)
@ -30,11 +29,12 @@ class LocationProducer(BrokerClientBase):
pass pass
def QueryLocation(self, ctx, **rpc_kwargs): def QueryLocation(self, ctx, **rpc_kwargs):
LOG.debug ("LocationProducer QueryLocation called %s" %rpc_kwargs) LOG.debug("LocationProducer QueryLocation called %s" % rpc_kwargs)
return self.location_info return self.location_info
def TriggerAnnouncement(self, ctx, **rpc_kwargs): def TriggerAnnouncement(self, ctx, **rpc_kwargs):
LOG.debug ("LocationProducer TriggerAnnouncement called %s" %rpc_kwargs) LOG.debug("LocationProducer TriggerAnnouncement called %s" %
rpc_kwargs)
if self.handler: if self.handler:
return self.handler.handle(**rpc_kwargs) return self.handler.handle(**rpc_kwargs)
else: else:
@ -52,23 +52,26 @@ class LocationProducer(BrokerClientBase):
return return
def announce_location(self, LocationInfo): def announce_location(self, LocationInfo):
location_topic_all='LocationListener-*' location_topic_all = 'LocationListener-*'
location_topic='LocationListener-{0}'.format(self.node_name) location_topic = 'LocationListener-{0}'.format(self.node_name)
server = None server = None
while True: while True:
try: try:
self.cast(location_topic_all, 'NotifyLocation', location_info=LocationInfo) self.cast(location_topic_all, 'NotifyLocation',
LOG.debug("Broadcast location info:{0}@Topic:{1}".format(LocationInfo, location_topic)) location_info=LocationInfo)
LOG.debug(
"Broadcast location info:{0}@Topic:{1}".format(LocationInfo, location_topic))
except Exception as ex: except Exception as ex:
LOG.debug("Failed to publish location due to: {0}".format(str(ex))) LOG.debug(
"Failed to publish location due to: {0}".format(str(ex)))
continue continue
else: else:
break break
def start_location_listener(self, location_info, handler=None): def start_location_listener(self, location_info, handler=None):
topic='LocationQuery' topic = 'LocationQuery'
server="LocationService-{0}".format(self.node_name) server = "LocationService-{0}".format(self.node_name)
endpoints = [LocationProducer.ListenerEndpoint(location_info, handler)] endpoints = [LocationProducer.ListenerEndpoint(location_info, handler)]
super(LocationProducer, self).add_listener( super(LocationProducer, self).add_listener(
@ -76,15 +79,13 @@ class LocationProducer(BrokerClientBase):
return True return True
def stop_location_listener(self): def stop_location_listener(self):
topic='LocationQuery' topic = 'LocationQuery'
server="LocationService-{0}".format(self.node_name) server = "LocationService-{0}".format(self.node_name)
super(LocationProducer, self).remove_listener( super(LocationProducer, self).remove_listener(
topic, server) topic, server)
def is_listening(self): def is_listening(self):
topic='LocationQuery' topic = 'LocationQuery'
server="LocationService-{0}".format(self.node_name) server = "LocationService-{0}".format(self.node_name)
return super(LocationProducer, self).is_listening( return super(LocationProducer, self).is_listening(
topic, server) topic, server)

View File

@ -1,27 +1,32 @@
#
# Copyright (c) 2024 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import os
import json import json
import time
import oslo_messaging
from oslo_config import cfg
import logging import logging
import multiprocessing as mp import multiprocessing as mp
import os
import time
from locationservicesdk.common.helpers import rpc_helper import oslo_messaging
from locationservicesdk.model.dto.rpc_endpoint import RpcEndpointInfo
from locationservicesdk.model.dto.resourcetype import ResourceType
from locationservicesdk.client.locationproducer import LocationProducer from locationservicesdk.client.locationproducer import LocationProducer
from locationservicesdk.common.helpers import log_helper, rpc_helper
from locationservicesdk.model.dto.resourcetype import ResourceType
from locationservicesdk.model.dto.rpc_endpoint import RpcEndpointInfo
from oslo_config import cfg
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
from locationservicesdk.common.helpers import log_helper
log_helper.config_logger(LOG) log_helper.config_logger(LOG)
'''Entry point of Default Process Worker''' '''Entry point of Default Process Worker'''
def ProcessWorkerDefault(event, sqlalchemy_conf_json, registration_endpoint, location_info_json): def ProcessWorkerDefault(event, sqlalchemy_conf_json, registration_endpoint, location_info_json):
worker = LocationWatcherDefault(event, sqlalchemy_conf_json, registration_endpoint, location_info_json) worker = LocationWatcherDefault(
event, sqlalchemy_conf_json, registration_endpoint, location_info_json)
worker.run() worker.run()
return return
@ -42,7 +47,8 @@ class LocationWatcherDefault:
self.location_info = json.loads(location_info_json) self.location_info = json.loads(location_info_json)
this_node_name = self.location_info['NodeName'] this_node_name = self.location_info['NodeName']
self.registration_endpoint = RpcEndpointInfo(registration_transport_endpoint) self.registration_endpoint = RpcEndpointInfo(
registration_transport_endpoint)
self.LocationProducer = LocationProducer( self.LocationProducer = LocationProducer(
this_node_name, this_node_name,
self.registration_endpoint.TransportEndpoint) self.registration_endpoint.TransportEndpoint)
@ -67,11 +73,13 @@ class LocationWatcherDefault:
# max timeout: 1 hour # max timeout: 1 hour
if self.event_timeout < float(3600): if self.event_timeout < float(3600):
self.event_timeout = self.event_timeout + self.event_timeout self.event_timeout = self.event_timeout + self.event_timeout
LOG.debug("daemon control event is timeout") LOG.debug("daemon control event is timeout: %s" %
self.event_timeout)
continue continue
self.__stop_listener() self.__stop_listener()
'''Start listener to answer querying from clients''' '''Start listener to answer querying from clients'''
def __start_listener(self): def __start_listener(self):
LOG.debug("start listener to answer location querying") LOG.debug("start listener to answer location querying")
@ -88,21 +96,25 @@ class LocationWatcherDefault:
return return
'''announce location''' '''announce location'''
def __announce_location(self): def __announce_location(self):
LOG.debug("announce location info to clients") LOG.debug("announce location info to clients")
self.LocationProducer.announce_location(self.location_info) self.LocationProducer.announce_location(self.location_info)
return return
class DaemonControl(object): class DaemonControl(object):
def __init__( def __init__(
self, sqlalchemy_conf_json, registration_transport_endpoint, self, sqlalchemy_conf_json, registration_transport_endpoint,
location_info, process_worker = None, daemon_mode=True): location_info, process_worker=None, daemon_mode=True):
self.daemon_mode = daemon_mode self.daemon_mode = daemon_mode
self.event = mp.Event() self.event = mp.Event()
self.registration_endpoint = RpcEndpointInfo(registration_transport_endpoint) self.registration_endpoint = RpcEndpointInfo(
self.registration_transport = rpc_helper.get_transport(self.registration_endpoint) registration_transport_endpoint)
self.registration_transport = rpc_helper.get_transport(
self.registration_endpoint)
self.location_info = location_info self.location_info = location_info
self.sqlalchemy_conf_json = sqlalchemy_conf_json self.sqlalchemy_conf_json = sqlalchemy_conf_json

View File

@ -1,5 +1,5 @@
# #
# Copyright (c) 2021 Wind River Systems, Inc. # Copyright (c) 2021-2024 Wind River Systems, Inc.
# #
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
# #
@ -17,12 +17,13 @@ server = {
} }
# Pecan Application Configurations # Pecan Application Configurations
# Ensure debug = False as per Pecan documentation
app = { app = {
'root': 'sidecar.controllers.root.RootController', 'root': 'sidecar.controllers.root.RootController',
'modules': ['sidecar'], 'modules': ['sidecar'],
'static_root': '%(confdir)s/public', 'static_root': '%(confdir)s/public',
'template_path': '%(confdir)s/sidecar/templates', 'template_path': '%(confdir)s/sidecar/templates',
'debug': True, 'debug': False,
'errors': { 'errors': {
404: '/error/404', 404: '/error/404',
'__force_dict__': True '__force_dict__': True
@ -60,11 +61,11 @@ logging = {
# Bindings and options to pass to SQLAlchemy's ``create_engine`` # Bindings and options to pass to SQLAlchemy's ``create_engine``
sqlalchemy = { sqlalchemy = {
'url' : "sqlite:////{0}/sidecar.db".format(DATASTORE_PATH), 'url': "sqlite:////{0}/sidecar.db".format(DATASTORE_PATH),
'echo' : False, 'echo': False,
'echo_pool' : False, 'echo_pool': False,
'pool_recycle' : 3600, 'pool_recycle': 3600,
'encoding' : 'utf-8' 'encoding': 'utf-8'
} }
# Custom Configurations must be in Python dictionary format:: # Custom Configurations must be in Python dictionary format::

View File

@ -0,0 +1,49 @@
#
# Copyright (c) 2024 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import logging
import os
import socket
from wsgiref import simple_server
from netaddr import IPAddress
from notificationclientsdk.common.helpers import log_helper
from pecan.deploy import deploy
LOG = logging.getLogger(__name__)
log_helper.config_logger(LOG)
SIDECAR_API_HOST = os.environ.get(
"SIDECAR_API_HOST", '127.0.0.1')
SIDECAR_API_PORT = int(
os.environ.get("SIDECAR_API_PORT", '8080'))
def get_address_family(ip_string):
"""
Get the family for the given ip address string.
"""
ip_address = IPAddress(ip_string)
if ip_address.version == 6:
return socket.AF_INET6
else:
return socket.AF_INET
def main():
simple_server.WSGIServer.address_family = get_address_family(
SIDECAR_API_HOST)
application = deploy('/opt/notificationclient/config.py')
with simple_server.make_server(SIDECAR_API_HOST,
SIDECAR_API_PORT,
application) as httpd:
LOG.info("notificationclient_start.py: Starting notificationclient")
httpd.serve_forever()
if __name__ == "__main__":
main()

View File

@ -1,5 +1,5 @@
# #
# Copyright (c) 2021-2023 Wind River Systems, Inc. # Copyright (c) 2021-2024 Wind River Systems, Inc.
# #
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
# #
@ -10,21 +10,21 @@ import multiprocessing as mp
import os import os
import threading import threading
import time import time
from oslo_utils import uuidutils
from oslo_utils import uuidutils
from trackingfunctionsdk.client.ptpeventproducer import PtpEventProducer from trackingfunctionsdk.client.ptpeventproducer import PtpEventProducer
from trackingfunctionsdk.common.helpers import constants from trackingfunctionsdk.common.helpers import constants, log_helper
from trackingfunctionsdk.common.helpers import ptpsync as utils from trackingfunctionsdk.common.helpers import ptpsync as utils
from trackingfunctionsdk.common.helpers import log_helper
from trackingfunctionsdk.common.helpers.gnss_monitor import GnssMonitor from trackingfunctionsdk.common.helpers.gnss_monitor import GnssMonitor
from trackingfunctionsdk.common.helpers.os_clock_monitor import OsClockMonitor from trackingfunctionsdk.common.helpers.os_clock_monitor import OsClockMonitor
from trackingfunctionsdk.common.helpers.ptp_monitor import PtpMonitor from trackingfunctionsdk.common.helpers.ptp_monitor import PtpMonitor
from trackingfunctionsdk.model.dto.ptpstate import PtpState
from trackingfunctionsdk.model.dto.gnssstate import GnssState from trackingfunctionsdk.model.dto.gnssstate import GnssState
from trackingfunctionsdk.model.dto.osclockstate import OsClockState from trackingfunctionsdk.model.dto.osclockstate import OsClockState
from trackingfunctionsdk.model.dto.overallclockstate import OverallClockState from trackingfunctionsdk.model.dto.overallclockstate import OverallClockState
from trackingfunctionsdk.model.dto.ptpstate import PtpState
from trackingfunctionsdk.model.dto.resourcetype import ResourceType from trackingfunctionsdk.model.dto.resourcetype import ResourceType
from trackingfunctionsdk.model.dto.rpc_endpoint import RpcEndpointInfo from trackingfunctionsdk.model.dto.rpc_endpoint import RpcEndpointInfo
from trackingfunctionsdk.services.health import HealthServer
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
log_helper.config_logger(LOG) log_helper.config_logger(LOG)
@ -379,6 +379,10 @@ class PtpWatcherDefault:
# start location listener # start location listener
self.__start_listener() self.__start_listener()
# Start the server for k8s httpGet health checks
notificationservice_health = HealthServer()
notificationservice_health.run()
while True: while True:
# announce the location # announce the location
forced = self.forced_publishing forced = self.forced_publishing

View File

@ -0,0 +1,79 @@
#
# Copyright (c) 2024 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import json
import logging
import os
import socket
import threading
from http.server import BaseHTTPRequestHandler, HTTPServer
from netaddr import IPAddress
from pecan.deploy import deploy
from trackingfunctionsdk.common.helpers import log_helper
LOG = logging.getLogger(__name__)
log_helper.config_logger(LOG)
HEALTH_API_HOST = os.environ.get(
"HEALTH_API_HOST", '127.0.0.1')
HEALTH_API_PORT = int(
os.environ.get("HEALTH_API_PORT", '8082'))
def get_address_family(ip_string):
"""
Get the family for the given ip address string.
"""
ip_address = IPAddress(ip_string)
if ip_address.version == 6:
return socket.AF_INET6
else:
return socket.AF_INET
class HealthRequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.end_headers()
self.wfile.write(self.get_response().encode("utf-8"))
def do_POST(self):
self.do_GET()
def get_response(self):
"""
Return a simple confirmation if the process is running.
Can be extended to check broader health aspects of notification service as needed
"""
return json.dumps(
{'health': True}
)
class HealthServer:
def __init__(self):
HTTPServer.address_family = get_address_family(HEALTH_API_HOST)
self.health_server = HTTPServer(
(HEALTH_API_HOST, HEALTH_API_PORT), HealthRequestHandler)
self.thread = threading.Thread(target=self.health_server.serve_forever)
self.thread.daemon = True
def run(self):
self.thread.start()
return
if __name__ == "__main__":
my_health = HealthServer()
my_health.run()
print(
f"Health server running on {HEALTH_API_HOST}:{str(HEALTH_API_PORT)}")
while True:
# run indefinitely
pass

View File

@ -1,31 +1,29 @@
# #
# Copyright (c) 2021-2023 Wind River Systems, Inc. # Copyright (c) 2021-2024 Wind River Systems, Inc.
# #
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
# #
import os
import json import json
import logging
import multiprocessing as mp
import os
import threading
import time import time
import oslo_messaging import oslo_messaging
from oslo_config import cfg from oslo_config import cfg
import logging
import multiprocessing as mp
import threading
from trackingfunctionsdk.common.helpers import rpc_helper
from trackingfunctionsdk.model.dto.rpc_endpoint import RpcEndpointInfo
from trackingfunctionsdk.model.dto.resourcetype import ResourceType
from trackingfunctionsdk.model.dto.ptpstate import PtpState
from trackingfunctionsdk.client.ptpeventproducer import PtpEventProducer from trackingfunctionsdk.client.ptpeventproducer import PtpEventProducer
from trackingfunctionsdk.common.helpers import ptpsync as ptpsync from trackingfunctionsdk.common.helpers import ptpsync as ptpsync
from trackingfunctionsdk.model.dto.ptpstate import PtpState
from trackingfunctionsdk.model.dto.resourcetype import ResourceType
from trackingfunctionsdk.model.dto.rpc_endpoint import RpcEndpointInfo
from trackingfunctionsdk.services.health import HealthServer
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
from trackingfunctionsdk.common.helpers import log_helper from trackingfunctionsdk.common.helpers import log_helper
log_helper.config_logger(LOG) log_helper.config_logger(LOG)
THIS_NODE_NAME = os.environ.get("THIS_NODE_NAME",'controller-0') THIS_NODE_NAME = os.environ.get("THIS_NODE_NAME",'controller-0')
@ -115,6 +113,11 @@ class PtpWatcherDefault:
def run(self): def run(self):
# start location listener # start location listener
self.__start_listener() self.__start_listener()
# Start the server for k8s httpGet health checks
notificationservice_health = HealthServer()
notificationservice_health.run()
while True: while True:
# annouce the location # annouce the location
forced = self.forced_publishing forced = self.forced_publishing

View File

@ -0,0 +1,79 @@
#
# Copyright (c) 2024 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import json
import logging
import os
import socket
import threading
from http.server import BaseHTTPRequestHandler, HTTPServer
from netaddr import IPAddress
from pecan.deploy import deploy
from trackingfunctionsdk.common.helpers import log_helper
LOG = logging.getLogger(__name__)
log_helper.config_logger(LOG)
HEALTH_API_HOST = os.environ.get(
"HEALTH_API_HOST", '127.0.0.1')
HEALTH_SERVICE_API_PORT = int(
os.environ.get("HEALTH_API_PORT", '8081'))
def get_address_family(ip_string):
"""
Get the family for the given ip address string.
"""
ip_address = IPAddress(ip_string)
if ip_address.version == 6:
return socket.AF_INET6
else:
return socket.AF_INET
class HealthRequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.end_headers()
self.wfile.write(self.get_response().encode("utf-8"))
def do_POST(self):
self.do_GET()
def get_response(self):
"""
Return a simple confirmation if the process is running.
Can be extended to check broader health aspects of notification service as needed
"""
return json.dumps(
{'health': True}
)
class HealthServer:
def __init__(self):
HTTPServer.address_family = get_address_family(HEALTH_API_HOST)
self.health_server = HTTPServer(
(HEALTH_API_HOST, HEALTH_SERVICE_API_PORT), HealthRequestHandler)
self.thread = threading.Thread(target=self.health_server.serve_forever)
self.thread.daemon = True
def run(self):
self.thread.start()
return
if __name__ == "__main__":
my_health = HealthServer()
my_health.run()
print(
f"Health server running on {HEALTH_API_HOST}:{str(HEALTH_SERVICE_API_PORT)}")
while True:
# run indefinitely
pass