From ddff95024c02894a7d563abe46140174ccae049a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Fri, 23 Jan 2026 09:19:49 +0100 Subject: [PATCH 01/20] Client config (sdmi centric) --- example/01-logical-replication/sysconfig.json | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 example/01-logical-replication/sysconfig.json diff --git a/example/01-logical-replication/sysconfig.json b/example/01-logical-replication/sysconfig.json new file mode 100644 index 0000000..ee51103 --- /dev/null +++ b/example/01-logical-replication/sysconfig.json @@ -0,0 +1,70 @@ +{ + "system": { + "config": { + "global": { + "managers": { + "failover_mode": false, + "run_on_host": true + } + }, + "roles": { + "SYSOwner": { + "id": "admin", + "password": "admin" + }, + "SYSPgDBReplication": { + "id": "replicator", + "password": "secure" + } + } + }, + "networks": [ + { + "dbpool-net": { + "config": { + "net": { + "ipv4": { + "sbunet": "192.168.10.0", + "netmask": "255.255.255.0", + "netbits": 24, + "gateway": "192.168.10.254" + }, + "parts": { + "local-1": { + "ipv4": { + "start": 1, + "end": 252, + "managers": [ 253 ] + } + } + } + }, + "permissions": { + "SYSOwner": "admin" + }, + "scale": { + "min-nodes": 2, + "max-nodes": 4, + "metrics": { + "characteristics": "default" + } + } + } + } + } + ], + "runtime": { + "network": { + "nodes": [ + { + "dbpool-net-0": { + "status": "active", + "metrics": { + } + } + } + ] + } + } + } +} From dcc4b346248859f459187551f6b1ebb997b00c08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Fri, 23 Jan 2026 09:21:08 +0100 Subject: [PATCH 02/20] Single DB-node docker file --- example/01-logical-replication/db-node.dockerfile | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 example/01-logical-replication/db-node.dockerfile diff --git a/example/01-logical-replication/db-node.dockerfile b/example/01-logical-replication/db-node.dockerfile new file mode 100644 index 0000000..81cab8c --- /dev/null +++ b/example/01-logical-replication/db-node.dockerfile @@ -0,0 +1,15 @@ +FROM postgres:18-bookworm +MAINTAINER Claus Prüfer + +RUN apt-get -qq update -y +RUN apt-get -qq install iproute2 iputils-ping net-tools python3-pip python3-psycopg2 -y + +RUN pip3 install microesb --break-system-packages +RUN pip3 install jsocket --break-system-packages + +ENV POSTGRES_USER postgres +ENV POSTGRES_PASSWORD password +ENV POSTGRES_DB lb-test + +EXPOSE 5432 +EXPOSE 64000 From 337a47834d867c4810dd4ad7ed432c60fc3546fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sat, 24 Jan 2026 11:07:06 +0100 Subject: [PATCH 03/20] Add run container shell script --- example/01-logical-replication/run-container.sh | 5 +++++ 1 file changed, 5 insertions(+) create mode 100755 example/01-logical-replication/run-container.sh diff --git a/example/01-logical-replication/run-container.sh b/example/01-logical-replication/run-container.sh new file mode 100755 index 0000000..fcb19c4 --- /dev/null +++ b/example/01-logical-replication/run-container.sh @@ -0,0 +1,5 @@ +#!/bin/sh +node_id=$1 +node_ip=$2 + +docker run --rm -d --name ${node_id} --net dbpool-net --ip ${node_ip} db-node From d9b0ce0704318826eb8fe6cc19971e643272aadd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sat, 24 Jan 2026 11:09:41 +0100 Subject: [PATCH 04/20] Disable iptables in docker daemon config --- example/01-logical-replication/docker-daemon/daemon.json | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 example/01-logical-replication/docker-daemon/daemon.json diff --git a/example/01-logical-replication/docker-daemon/daemon.json b/example/01-logical-replication/docker-daemon/daemon.json new file mode 100644 index 0000000..7e0c4a1 --- /dev/null +++ b/example/01-logical-replication/docker-daemon/daemon.json @@ -0,0 +1,4 @@ +{ + "iptables": false +} + From 8503e668d4ebde4eb0371957d546bad4a92d321f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sat, 24 Jan 2026 11:10:26 +0100 Subject: [PATCH 05/20] Remove unrelevant runtime sub-data --- example/01-logical-replication/sysconfig.json | 7 ------- 1 file changed, 7 deletions(-) diff --git a/example/01-logical-replication/sysconfig.json b/example/01-logical-replication/sysconfig.json index ee51103..46167a6 100644 --- a/example/01-logical-replication/sysconfig.json +++ b/example/01-logical-replication/sysconfig.json @@ -56,13 +56,6 @@ "runtime": { "network": { "nodes": [ - { - "dbpool-net-0": { - "status": "active", - "metrics": { - } - } - } ] } } From 56926999a3ddfa9a3cc089ff74d6e5a2d43f2db6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sat, 24 Jan 2026 11:10:58 +0100 Subject: [PATCH 06/20] Add local orchestrator code --- .../01-logical-replication/orchestrator.py | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 example/01-logical-replication/orchestrator.py diff --git a/example/01-logical-replication/orchestrator.py b/example/01-logical-replication/orchestrator.py new file mode 100644 index 0000000..4e4be62 --- /dev/null +++ b/example/01-logical-replication/orchestrator.py @@ -0,0 +1,30 @@ +import json +import jsocket + +def mm_connect(dst_address, dst_port=64000): + client = jsocket.JsonClient(address=dst_address, port=dst_port) + assert client.connect() is True + return client + +def mm_send(client_ref, payload): + client_ref.send_obj(payload) + return client_ref.read_obj() + +def mm_close(client_ref): + client_ref.close() + +sysconfig = None + +with open('sysconfig.json', 'r') as fh: + sysconfig = json.loads(fh.read()) + + +sysconfig + +{ + "dbpool-net-0": { + "status": "active", + "metrics": { + } + } +} From 054b10833a51e703e8dbf5e6092d62dfe68df6e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Sat, 24 Jan 2026 11:11:33 +0100 Subject: [PATCH 07/20] Add docker metadata --- example/01-logical-replication/docker-build.sh | 5 +++++ example/01-logical-replication/docker-network.sh | 3 +++ 2 files changed, 8 insertions(+) create mode 100755 example/01-logical-replication/docker-build.sh create mode 100755 example/01-logical-replication/docker-network.sh diff --git a/example/01-logical-replication/docker-build.sh b/example/01-logical-replication/docker-build.sh new file mode 100755 index 0000000..80819f3 --- /dev/null +++ b/example/01-logical-replication/docker-build.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +# build docker database node +docker build -t db-node --file ./db-node.dockerfile . + diff --git a/example/01-logical-replication/docker-network.sh b/example/01-logical-replication/docker-network.sh new file mode 100755 index 0000000..1c98f93 --- /dev/null +++ b/example/01-logical-replication/docker-network.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +docker network create --subnet=172.16.1.0/24 --gateway=172.16.1.254 -o com.docker.network.bridge.enable_ip_masquerade=false -o com.docker.network.bridge.name=dbr0 dbpool-net From 70a08d6d438b676cdfdafec13510443c7347c1a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 26 Jan 2026 11:03:20 +0100 Subject: [PATCH 08/20] Update docker related --- example/01-logical-replication/db-node.dockerfile | 4 ++++ example/01-logical-replication/docker-build.sh | 1 - example/01-logical-replication/run-container.sh | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/example/01-logical-replication/db-node.dockerfile b/example/01-logical-replication/db-node.dockerfile index 81cab8c..449ee54 100644 --- a/example/01-logical-replication/db-node.dockerfile +++ b/example/01-logical-replication/db-node.dockerfile @@ -7,6 +7,10 @@ RUN apt-get -qq install iproute2 iputils-ping net-tools python3-pip python3-psyc RUN pip3 install microesb --break-system-packages RUN pip3 install jsocket --break-system-packages +RUN mkdir /json-rpc-server +COPY ./db-node-rpc/*.py /json-rpc-server/ +COPY ./db-node-rpc/*.sh /json-rpc-server/ + ENV POSTGRES_USER postgres ENV POSTGRES_PASSWORD password ENV POSTGRES_DB lb-test diff --git a/example/01-logical-replication/docker-build.sh b/example/01-logical-replication/docker-build.sh index 80819f3..6ce5713 100755 --- a/example/01-logical-replication/docker-build.sh +++ b/example/01-logical-replication/docker-build.sh @@ -2,4 +2,3 @@ # build docker database node docker build -t db-node --file ./db-node.dockerfile . - diff --git a/example/01-logical-replication/run-container.sh b/example/01-logical-replication/run-container.sh index fcb19c4..d380b94 100755 --- a/example/01-logical-replication/run-container.sh +++ b/example/01-logical-replication/run-container.sh @@ -1,5 +1,6 @@ #!/bin/sh node_id=$1 node_ip=$2 +node_net=$3 -docker run --rm -d --name ${node_id} --net dbpool-net --ip ${node_ip} db-node +docker run --rm -d --name ${node_id} --ip ${node_ip} --net ${node_net} db-node From 1f0da9c7b13c80c31ebcdc89a7595d9bc6d13cd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 26 Jan 2026 11:03:44 +0100 Subject: [PATCH 09/20] Add service metadata templates --- .../svc_call_metadata.py | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 example/01-logical-replication/svc_call_metadata.py diff --git a/example/01-logical-replication/svc_call_metadata.py b/example/01-logical-replication/svc_call_metadata.py new file mode 100644 index 0000000..572b39b --- /dev/null +++ b/example/01-logical-replication/svc_call_metadata.py @@ -0,0 +1,60 @@ +update_net_topology = { + 'SYSServiceID': 'UpdateNetworkTopology', + 'data': [ + { + 'SYSBackendMethod': { 'NetworkTopology': 'update' }, + 'System': { + 'id': 'db-loadbalancing-test', + 'NetworkTopology': { + 'NetIPv4': {}, + 'HostNode': [] + } + } + } + ] +} + +set_global_db_properties = { + 'SYSServiceID': 'InitDatabase', + 'data': [ + { + 'SYSBackendMethod': { 'Database': 'init_db' }, + 'Database': { + 'id': 'lb-test' + } + } + ] +} + +create_repl_table = { + 'SYSServiceID': 'CreateReplicaTable', + 'data': [ + { + 'SYSBackendMethod': { 'Database': 'create_replica_table' }, + 'Database': { + 'id': 'lb-test', + 'Table': { + 'name': 'table1', + 'add_timestamp_cols': True, + 'attach_replication_trigger': True, + 'Column': [ + { + 'name': 'id', + 'type': 'serial', + 'primary_key': True + }, + { + 'name': 'col1', + 'type': 'varchar', + 'default': 'default-value' + }, + { + 'name': 'col2', + 'type': 'varchar' + } + ] + } + } + } + ] +} From bc2cacd5e6eafde545907ceee98573d1d3ecfa8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 26 Jan 2026 11:04:07 +0100 Subject: [PATCH 10/20] Add main orchestrator --- .../01-logical-replication/orchestrator.py | 50 ++++++++++++++++--- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/example/01-logical-replication/orchestrator.py b/example/01-logical-replication/orchestrator.py index 4e4be62..d9f3e0d 100644 --- a/example/01-logical-replication/orchestrator.py +++ b/example/01-logical-replication/orchestrator.py @@ -1,5 +1,10 @@ import json +import ipcalc import jsocket +import subprocess + +import svc_call_metadata + def mm_connect(dst_address, dst_port=64000): client = jsocket.JsonClient(address=dst_address, port=dst_port) @@ -13,18 +18,49 @@ def mm_send(client_ref, payload): def mm_close(client_ref): client_ref.close() + sysconfig = None with open('sysconfig.json', 'r') as fh: sysconfig = json.loads(fh.read()) +network = sysconfig['system']['networks'][0] + +network_id = network['id'] +network_config = network['config'] + +network_segment = '{}/{}'.format( + network_config['net']['ipv4']['subnet'], + network_config['net']['ipv4']['netbits'] +) + +network_ipv4_addresses = iter(ipcalc.Network(network_segment)) -sysconfig +svc_net_topology = svc_call_metadata.update_net_topology['data'][0]['System']['NetworkTopology'] +svc_net_topology['NetIPv4'] = network_config['net']['ipv4'] -{ - "dbpool-net-0": { - "status": "active", - "metrics": { - } +# start containers +for i in range(0, 3): + + node_id = 'node-'+str(i) + node_ip = next(network_ipv4_addresses) + + node_cfg = { + 'name': node_id, + 'ipv4': node_ip } -} + + svc_net_topology['HostNode'].append(node_cfg) + + cmd_run_container = [] + cmd_run_container.append('./run-container.sh') + cmd_run_container.append(node_id) + cmd_run_container.append(str(node_ip)) + cmd_run_container.append(network['id']) + + subprocess.run(cmd_run_container, capture_output=True) + + cmd_start_server = 'docker exec {} /json-rpc-server/start-server.sh'.format(node_id) + res = subprocess.run(cmd_start_server, shell=True, capture_output=True) + test = res.stdout.strip() + print(test) From 34c7f629ceb4dc9d4afc821e21a3f6bb8d08ceee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 26 Jan 2026 11:04:28 +0100 Subject: [PATCH 11/20] Update config --- example/01-logical-replication/sysconfig.json | 63 +++++++++---------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/example/01-logical-replication/sysconfig.json b/example/01-logical-replication/sysconfig.json index 46167a6..fb072b1 100644 --- a/example/01-logical-replication/sysconfig.json +++ b/example/01-logical-replication/sysconfig.json @@ -1,11 +1,10 @@ { "system": { "config": { - "global": { - "managers": { - "failover_mode": false, - "run_on_host": true - } + "network": { + "failover_mode": false, + "run_on_host": true, + "managers": [ "172.16.1.254" ] }, "roles": { "SYSOwner": { @@ -14,40 +13,40 @@ }, "SYSPgDBReplication": { "id": "replicator", - "password": "secure" + "password": "replicator" } } }, "networks": [ { - "dbpool-net": { - "config": { - "net": { - "ipv4": { - "sbunet": "192.168.10.0", - "netmask": "255.255.255.0", - "netbits": 24, - "gateway": "192.168.10.254" - }, - "parts": { - "local-1": { - "ipv4": { - "start": 1, - "end": 252, - "managers": [ 253 ] - } - } - } - }, - "permissions": { - "SYSOwner": "admin" + "id": "dbpool-net", + "config": { + "net": { + "ipv4": { + "subnet": "172.16.1.0", + "netmask": "255.255.255.0", + "netbits": 24, + "gateway": "172.16.1.254", + "hostaddress": "172.16.1.254" }, - "scale": { - "min-nodes": 2, - "max-nodes": 4, - "metrics": { - "characteristics": "default" + "parts": [ + { + "id": "dbpool-net-1", + "ipv4": { + "start": 1, + "end": 253 + } } + ] + }, + "permissions": { + "SYSOwner": "admin" + }, + "scale": { + "min-nodes": 2, + "max-nodes": 4, + "metrics": { + "characteristics": "default" } } } From 75b90c43fbd3f64cd345c3b6d17e6c6ed051cd42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 26 Jan 2026 11:04:44 +0100 Subject: [PATCH 12/20] Add json-rpc server component --- .../db-node-rpc/json-rpc-server.py | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 example/01-logical-replication/db-node-rpc/json-rpc-server.py diff --git a/example/01-logical-replication/db-node-rpc/json-rpc-server.py b/example/01-logical-replication/db-node-rpc/json-rpc-server.py new file mode 100644 index 0000000..4ded7d4 --- /dev/null +++ b/example/01-logical-replication/db-node-rpc/json-rpc-server.py @@ -0,0 +1,52 @@ +import sys +import jsocket +import logging +import subprocess + +from microesb import microesb + +from class_reference import references as class_reference +from service_properties import service_properties +from class_mapping import class_mapping + +logging.getLogger().addHandler( + logging.StreamHandler(sys.stdout) +) + +logging.getLogger().setLevel( + logging.DEBUG +) + + +def get_current_ip_address(): + cmd_get_ip = 'ip -h addr show dev eth0 | grep inet | cut -d " " -f 6' + res = subprocess.run(cmd_get_ip, shell=True, capture_output=True) + raw_ip = res.stdout.strip() + raw_ip_sep = raw_ip.find(b'/') + return raw_ip[:raw_ip_sep] + + +class JSONServer(jsocket.ThreadedServer): + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + def _process_message(self, call_obj): + if isinstance(call_obj, dict): + class_mapper = microesb.ClassMapper( + class_references=class_reference[call_object['SYSServiceID']], + class_mappings=class_mapping, + class_properties=service_properties + ) + return microesb.ServiceExecuter().execute_get_hierarchy( + class_mapper=class_mapper, + service_data=call_obj + ) + return None + + +server = JSONServer( + address=get_current_ip_address(), + port=64000) + +server.start() From 3676b5f7957a0ec2d728f864d497bf7548b7909d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 26 Jan 2026 11:05:18 +0100 Subject: [PATCH 13/20] Add server start script --- example/01-logical-replication/db-node-rpc/start-server.sh | 2 ++ 1 file changed, 2 insertions(+) create mode 100755 example/01-logical-replication/db-node-rpc/start-server.sh diff --git a/example/01-logical-replication/db-node-rpc/start-server.sh b/example/01-logical-replication/db-node-rpc/start-server.sh new file mode 100755 index 0000000..3ebf1fa --- /dev/null +++ b/example/01-logical-replication/db-node-rpc/start-server.sh @@ -0,0 +1,2 @@ +#!/bin/sh +cd /json-rpc-server && python3 ./json-rpc-server.py & From 6af53deb6d59327b7491c1c7a6c3eb48b54a7dc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Mon, 26 Jan 2026 11:05:41 +0100 Subject: [PATCH 14/20] Add ESB metadata / processing logic --- .../db-node-rpc/class_mapping.py | 10 ++ .../db-node-rpc/class_reference.py | 43 ++++++ .../db-node-rpc/esbconfig.py | 12 ++ .../db-node-rpc/service_implementation.py | 68 +++++++++ .../db-node-rpc/service_properties.py | 141 ++++++++++++++++++ 5 files changed, 274 insertions(+) create mode 100644 example/01-logical-replication/db-node-rpc/class_mapping.py create mode 100644 example/01-logical-replication/db-node-rpc/class_reference.py create mode 100644 example/01-logical-replication/db-node-rpc/esbconfig.py create mode 100644 example/01-logical-replication/db-node-rpc/service_implementation.py create mode 100644 example/01-logical-replication/db-node-rpc/service_properties.py diff --git a/example/01-logical-replication/db-node-rpc/class_mapping.py b/example/01-logical-replication/db-node-rpc/class_mapping.py new file mode 100644 index 0000000..0744bb2 --- /dev/null +++ b/example/01-logical-replication/db-node-rpc/class_mapping.py @@ -0,0 +1,10 @@ +class_mapping = { + 'System': 'System', + 'NetworkTopology': 'NetworkTopology', + 'NetIPv4': 'NetIPv4', + 'NetIPv6': 'NetIPv6', + 'HostNode': 'HostNode', + 'Database': 'Database', + 'Table': 'Table', + 'Column': 'Column' +} diff --git a/example/01-logical-replication/db-node-rpc/class_reference.py b/example/01-logical-replication/db-node-rpc/class_reference.py new file mode 100644 index 0000000..04c1fb5 --- /dev/null +++ b/example/01-logical-replication/db-node-rpc/class_reference.py @@ -0,0 +1,43 @@ +references = { + 'UpdateNetworkTopology': { + 'System': { + 'property_ref': 'System', + 'children': { + 'NetworkTopology': { + 'property_ref': 'NetworkTopology', + 'children': { + 'NetIPv4': { + 'property_ref': 'NetIPv4' + }, + 'NetIPv6': { + 'property_ref': 'NetIPv6' + }, + 'HostNode': { + 'property_ref': 'HostNode' + } + } + } + } + } + }, + 'InitDatabase': { + 'Database': { + 'property_ref': 'Database' + } + }, + 'CreateReplicaTable': { + 'Database': { + 'property_ref': 'Database', + 'children': { + 'Table': { + 'property_ref': 'Table', + 'children': { + 'Column': { + 'property_ref': 'Column' + } + } + } + } + } + } +} diff --git a/example/01-logical-replication/db-node-rpc/esbconfig.py b/example/01-logical-replication/db-node-rpc/esbconfig.py new file mode 100644 index 0000000..664216e --- /dev/null +++ b/example/01-logical-replication/db-node-rpc/esbconfig.py @@ -0,0 +1,12 @@ +import_classes = { + 'service_implementation': [ + 'System', + 'NetworkTopology', + 'NetIPv4', + 'NetIPv6', + 'HostNode', + 'Database', + 'Table', + 'Column' + ] +} diff --git a/example/01-logical-replication/db-node-rpc/service_implementation.py b/example/01-logical-replication/db-node-rpc/service_implementation.py new file mode 100644 index 0000000..96aa36a --- /dev/null +++ b/example/01-logical-replication/db-node-rpc/service_implementation.py @@ -0,0 +1,68 @@ +import abc +import logging +import datetime + +from microesb import microesb + +logger = logging.getLogger(__name__) + + +class System(microesb.ClassHandler): + + def __init__(self): + super().__init__() + + +class NetworkTopology(microesb.ClassHandler): + + def __init__(self): + super().__init__() + + def update(self): + pass + + +class NetIPv4(microesb.ClassHandler): + + def __init__(self): + super().__init__() + + +class NetIPv6(microesb.ClassHandler): + + def __init__(self): + super().__init__() + + +class HostNode(microesb.MultiClassHandler): + + def __init__(self): + super().__init__() + + +class Database(microesb.ClassHandler): + + def __init__(self): + super().__init__() + + def init_db(self): + pass + + def create_replica_table(self): + pass + + +class Table(microesb.ClassHandler): + + def __init__(self): + super().__init__() + + +class Column(microesb.MultiClassHandler): + + def __init__(self): + super().__init__() + self.primary_key = False + self.name = None + self.type = None + self.default = None diff --git a/example/01-logical-replication/db-node-rpc/service_properties.py b/example/01-logical-replication/db-node-rpc/service_properties.py new file mode 100644 index 0000000..b7fe20a --- /dev/null +++ b/example/01-logical-replication/db-node-rpc/service_properties.py @@ -0,0 +1,141 @@ +service_properties = { + 'System': { + 'properties': { + 'id': { + 'type': 'str', + 'default': None, + 'required': True, + 'description': 'System id' + } + } + }, + 'NetworkTopology': { + 'properties': { + 'type': { + 'type': 'str', + 'default': 'un-partitioned', + 'required': False, + 'description': 'Network topology type' + } + }, + 'methods': [ + 'update' + ] + }, + 'NetIPv4': { + 'properties': { + 'subnet': { + 'type': 'str', + 'required': True, + 'description': 'IPv4 subnet' + }, + 'netmask': { + 'type': 'str', + 'required': True, + 'description': 'IPv4 netmask' + }, + 'netbits': { + 'type': 'int', + 'required': True, + 'description': 'IPv4 netmask bits' + }, + 'gateway': { + 'type': 'str', + 'required': True, + 'description': 'IPv4 gateway address' + }, + 'hostaddress': { + 'type': 'str', + 'required': False, + 'description': 'IPv4 docker host address' + } + } + }, + 'NetIPv6': { + 'properties': { + } + }, + 'HostNode': { + 'properties': { + 'name': { + 'type': 'str', + 'required': True, + 'description': 'Hostname' + }, + 'ipv4': { + 'type': 'str', + 'required': False, + 'description': 'IPv4 address' + }, + 'ipv6': { + 'type': 'str', + 'required': False, + 'description': 'IPv6 address' + } + } + }, + 'Database': { + 'properties': { + 'id': { + 'type': 'str', + 'default': None, + 'required': True, + 'description': 'Database id' + } + }, + 'methods': [ + 'init_db', + 'create_replica_table' + ] + }, + 'Table': { + 'properties': { + 'name': { + 'type': 'str', + 'default': None, + 'required': True, + 'description': 'Table name' + }, + 'add_timestamp_cols': { + 'type': 'bool', + 'default': True, + 'required': False, + 'description': 'Automatically add timestamp columns for insert and update' + }, + 'attach_replication_trigger': { + 'type': 'bool', + 'default': True, + 'required': False, + 'description': 'Automatically attach replication check trigger (currently only update)' + }, + } + }, + 'Column': { + 'properties': { + 'primary_key': { + 'type': 'bool', + 'default': False, + 'required': False, + 'description': 'Column primary key flag' + }, + 'name': { + 'type': 'str', + 'default': None, + 'required': True, + 'description': 'Column id' + }, + 'type': { + 'type': 'str', + 'default': None, + 'required': True, + 'description': 'Column type' + }, + 'default': { + 'type': 'str', + 'default': None, + 'required': False, + 'description': 'Column default value' + } + } + } +} From d5d8c059b3ed3fd60f22a59477f8409ce85077e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Fri, 30 Jan 2026 05:55:54 +0100 Subject: [PATCH 15/20] Replace jsocket with local cleaned-up (working) version --- example/01-logical-replication/db-node.dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/example/01-logical-replication/db-node.dockerfile b/example/01-logical-replication/db-node.dockerfile index 449ee54..565c696 100644 --- a/example/01-logical-replication/db-node.dockerfile +++ b/example/01-logical-replication/db-node.dockerfile @@ -4,8 +4,10 @@ MAINTAINER Claus Prüfer RUN apt-get -qq update -y RUN apt-get -qq install iproute2 iputils-ping net-tools python3-pip python3-psycopg2 -y +COPY ./packages/jsocket-1.9.5.tar.gz / + RUN pip3 install microesb --break-system-packages -RUN pip3 install jsocket --break-system-packages +RUN pip3 install ./jsocket-1.9.5.tar.gz --break-system-packages RUN mkdir /json-rpc-server COPY ./db-node-rpc/*.py /json-rpc-server/ From ee47dcd933012d9ab84be25e8278e9e5c5ece99d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Fri, 30 Jan 2026 05:56:39 +0100 Subject: [PATCH 16/20] Fix bugs --- example/01-logical-replication/orchestrator.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/example/01-logical-replication/orchestrator.py b/example/01-logical-replication/orchestrator.py index d9f3e0d..a6b34b1 100644 --- a/example/01-logical-replication/orchestrator.py +++ b/example/01-logical-replication/orchestrator.py @@ -18,12 +18,11 @@ def mm_send(client_ref, payload): def mm_close(client_ref): client_ref.close() - -sysconfig = None - +# load configuration with open('sysconfig.json', 'r') as fh: sysconfig = json.loads(fh.read()) +# model config parts network = sysconfig['system']['networks'][0] network_id = network['id'] @@ -34,6 +33,7 @@ def mm_close(client_ref): network_config['net']['ipv4']['netbits'] ) +# make network segment iterator network_ipv4_addresses = iter(ipcalc.Network(network_segment)) svc_net_topology = svc_call_metadata.update_net_topology['data'][0]['System']['NetworkTopology'] @@ -47,7 +47,7 @@ def mm_close(client_ref): node_cfg = { 'name': node_id, - 'ipv4': node_ip + 'ipv4': str(node_ip) } svc_net_topology['HostNode'].append(node_cfg) @@ -61,6 +61,10 @@ def mm_close(client_ref): subprocess.run(cmd_run_container, capture_output=True) cmd_start_server = 'docker exec {} /json-rpc-server/start-server.sh'.format(node_id) - res = subprocess.run(cmd_start_server, shell=True, capture_output=True) - test = res.stdout.strip() - print(test) + res = subprocess.run(cmd_start_server, shell=True, capture_output=True, check=True) + +for node in svc_net_topology['HostNode']: + + client = mm_connect(node['ipv4']) + res = mm_send(client, svc_call_metadata.update_net_topology, check=True) + print(res) From 74a442e604bd9813f58fa7b7f8e24edf6e21a38f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Fri, 30 Jan 2026 05:57:04 +0100 Subject: [PATCH 17/20] Add missing default values --- example/01-logical-replication/svc_call_metadata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/01-logical-replication/svc_call_metadata.py b/example/01-logical-replication/svc_call_metadata.py index 572b39b..5af979f 100644 --- a/example/01-logical-replication/svc_call_metadata.py +++ b/example/01-logical-replication/svc_call_metadata.py @@ -2,7 +2,7 @@ 'SYSServiceID': 'UpdateNetworkTopology', 'data': [ { - 'SYSBackendMethod': { 'NetworkTopology': 'update' }, + 'SYSBackendMethod': { 'System': 'update_network_topology' }, 'System': { 'id': 'db-loadbalancing-test', 'NetworkTopology': { From acdcb348ed63913fd81b7432c6cf8b61a77d12c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Fri, 30 Jan 2026 05:57:36 +0100 Subject: [PATCH 18/20] Update (bug fix, working with modified jsocket) --- .../db-node-rpc/json-rpc-server.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/example/01-logical-replication/db-node-rpc/json-rpc-server.py b/example/01-logical-replication/db-node-rpc/json-rpc-server.py index 4ded7d4..d7e275a 100644 --- a/example/01-logical-replication/db-node-rpc/json-rpc-server.py +++ b/example/01-logical-replication/db-node-rpc/json-rpc-server.py @@ -26,27 +26,28 @@ def get_current_ip_address(): return raw_ip[:raw_ip_sep] -class JSONServer(jsocket.ThreadedServer): +class JSONServer(jsocket.JsonServer): def __init__(self, **kwargs): super().__init__(**kwargs) def _process_message(self, call_obj): if isinstance(call_obj, dict): + print(call_obj) class_mapper = microesb.ClassMapper( - class_references=class_reference[call_object['SYSServiceID']], + class_references=class_reference[call_obj['SYSServiceID']], class_mappings=class_mapping, class_properties=service_properties ) - return microesb.ServiceExecuter().execute_get_hierarchy( + res = microesb.ServiceExecuter().execute_get_hierarchy( class_mapper=class_mapper, service_data=call_obj ) - return None + return res[0]['System']['object_instance'].json_dict + return { "Status": "NoObject received" } server = JSONServer( address=get_current_ip_address(), - port=64000) - -server.start() + port=64000 +).server_loop() From 81999a1c663933e1ab762ff3f2e3e3b570a0c54e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Fri, 30 Jan 2026 05:59:13 +0100 Subject: [PATCH 19/20] Add missing default values --- .../db-node-rpc/service_properties.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/example/01-logical-replication/db-node-rpc/service_properties.py b/example/01-logical-replication/db-node-rpc/service_properties.py index b7fe20a..07ee7cd 100644 --- a/example/01-logical-replication/db-node-rpc/service_properties.py +++ b/example/01-logical-replication/db-node-rpc/service_properties.py @@ -7,7 +7,10 @@ 'required': True, 'description': 'System id' } - } + }, + 'methods': [ + 'update_network_topology' + ] }, 'NetworkTopology': { 'properties': { @@ -17,35 +20,37 @@ 'required': False, 'description': 'Network topology type' } - }, - 'methods': [ - 'update' - ] + } }, 'NetIPv4': { 'properties': { 'subnet': { 'type': 'str', + 'default': None, 'required': True, 'description': 'IPv4 subnet' }, 'netmask': { 'type': 'str', + 'default': None, 'required': True, 'description': 'IPv4 netmask' }, 'netbits': { 'type': 'int', + 'default': None, 'required': True, 'description': 'IPv4 netmask bits' }, 'gateway': { 'type': 'str', + 'default': None, 'required': True, 'description': 'IPv4 gateway address' }, 'hostaddress': { 'type': 'str', + 'default': None, 'required': False, 'description': 'IPv4 docker host address' } @@ -59,16 +64,19 @@ 'properties': { 'name': { 'type': 'str', + 'default': None, 'required': True, 'description': 'Hostname' }, 'ipv4': { 'type': 'str', + 'default': None, 'required': False, 'description': 'IPv4 address' }, 'ipv6': { 'type': 'str', + 'default': None, 'required': False, 'description': 'IPv6 address' } From f3c316ac2979a8884176b014555af3d8cad2a034 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus=20Pr=C3=BCfer?= Date: Fri, 30 Jan 2026 05:59:44 +0100 Subject: [PATCH 20/20] Disable iptables globally in docker-daemon --- example/01-logical-replication/docker-daemon/daemon.json | 1 - 1 file changed, 1 deletion(-) diff --git a/example/01-logical-replication/docker-daemon/daemon.json b/example/01-logical-replication/docker-daemon/daemon.json index 7e0c4a1..326fe13 100644 --- a/example/01-logical-replication/docker-daemon/daemon.json +++ b/example/01-logical-replication/docker-daemon/daemon.json @@ -1,4 +1,3 @@ { "iptables": false } -