From 37c15ea4fb3f5e7be2e8663ce67c02ccb72adc3d Mon Sep 17 00:00:00 2001 From: Cole Walker Date: Fri, 5 Apr 2024 12:40:53 -0400 Subject: [PATCH] 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 Change-Id: I4671c7f8c67c4869a6d5e3b384eae66d8c57a284 --- .gitignore | 4 +- .../ptp-notification/Chart.yaml | 4 +- .../scripts/init/locationservice_start.sh | 120 +----------------- .../ptp-notification/templates/daemonset.yaml | 59 ++++++++- .../ptp-notification/values.yaml | 9 ++ .../docker/locationservice/config.py | 5 +- .../locationservice/locationservice_start.py | 49 +++++++ .../client/locationproducer.py | 49 +++---- .../locationservicesdk/services/daemon.py | 52 +++++--- .../notificationclient-sidecar/config.py | 17 +-- .../notificationclient_start.py | 49 +++++++ .../trackingfunctionsdk/services/daemon.py | 14 +- .../trackingfunctionsdk/services/health.py | 79 ++++++++++++ .../trackingfunctionsdk/services/daemon.py | 29 +++-- .../trackingfunctionsdk/services/health.py | 79 ++++++++++++ 15 files changed, 421 insertions(+), 197 deletions(-) create mode 100644 locationservice-base/docker/locationservice/locationservice_start.py create mode 100644 notificationclient-base/docker/notificationclient-sidecar/notificationclient_start.py create mode 100644 notificationservice-base-v2/docker/ptptrackingfunction/trackingfunctionsdk/services/health.py create mode 100644 notificationservice-base/docker/ptptrackingfunction/trackingfunctionsdk/services/health.py diff --git a/.gitignore b/.gitignore index ce6f360..b34d978 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ .tox api-ref/build doc/build -venv/ \ No newline at end of file +venv/ +__pycache__/ +*.pyc diff --git a/helm-charts/custom/ptp-notification-helm/ptp-notification-helm/ptp-notification/Chart.yaml b/helm-charts/custom/ptp-notification-helm/ptp-notification-helm/ptp-notification/Chart.yaml index 34d6ba7..8b16b63 100644 --- a/helm-charts/custom/ptp-notification-helm/ptp-notification-helm/ptp-notification/Chart.yaml +++ b/helm-charts/custom/ptp-notification-helm/ptp-notification-helm/ptp-notification/Chart.yaml @@ -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 # @@ -7,4 +7,4 @@ apiVersion: v1 appVersion: "2.0" description: A Helm chart to deploy PTP Notification Service name: ptp-notification -version: 2.0.0 +version: 2.0.1 diff --git a/helm-charts/custom/ptp-notification-helm/ptp-notification-helm/ptp-notification/resources/scripts/init/locationservice_start.sh b/helm-charts/custom/ptp-notification-helm/ptp-notification-helm/ptp-notification/resources/scripts/init/locationservice_start.sh index 78efe94..c3d6014 100644 --- a/helm-charts/custom/ptp-notification-helm/ptp-notification-helm/ptp-notification/resources/scripts/init/locationservice_start.sh +++ b/helm-charts/custom/ptp-notification-helm/ptp-notification-helm/ptp-notification/resources/scripts/init/locationservice_start.sh @@ -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 # @@ -18,124 +18,6 @@ # pip3 install oslo-config # pip3 install oslo-messaging -cat </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 </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 </root/notification_control.py import os import time diff --git a/helm-charts/custom/ptp-notification-helm/ptp-notification-helm/ptp-notification/templates/daemonset.yaml b/helm-charts/custom/ptp-notification-helm/ptp-notification-helm/ptp-notification/templates/daemonset.yaml index 3e2b940..cc8eeea 100644 --- a/helm-charts/custom/ptp-notification-helm/ptp-notification-helm/ptp-notification/templates/daemonset.yaml +++ b/helm-charts/custom/ptp-notification-helm/ptp-notification-helm/ptp-notification/templates/daemonset.yaml @@ -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 # @@ -88,10 +88,27 @@ spec: value: "{{ .Values.notification.endpoint.pass }}" - name: NOTIFICATIONSERVICE_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 value: "{{ .Values.location.log_level }}" - - command: ["/bin/bash", "/mnt/locationservice_start.sh"] + command: ["python3", "/opt/locationservice/locationservice_start.py"] +{{- 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: - name: scripts mountPath: /mnt @@ -136,6 +153,12 @@ spec: value: "5672" - name: REGISTRATION_HOST 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 value: "{{ .Values.ptptrackingv2.ptp4lServiceName }}" - name: PTP4L_CLOCK_CLASS_LOCKED_LIST @@ -153,6 +176,18 @@ spec: - name: CONTROL_TIMEOUT value: "{{ .Values.ptptrackingv2.control_timeout }}" 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: privileged: true capabilities: @@ -225,6 +260,12 @@ spec: value: "5672" - name: REGISTRATION_HOST 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 value: "{{ .Values.ptptracking.ptp4lServiceName }}" - name: PTP4L_CLOCK_CLASS_LOCKED_LIST @@ -238,6 +279,18 @@ spec: - name: LOGGING_LEVEL value: "{{ .Values.ptptracking.logging_level }}" 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: privileged: true capabilities: diff --git a/helm-charts/custom/ptp-notification-helm/ptp-notification-helm/ptp-notification/values.yaml b/helm-charts/custom/ptp-notification-helm/ptp-notification-helm/ptp-notification/values.yaml index dd7a9e9..ce0f0af 100644 --- a/helm-charts/custom/ptp-notification-helm/ptp-notification-helm/ptp-notification/values.yaml +++ b/helm-charts/custom/ptp-notification-helm/ptp-notification-helm/ptp-notification/values.yaml @@ -63,6 +63,9 @@ notification: location: log_level: INFO + endpoint: + port: 8080 + liveness: False image: repository: starlingx/locationservice-base tag: stx.9.0-v2.2.0 @@ -77,6 +80,9 @@ ptptracking: phc2sysServiceName: phc2sys-legacy phc2sysComSocket: False logging_level: INFO + endpoint: + port: 8081 + liveness: False image: repository: starlingx/notificationservice-base tag: stx.9.0-v2.2.0 @@ -96,6 +102,9 @@ ptptrackingv2: phc2sysToleranceThreshold: 1000 ts2phcServiceName: True log_level: INFO + endpoint: + port: 8082 + liveness: False image: repository: starlingx/notificationservice-base-v2 tag: stx.9.0-v2.2.0 diff --git a/locationservice-base/docker/locationservice/config.py b/locationservice-base/docker/locationservice/config.py index 741835d..34a778f 100644 --- a/locationservice-base/docker/locationservice/config.py +++ b/locationservice-base/docker/locationservice/config.py @@ -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 # @@ -11,12 +11,13 @@ server = { } # Pecan Application Configurations +# Ensure debug = False as per Pecan documentation app = { 'root': 'apiserver.controllers.root.RootController', 'modules': ['apiserver'], 'static_root': '%(confdir)s/public', 'template_path': '%(confdir)s/apiserver/templates', - 'debug': True, + 'debug': False, 'errors': { 404: '/error/404', '__force_dict__': True diff --git a/locationservice-base/docker/locationservice/locationservice_start.py b/locationservice-base/docker/locationservice/locationservice_start.py new file mode 100644 index 0000000..30a6936 --- /dev/null +++ b/locationservice-base/docker/locationservice/locationservice_start.py @@ -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() diff --git a/locationservice-base/docker/locationservice/locationservicesdk/client/locationproducer.py b/locationservice-base/docker/locationservice/locationservicesdk/client/locationproducer.py index 5214c8e..dabf136 100644 --- a/locationservice-base/docker/locationservice/locationservicesdk/client/locationproducer.py +++ b/locationservice-base/docker/locationservice/locationservicesdk/client/locationproducer.py @@ -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 # -import os import json -import time -import oslo_messaging -from oslo_config import cfg - -from locationservicesdk.client.base import BrokerClientBase - 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__) -from locationservicesdk.common.helpers import log_helper log_helper.config_logger(LOG) @@ -30,11 +29,12 @@ class LocationProducer(BrokerClientBase): pass 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 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: return self.handler.handle(**rpc_kwargs) else: @@ -52,23 +52,26 @@ class LocationProducer(BrokerClientBase): return def announce_location(self, LocationInfo): - location_topic_all='LocationListener-*' - location_topic='LocationListener-{0}'.format(self.node_name) + location_topic_all = 'LocationListener-*' + location_topic = 'LocationListener-{0}'.format(self.node_name) server = None while True: try: - self.cast(location_topic_all, 'NotifyLocation', location_info=LocationInfo) - LOG.debug("Broadcast location info:{0}@Topic:{1}".format(LocationInfo, location_topic)) + self.cast(location_topic_all, 'NotifyLocation', + location_info=LocationInfo) + LOG.debug( + "Broadcast location info:{0}@Topic:{1}".format(LocationInfo, location_topic)) 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 else: break def start_location_listener(self, location_info, handler=None): - topic='LocationQuery' - server="LocationService-{0}".format(self.node_name) + topic = 'LocationQuery' + server = "LocationService-{0}".format(self.node_name) endpoints = [LocationProducer.ListenerEndpoint(location_info, handler)] super(LocationProducer, self).add_listener( @@ -76,15 +79,13 @@ class LocationProducer(BrokerClientBase): return True def stop_location_listener(self): - topic='LocationQuery' - server="LocationService-{0}".format(self.node_name) + topic = 'LocationQuery' + server = "LocationService-{0}".format(self.node_name) super(LocationProducer, self).remove_listener( topic, server) def is_listening(self): - topic='LocationQuery' - server="LocationService-{0}".format(self.node_name) + topic = 'LocationQuery' + server = "LocationService-{0}".format(self.node_name) return super(LocationProducer, self).is_listening( topic, server) - - diff --git a/locationservice-base/docker/locationservice/locationservicesdk/services/daemon.py b/locationservice-base/docker/locationservice/locationservicesdk/services/daemon.py index 02455bd..98b7434 100644 --- a/locationservice-base/docker/locationservice/locationservicesdk/services/daemon.py +++ b/locationservice-base/docker/locationservice/locationservicesdk/services/daemon.py @@ -1,27 +1,32 @@ +# +# Copyright (c) 2024 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# -import os import json -import time -import oslo_messaging -from oslo_config import cfg import logging - import multiprocessing as mp +import os +import time -from locationservicesdk.common.helpers import rpc_helper -from locationservicesdk.model.dto.rpc_endpoint import RpcEndpointInfo -from locationservicesdk.model.dto.resourcetype import ResourceType - +import oslo_messaging 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__) -from locationservicesdk.common.helpers import log_helper log_helper.config_logger(LOG) '''Entry point of Default Process Worker''' + + 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() return @@ -42,7 +47,8 @@ class LocationWatcherDefault: self.location_info = json.loads(location_info_json) this_node_name = self.location_info['NodeName'] - self.registration_endpoint = RpcEndpointInfo(registration_transport_endpoint) + self.registration_endpoint = RpcEndpointInfo( + registration_transport_endpoint) self.LocationProducer = LocationProducer( this_node_name, self.registration_endpoint.TransportEndpoint) @@ -67,18 +73,20 @@ class LocationWatcherDefault: # max timeout: 1 hour if self.event_timeout < float(3600): 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 self.__stop_listener() '''Start listener to answer querying from clients''' + def __start_listener(self): LOG.debug("start listener to answer location querying") self.LocationProducer.start_location_listener( self.location_info, LocationWatcherDefault.LocationRequestHandlerDefault(self) - ) + ) return def __stop_listener(self): @@ -88,21 +96,25 @@ class LocationWatcherDefault: return '''announce location''' + def __announce_location(self): LOG.debug("announce location info to clients") self.LocationProducer.announce_location(self.location_info) return + class DaemonControl(object): def __init__( - self, sqlalchemy_conf_json, registration_transport_endpoint, - location_info, process_worker = None, daemon_mode=True): + self, sqlalchemy_conf_json, registration_transport_endpoint, + location_info, process_worker=None, daemon_mode=True): self.daemon_mode = daemon_mode self.event = mp.Event() - self.registration_endpoint = RpcEndpointInfo(registration_transport_endpoint) - self.registration_transport = rpc_helper.get_transport(self.registration_endpoint) + self.registration_endpoint = RpcEndpointInfo( + registration_transport_endpoint) + self.registration_transport = rpc_helper.get_transport( + self.registration_endpoint) self.location_info = location_info self.sqlalchemy_conf_json = sqlalchemy_conf_json @@ -116,8 +128,8 @@ class DaemonControl(object): self.mpinstance = mp.Process( target=process_worker, args=(self.event, self.sqlalchemy_conf_json, - self.registration_endpoint.TransportEndpoint, - self.location_info)) + self.registration_endpoint.TransportEndpoint, + self.location_info)) self.mpinstance.start() pass diff --git a/notificationclient-base/docker/notificationclient-sidecar/config.py b/notificationclient-base/docker/notificationclient-sidecar/config.py index 3f1625b..88f238c 100644 --- a/notificationclient-base/docker/notificationclient-sidecar/config.py +++ b/notificationclient-base/docker/notificationclient-sidecar/config.py @@ -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 # @@ -17,12 +17,13 @@ server = { } # Pecan Application Configurations +# Ensure debug = False as per Pecan documentation app = { 'root': 'sidecar.controllers.root.RootController', 'modules': ['sidecar'], 'static_root': '%(confdir)s/public', 'template_path': '%(confdir)s/sidecar/templates', - 'debug': True, + 'debug': False, 'errors': { 404: '/error/404', '__force_dict__': True @@ -53,18 +54,18 @@ logging = { '()': 'pecan.log.ColorFormatter', 'format': ('%(asctime)s [%(padded_color_levelname)s] [%(name)s]' '[%(threadName)s] %(message)s'), - '__force_dict__': True + '__force_dict__': True } } } # Bindings and options to pass to SQLAlchemy's ``create_engine`` sqlalchemy = { - 'url' : "sqlite:////{0}/sidecar.db".format(DATASTORE_PATH), - 'echo' : False, - 'echo_pool' : False, - 'pool_recycle' : 3600, - 'encoding' : 'utf-8' + 'url': "sqlite:////{0}/sidecar.db".format(DATASTORE_PATH), + 'echo': False, + 'echo_pool': False, + 'pool_recycle': 3600, + 'encoding': 'utf-8' } # Custom Configurations must be in Python dictionary format:: diff --git a/notificationclient-base/docker/notificationclient-sidecar/notificationclient_start.py b/notificationclient-base/docker/notificationclient-sidecar/notificationclient_start.py new file mode 100644 index 0000000..762e2d3 --- /dev/null +++ b/notificationclient-base/docker/notificationclient-sidecar/notificationclient_start.py @@ -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() diff --git a/notificationservice-base-v2/docker/ptptrackingfunction/trackingfunctionsdk/services/daemon.py b/notificationservice-base-v2/docker/ptptrackingfunction/trackingfunctionsdk/services/daemon.py index b0a2694..d96f7f1 100644 --- a/notificationservice-base-v2/docker/ptptrackingfunction/trackingfunctionsdk/services/daemon.py +++ b/notificationservice-base-v2/docker/ptptrackingfunction/trackingfunctionsdk/services/daemon.py @@ -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 # @@ -10,21 +10,21 @@ import multiprocessing as mp import os import threading import time -from oslo_utils import uuidutils +from oslo_utils import uuidutils 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 log_helper from trackingfunctionsdk.common.helpers.gnss_monitor import GnssMonitor from trackingfunctionsdk.common.helpers.os_clock_monitor import OsClockMonitor 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.osclockstate import OsClockState 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.rpc_endpoint import RpcEndpointInfo +from trackingfunctionsdk.services.health import HealthServer LOG = logging.getLogger(__name__) log_helper.config_logger(LOG) @@ -379,6 +379,10 @@ class PtpWatcherDefault: # start location listener self.__start_listener() + # Start the server for k8s httpGet health checks + notificationservice_health = HealthServer() + notificationservice_health.run() + while True: # announce the location forced = self.forced_publishing diff --git a/notificationservice-base-v2/docker/ptptrackingfunction/trackingfunctionsdk/services/health.py b/notificationservice-base-v2/docker/ptptrackingfunction/trackingfunctionsdk/services/health.py new file mode 100644 index 0000000..651a74c --- /dev/null +++ b/notificationservice-base-v2/docker/ptptrackingfunction/trackingfunctionsdk/services/health.py @@ -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 diff --git a/notificationservice-base/docker/ptptrackingfunction/trackingfunctionsdk/services/daemon.py b/notificationservice-base/docker/ptptrackingfunction/trackingfunctionsdk/services/daemon.py index 3bb26a1..ff59cbf 100644 --- a/notificationservice-base/docker/ptptrackingfunction/trackingfunctionsdk/services/daemon.py +++ b/notificationservice-base/docker/ptptrackingfunction/trackingfunctionsdk/services/daemon.py @@ -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 # -import os import json +import logging +import multiprocessing as mp +import os +import threading import time + import oslo_messaging 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.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__) from trackingfunctionsdk.common.helpers import log_helper + log_helper.config_logger(LOG) THIS_NODE_NAME = os.environ.get("THIS_NODE_NAME",'controller-0') @@ -115,6 +113,11 @@ class PtpWatcherDefault: def run(self): # start location listener self.__start_listener() + + # Start the server for k8s httpGet health checks + notificationservice_health = HealthServer() + notificationservice_health.run() + while True: # annouce the location forced = self.forced_publishing diff --git a/notificationservice-base/docker/ptptrackingfunction/trackingfunctionsdk/services/health.py b/notificationservice-base/docker/ptptrackingfunction/trackingfunctionsdk/services/health.py new file mode 100644 index 0000000..ca0e323 --- /dev/null +++ b/notificationservice-base/docker/ptptrackingfunction/trackingfunctionsdk/services/health.py @@ -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