Skip to content

Commit 32247f7

Browse files
authored
timeseries: fixed opc ua sending alerts (#2124)
This PR aims to prevent OPC UA alert sending failures after moving to a slimmer Python image by making OPC UA server connectivity more resilient (notably around DNS resolution / connection state checks). Changes: - Add OPC UA server hostname resolution and track both configured vs resolved server URL. - Update OPC UA client re-initialization logic to key off the configured server URL. - Adjust OPC UA connection-status checks and reduce server details in logs. Signed-off-by: Vellaisamy, Sathyendran <sathyendran.vellaisamy@intel.com>
1 parent 28dcf63 commit 32247f7

File tree

2 files changed

+53
-8
lines changed

2 files changed

+53
-8
lines changed

microservices/time-series-analytics/src/main.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,8 +253,9 @@ async def receive_alert(alert: OpcuaAlertsMessage):
253253
try:
254254
if "alerts" in config.keys() and "opcua" in config["alerts"].keys():
255255
try:
256+
configured_opcua_server = config["alerts"]["opcua"]["opcua_server"]
256257
if OPCUA_SEND_ALERT is None or \
257-
OPCUA_SEND_ALERT.opcua_server != config["alerts"]["opcua"]["opcua_server"] or \
258+
OPCUA_SEND_ALERT.configured_opcua_server != configured_opcua_server or \
258259
not (await OPCUA_SEND_ALERT.is_connected()):
259260
logger.info("Initializing OPC UA client for sending alerts")
260261
OPCUA_SEND_ALERT = OpcuaAlerts(config)

microservices/time-series-analytics/src/opcua_alerts.py

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,36 @@ def __init__(self, config):
4444
self.node_id = None
4545
self.namespace = None
4646
self.opcua_server = None
47+
self.configured_opcua_server = None
48+
49+
def resolve_opcua_server_address(self, opcua_server):
50+
"""
51+
Resolve OPC UA server address if it is a hostname.
52+
53+
Args:
54+
opcua_server: OPC UA server URL which may contain a hostname
55+
Returns:
56+
str: Resolved OPC UA server URL with IP address if resolution is successful,
57+
otherwise returns the original URL
58+
"""
59+
try:
60+
from urllib.parse import urlparse
61+
import socket
62+
63+
parsed_url = urlparse(opcua_server)
64+
hostname = parsed_url.hostname
65+
if hostname:
66+
ip_address = socket.gethostbyname(hostname)
67+
resolved_url = opcua_server.replace(hostname, ip_address)
68+
logger.info("Resolved OPC UA server address: %s", opcua_server)
69+
return resolved_url
70+
else:
71+
logger.warning("No hostname found in OPC UA server URL: %s", opcua_server)
72+
return opcua_server
73+
except Exception as error:
74+
logger.error("Failed to resolve OPC UA server address: %s, Error: %s",
75+
opcua_server, error)
76+
return opcua_server
4777

4878
def load_opcua_config(self):
4979
"""
@@ -55,7 +85,8 @@ def load_opcua_config(self):
5585
try:
5686
self.node_id = self.config["alerts"]["opcua"]["node_id"]
5787
self.namespace = self.config["alerts"]["opcua"]["namespace"]
58-
self.opcua_server = self.config["alerts"]["opcua"]["opcua_server"]
88+
self.configured_opcua_server = self.config["alerts"]["opcua"]["opcua_server"]
89+
self.opcua_server = self.resolve_opcua_server_address(self.configured_opcua_server)
5990
return self.node_id, self.namespace, self.opcua_server
6091
except Exception as error:
6192
logger.exception("Fetching app configuration failed, Error: %s", error)
@@ -74,7 +105,7 @@ async def connect_opcua_client(self, secure_mode, max_retries=10):
74105
bool: True if connection successful, False otherwise
75106
"""
76107
if self.opcua_server:
77-
logger.info("Creating OPC UA client for server: %s", self.opcua_server)
108+
logger.info("Creating OPC UA client for server")
78109
self.client = Client(self.opcua_server)
79110
self.client.application_uri = "urn:opcua:python:server"
80111
else:
@@ -99,10 +130,10 @@ async def connect_opcua_client(self, secure_mode, max_retries=10):
99130
if opcua_server_username:
100131
self.client.set_user(opcua_server_username)
101132
self.client.set_password(opcua_server_password)
102-
logger.info("Attempting to connect to OPC UA server: %s "
103-
"%s (Attempt %s)", self.opcua_server, self.client, attempt + 1)
133+
logger.info("Attempting to connect to OPC UA server. "
134+
"(Attempt %s)", attempt + 1)
104135
await self.client.connect()
105-
logger.info("Connected to OPC UA server: %s successfully.", self.opcua_server)
136+
logger.info("Connected to OPC UA server successfully.")
106137
return True
107138
except Exception as error:
108139
logger.error("Connection failed: %s", error)
@@ -161,8 +192,21 @@ async def is_connected(self) -> bool:
161192
Returns True if connected, False otherwise.
162193
"""
163194
try:
164-
node = self.client.get_node(f"ns={self.namespace};i={self.node_id}")
165-
await node.read_value()
195+
if self.client is None:
196+
logger.info("OPC UA client is not initialized; connection state is disconnected.")
197+
return False
198+
199+
protocol = getattr(self.client.uaclient, "protocol", None)
200+
if protocol is None:
201+
logger.info("OPC UA client has no active protocol; connection state is disconnected.")
202+
return False
203+
204+
if getattr(protocol, "state", None) != "open":
205+
logger.info("OPC UA client protocol state is %s; connection state is disconnected.",
206+
getattr(protocol, "state", None))
207+
return False
208+
209+
await self.client.check_connection()
166210
return True
167211
except Exception as error:
168212
logger.error("Error checking OPC UA connection status: %s", error)

0 commit comments

Comments
 (0)