diff --git a/common/src/foundation/python-packages/pyproject.toml b/common/src/foundation/python-packages/pyproject.toml
index cfd8d45b6..9c5f3bc72 100644
--- a/common/src/foundation/python-packages/pyproject.toml
+++ b/common/src/foundation/python-packages/pyproject.toml
@@ -108,5 +108,5 @@ cryptography = { appliances = ["server"] }
jinja2 = { appliances = ["server"], bootstrap = true }
markupsafe = { appliances = ["server"] }
pycparser = { appliances = ["server"] }
-pyyaml = { appliances = ["server"], bootstrap = true }
+pyyaml = { appliances = ["server", "client"], bootstrap = true }
six = { appliances = ["server"] }
diff --git a/common/src/stack/command/stack/commands/sync/host/config/plugin_smq.py b/common/src/stack/command/stack/commands/sync/host/config/plugin_smq.py
new file mode 100644
index 000000000..ffde40358
--- /dev/null
+++ b/common/src/stack/command/stack/commands/sync/host/config/plugin_smq.py
@@ -0,0 +1,30 @@
+import stack.commands
+from pathlib import Path
+from stack.util import _exec
+
+class Plugin(stack.commands.Plugin):
+ """
+ Sync smq settings to hosts and restart all smq related services
+ """
+
+ def provides(self):
+ return 'smq'
+
+ def requires(self):
+ return []
+
+ def run(self, args):
+ hosts = args['hosts']
+ settings_file = Path('/opt/stack/etc/stacki.yml')
+ services = ['smq-processor', 'smq-producer', 'smq-publisher', 'smq-shipper']
+
+ for host in hosts:
+ self.owner.notify('Sync SMQ Settings')
+ if settings_file.is_file():
+ copy_settings = _exec(f'scp {settings_file} {host}:{settings_file}', shlexsplit=True)
+ if copy_settings.returncode != 0:
+ self.owner.notify(f'Failed to copy settings file to {host}:{copy_settings.stderr}')
+ for service in services:
+ restart_smq = _exec(f'ssh -t {host} "systemctl restart {service}" ', shlexsplit=True)
+ if restart_smq.returncode != 0:
+ self.owner.notify(f'Failed to restart service {service} on host {host}')
diff --git a/common/src/stack/mq/pylib/mq/__init__.py b/common/src/stack/mq/pylib/mq/__init__.py
index 882075769..3b84448c9 100644
--- a/common/src/stack/mq/pylib/mq/__init__.py
+++ b/common/src/stack/mq/pylib/mq/__init__.py
@@ -10,6 +10,8 @@
import socket
import sys
import os
+import yaml
+from pathlib import Path
# We need the "ports" class when we are inside the installer, but don't need to
# setup and zmq based services. The alternative is to add zmq and dependencies
@@ -23,18 +25,49 @@
-class ports:
+class PortsMeta(type):
"""
Socket port numbers used by the Stack Message Queue daemons.
- :var publish: UDP socket service for publishing a message
- :var subscribe: zmq.SUB socket for subscribing to a channel
- :var control: TCP socket service for enabling/disabling channel propagation
+ :publish: UDP socket service for publishing a message
+ :subscribe: zmq.SUB socket for subscribing to a channel
+ :control: TCP socket service for enabling/disabling channel propagation
"""
- publish = 5000
- subscribe = 5001
- control = 5002
+ # Stacki settings file location
+ # and default ports
+ SETTINGS = Path('/opt/stack/etc/stacki.yml')
+ pub_port = 5000
+ sub_port = 5001
+ con_port = 5002
+
+ def load_settings(cls):
+ config = {}
+ if cls.SETTINGS.is_file():
+ with cls.SETTINGS.open() as f:
+ config = yaml.safe_load(f)
+ return config
+
+ @property
+ def publish(cls):
+ config = cls.load_settings()
+ port = int(config.get('smq.pub.port', cls.pub_port))
+ return port
+
+ @property
+ def subscribe(cls):
+ config = cls.load_settings()
+ port = int(config.get('smq.sub.port', cls.sub_port))
+ return port
+
+ @property
+ def control(cls):
+ config = cls.load_settings()
+ port = int(config.get('smq.control.port', cls.con_port))
+ return port
+
+class ports(metaclass=PortsMeta):
+ pass
class Message():
"""
@@ -42,7 +75,7 @@ class Message():
A Message is composed of header fields and the *message* text body. For many
applications only body is manipulated and other fields are controlled by
- lower software levels.
+ lower software levels.
For simple Messages the *message* body can be a string. For more complex Messages
the body should be a json encoded python dictionary.
@@ -87,7 +120,7 @@ def __init__(self, payload=None, *, message=None, channel=None, hops=None, ttl=N
# JSON does not override parameters, this allows loading an
# existing Message and overwriting some of the fields
-
+
self.channel = channel if channel else msg.get('channel')
self.id = id if id else msg.get('id')
self.payload = payload if payload else msg.get('payload')
@@ -160,7 +193,7 @@ def getPayload(self):
def setPayload(self, payload):
"""
Sets the payload text
-
+
:param payload: text
:type payload: string
"""
@@ -169,7 +202,7 @@ def setPayload(self, payload):
def getHops(self):
"""
- :returns: number of software hops
+ :returns: number of software hops
"""
return self.hops
@@ -189,7 +222,7 @@ def setSource(self, addr):
"""
Set the source host address. This address can be a hostname
or an IP Address.
-
+
:param addr: source address
:type addr: string
"""
@@ -234,9 +267,9 @@ def setID(self, id):
def addHop(self):
"""
Increments the hop count for the :class:`Message`. A hop is
- defined as a software hop not a physical network hop.
+ defined as a software hop not a physical network hop.
Every time an application receives and retransmits a message the
- hop should be incremented.
+ hop should be incremented.
This value is used to debugging.
"""
self.hops += 1
@@ -289,12 +322,12 @@ def unsubscribe(self, channel):
:type channel: string
"""
self.sub.setsockopt_string(zmq.UNSUBSCRIBE, channel)
-
+
def run(self):
while True:
try:
channel, payload = self.sub.recv_multipart()
- msg = Message(message=payload.decode(),
+ msg = Message(message=payload.decode(),
channel=channel.decode())
except:
continue
@@ -341,10 +374,10 @@ def run(self):
#
# Note the callback() always pushes data as
# stack.mq.Message objects. This is the only
- # part of the code where we handle receiving
+ # part of the code where we handle receiving
# unstructured data.
#
- # Design point here was to keep the clients
+ # Design point here was to keep the clients
# simple so we don't need an API to write to
# the message queue.
diff --git a/common/src/stack/pylib/stack/settings.py b/common/src/stack/pylib/stack/settings.py
index f16e30060..41292217e 100644
--- a/common/src/stack/pylib/stack/settings.py
+++ b/common/src/stack/pylib/stack/settings.py
@@ -35,6 +35,11 @@
# these settings control the starting points for discover-nodes (nee insert ethers)
discovery.base.rack: '0'
discovery.base.rank: '0'
+
+# smq ports
+smq.pub.port: '5000'
+smq.sub.port: '5001'
+smq.control.port: '5002'
'''
def get_settings():
diff --git a/redhat/nodes/backend.xml b/redhat/nodes/backend.xml
index 4d26dd6d4..c34a23dcb 100644
--- a/redhat/nodes/backend.xml
+++ b/redhat/nodes/backend.xml
@@ -37,7 +37,7 @@ for o in stack.api.Call('list cart'):
-
+
/opt/stack/bin/stacki-status.py Running preinstall
@@ -50,11 +50,21 @@ include ld.so.conf.d/*.conf
/opt/stack/bin/stacki-status.py install complete rebooting
+
+cat /opt/stack/etc/stacki.yml;
+
+
+
+
+
+import stack.settings
+
+stack.settings.write_default_settings_file(overwrite=True)
cp /run/install/tmp/stack.conf /tmp/stack.conf
-
+
diff --git a/sles/nodes/backend.xml b/sles/nodes/backend.xml
index e38ed3d05..dbb125cd0 100644
--- a/sles/nodes/backend.xml
+++ b/sles/nodes/backend.xml
@@ -1,5 +1,5 @@
-
+
Handle for Backend Appliance
@@ -56,6 +56,11 @@ rm -f /etc/zypp/repos.d/*
&hostname;
+
+
+cat /opt/stack/etc/stacki.yml;
+
+
&hostname;
@@ -69,6 +74,9 @@ rm -f /etc/zypp/repos.d/*
&hostname;
&hostname;
&hostname;
+
+cat /opt/stack/etc/stacki.yml;
+
#
# the above command (report host resolv) writes /etc/resolv.conf. make sure
@@ -102,5 +110,5 @@ rm -rf /tmp/ipmisetup
-
+