From d12c86b3155931d72acfa6d15311301a8c637b43 Mon Sep 17 00:00:00 2001 From: Sergei S Date: Sun, 31 Oct 2021 02:01:15 +0200 Subject: [PATCH] psrt feature --- UPDATE.rst | 2 + cli/check-psrt.py | 77 ++++++++++++++++++++++++++++++ install/mklinks | 2 +- lib/eva/features/default_cloud.py | 57 +++++++++++++++------- lib/eva/features/default_cloud.yml | 14 +++--- lib/eva/notify.py | 10 ++-- 6 files changed, 134 insertions(+), 28 deletions(-) create mode 100644 cli/check-psrt.py diff --git a/UPDATE.rst b/UPDATE.rst index 8789e7d8b..629ea9008 100644 --- a/UPDATE.rst +++ b/UPDATE.rst @@ -4,6 +4,8 @@ EVA ICS 3.4.2 What's new ========== +- PSRT support +- "mqtt" parameter in "default cloud" feature renamed to "host" - LM PLC stability and performance improvements - Bug fixes and general improvements diff --git a/cli/check-psrt.py b/cli/check-psrt.py new file mode 100644 index 000000000..d794ec284 --- /dev/null +++ b/cli/check-psrt.py @@ -0,0 +1,77 @@ +__author__ = "Altertech Group, https://www.altertech.com/" +__copyright__ = "Copyright (C) 2012-2021 Altertech Group" +__license__ = "Apache License 2.0" +__version__ = "3.4.2" + +import sys +import argparse +import logging + +logging.basicConfig(level=logging.DEBUG) + +from pathlib import Path +sys.path.insert(0, (Path(__file__).absolute().parents[1] / 'lib').as_posix()) + +import eva.core +import eva.tools +import eva.notify +import eva.api + +eva.core.set_product('test', -1) + +_me = 'EVA ICS PSRT test version %s' % __version__ + +ap = argparse.ArgumentParser(description=_me) +ap.add_argument(help='PSRT user:pass@host:port/space', + dest='_psrt', + metavar='PSRT') +ap.add_argument('--cafile', help='CA File', dest='_ca_file', metavar='FILE') + +try: + import argcomplete + argcomplete.autocomplete(ap) +except: + pass + +a = ap.parse_args() + +if not a._psrt: + ap.print_usage() + sys.exit(99) + +if a._psrt.find('@') != -1: + try: + mqa, mq = a._psrt.split('@') + user, password = mqa.split(':') + except: + ap.print_usage() + sys.exit(99) +else: + mq, user, password = a._psrt, None, None + +if mq.find('/') != -1: + try: + x = mq.split('/') + mq = x[0] + space = '/'.join(x[1:]) + except: + ap.print_usage() + sys.exit(99) +else: + space = None + +psrt_host, psrt_port = eva.tools.parse_host_port(mq, 2883) + +n = eva.notify.PSRTNotifier(notifier_id='test', + host=psrt_host, + port=psrt_port, + space=space, + username=user if user else None, + password=password if password else None, + ca_certs=a._ca_file) + +if n.test(): + print('OK') +else: + print('FAILED') + sys.exit(1) diff --git a/install/mklinks b/install/mklinks index fbf9169ba..7e817cf41 100755 --- a/install/mklinks +++ b/install/mklinks @@ -9,7 +9,7 @@ ln -sf ../cli/venvl tests/benchmark-uc-crt BIN="uc-cmd lm-cmd sfa-cmd test-uc-xc test-ext test-phi uc-tpl prepare-sr gen-intl key-deploy key-import" NBIN="uc-notifier lm-notifier sfa-notifier" -SBIN="check-mqtt get-setup-options eva-update-tables apikey-set pypi-mirror" +SBIN="check-mqtt check-psrt get-setup-options eva-update-tables apikey-set pypi-mirror" INSTALL="import-registry-defaults import-registry-schema convert-legacy-configs" for p in ${BIN}; do diff --git a/lib/eva/features/default_cloud.py b/lib/eva/features/default_cloud.py index c4c212600..304f724a6 100644 --- a/lib/eva/features/default_cloud.py +++ b/lib/eva/features/default_cloud.py @@ -1,40 +1,66 @@ from eva.features import InvalidParameter, dir_eva from eva.features import cli_call, is_enabled, exec_shell, restart_controller from eva.features import val_to_boolean +from eva.features import print_warn from textwrap import dedent -def setup(mqtt=None, +def setup(host=None, id=None, ca=None, cert=None, key=None, retain=None, - announce=None): - check_cmd = dir_eva + '/sbin/check-mqtt' - retain = True if retain is None else val_to_boolean(retain) + announce=None, + proto=None, + socket_buf_size=None): + if proto is None: + proto = 'mqtt' + elif proto not in ['mqtt', 'psrt']: + raise InvalidParameter(f'Invalid protocol: {proto}') + check_cmd = dir_eva + f'/sbin/check-{proto}' + if proto == 'psrt': + retain = False if retain is None else val_to_boolean(retain) + else: + retain = True if retain is None else val_to_boolean(retain) announce = 30 if announce is None else float(announce) if not id: id = 'eva_1' - if '/' in mqtt: - _mqtt, space = mqtt.rsplit('/', 1) + if '/' in host: + _host, space = host.rsplit('/', 1) else: - _mqtt = mqtt + _host = host space = None - batch = [f'create {id} mqtt:{_mqtt}{(" -s " + space) if space else ""}'] + batch = [f'create {id} {proto}:{_host}{(" -s " + space) if space else ""}'] if ca: batch.append(f'set {id} ca_certs {ca}') check_cmd += f' --cafile {ca}' if cert: - batch.append(f'set {id} certfile {ca}') - check_cmd += f' --cert {cert}' - if key: - batch.append(f'set {id} keyfile {ca}') - check_cmd += f' --key {key}' - check_cmd += f' {mqtt}' + if proto == 'psrt': + print_warn('cert/key auth no supported by psrt') + else: + batch.append(f'set {id} certfile {ca}') + check_cmd += f' --cert {cert}' + if key and proto != 'psrt': + if proto == 'psrt': + print_warn('cert/key auth no supported by psrt') + else: + batch.append(f'set {id} keyfile {ca}') + check_cmd += f' --key {key}' + check_cmd += f' {host}' exec_shell(check_cmd, passthru=True) - batch.append(f'set {id} retain_enabled {retain}') + if retain and proto == 'psrt': + print_warn('retain not supported by psrt') + if proto == 'mqtt': + batch.append(f'set {id} retain_enabled {retain}') + if socket_buf_size: + if proto == 'psrt': + batch.append(f'set {id} socket_buf_size {socket_buf_size}') + else: + print_warn('socket_buf_size supported by psrt only') + if proto == 'mqtt': + batch.append(f'set {id} retain_enabled {retain}') batch.append(f'test {id}') batch.append(f'subscribe state {id} -p "#" -g "#"') batch.append(f'subscribe log {id}') @@ -60,5 +86,4 @@ def remove(id=None): cli_call(f'ns {c}', f'destroy {id}', return_result=True) restart_controller(c) except: - from eva.features import print_warn print_warn(f'unable to destroy {id} notifier for {c}') diff --git a/lib/eva/features/default_cloud.yml b/lib/eva/features/default_cloud.yml index 28e76c97e..592a4804b 100644 --- a/lib/eva/features/default_cloud.yml +++ b/lib/eva/features/default_cloud.yml @@ -6,18 +6,20 @@ help: | Sets default announce interval to 30 seconds. Default connection id is eva_1 Subscribes MQTT to all state, log (INFO) and server events -example: {{ setup_cmd }}mqtt=user:password@192.168.1.12 +example: {{ setup_cmd }}host=user:password@192.168.1.12 doc-url: https://eva-ics.readthedocs.io/en/{{ EVA_VERSION }}/notifiers.html#mqtt-mqtt setup: mandatory-args: - mqtt: "as [user:password]@host[:port][/space]" + host: "as [user:password]@host[:port][/space]" optional-args: - id: "MQTT connection ID (default: eva_1)" + id: "MQTT/PSRT connection ID (default: eva_1)" ca: CA file (absolute path) - cert: Certificate file (absolute path) - key: Key file (absolute path) + cert: "Certificate file (absolute path, for MQTT only)" + key: "Key file (absolute path, for MQTT only" retain: "use retain topics (default: True)" announce: custom annonce interval + proto: "mqtt or psrt" + socket_buf_size: "psrt socket buffer size" remove: optional-args: - id: "MQTT connection ID (default: eva_1)" + id: "MQTT/PSRT connection ID (default: eva_1)" diff --git a/lib/eva/notify.py b/lib/eva/notify.py index 510116560..dd3329465 100644 --- a/lib/eva/notify.py +++ b/lib/eva/notify.py @@ -2454,7 +2454,7 @@ def start_announcer(self): def on_connect(self, client, userdata, flags, rc): if eva.core.is_shutdown_requested(): return - logging.debug('.%s mqtt reconnect' % self.notifier_id) + logging.debug(f'.{self.notifier_id} {self.proto} reconnect') self.mq_connected.set() if self.announce_interval and not self.test_only_mode: eva.core.spawn_daemon(self.start_announcer) @@ -2682,8 +2682,8 @@ def on_message(self, client, userdata, msg): d = msg.payload if msg.payload.startswith( b'\x00') else msg.payload.decode() except: - logging.warning('.Invalid message from MQTT server: {}'.format( - msg.payload)) + logging.warning( + f'.Invalid message from {self.proto} server, topic: {t}') eva.core.log_traceback(notifier=True) return try: @@ -3031,8 +3031,8 @@ def test(self): f'{self.pfx}controller/{eva.core.product.code}' f'/{eva.core.config.system_name}/test-{uuid.uuid4()}') self.test_topic = test_topic - logging.debug('.Testing mqtt notifier %s (%s:%u)' % \ - (self.notifier_id,self.host, self.port)) + logging.debug('.Testing %s notifier %s (%s:%u)' % \ + (self.proto, self.notifier_id,self.host, self.port)) if not self.check_connection(): return False t_start = time.perf_counter()