Source code for pyrolab.nameserver
# Copyright © PyroLab Project Contributors
# Licensed under the terms of the GNU GPLv3+ License
# (see pyrolab/__init__.py for details)
"""
Nameserver
==========
Wrapped nameserver functions that references PyroLab configuration settings.
"""
import logging
import socket
import sys
from typing import Callable
from Pyro5.nameserver import BroadcastServer, NameServerDaemon, start_ns
from pyrolab import NAMESERVER_STORAGE
from pyrolab.configure import NameServerConfiguration
log = logging.getLogger(__name__)
STORAGE_FILE = NAMESERVER_STORAGE / "storage"
# # Inheriting from the Nameserver
# class NameServer(Pyro5.nameserver.NameServer):
# """
# PyroLab specific NameServer with custom functionality including the ability
# to reject duplicate registration names.
# """
# pass
[docs]
def start_ns_loop(
cfg: NameServerConfiguration, loop_condition: Callable = lambda: True
) -> None:
"""
Utility function that starts a new NameServer and enters its requestloop.
This function is a reimplemntation of the ``Pyro5.nameserver.start_ns_loop``
that allows for a loop condition to kill the loop. Alternatively, the loop
can be shut down using ``ctrl+c``.
Parameters
----------
cfg : NameserverConfiguration
The configuration object for the nameserver.
loop_condition : callable, optional
A callable that returns a boolean value. If the value is True, the loop
will continue. If the value is False, the loop will stop. Defaults to
``lambda: True``.
"""
# Parameters from the original function
host = cfg.host
port = cfg.ns_port
unixsocket = None
nathost = None
natport = None
enableBroadcast = cfg.broadcast
bchost = cfg.ns_bchost
bcport = cfg.ns_bcport
storage = cfg.get_storage_location()
cfg.update_pyro_config()
daemon = NameServerDaemon(
unixsocket, nathost=nathost, natport=natport, storage=storage
)
nsUri = daemon.uriFor(daemon.nameserver)
internalUri = daemon.uriFor(daemon.nameserver, nat=False)
bcserver = None
if unixsocket:
hostip = "Unix domain socket"
else:
hostip = daemon.sock.getsockname()[0]
if (
daemon.sock.family == socket.AF_INET6
): # ipv6 doesn't have broadcast. We should probably use multicast instead...
log.info("Not starting NS broadcast server because NS is using IPv6")
enableBroadcast = False
elif hostip.startswith("127.") or hostip == "::1":
log.info(
"Not starting NS broadcast server because NS is bound to localhost"
)
enableBroadcast = False
if enableBroadcast:
# Make sure to pass the internal uri to the broadcast responder.
# It is almost always useless to let it return the external uri,
# because external systems won't be able to talk to this thing anyway.
bcserver = BroadcastServer(
internalUri, bchost, bcport, ipv6=daemon.sock.family == socket.AF_INET6
)
log.info("Broadcast server running on %s" % bcserver.locationStr)
bcserver.runInThread()
existing = daemon.nameserver.count()
if existing > 1: # don't count our own nameserver registration
log.info("Persistent store contains %d existing registrations." % existing)
log.info("NS running on %s (%s)" % (daemon.locationStr, hostip))
if daemon.natLocationStr:
log.info("internal URI = %s" % internalUri)
log.info("external URI = %s" % nsUri)
else:
log.info("URI = %s" % nsUri)
try:
# Placed in a try block because this fails with pythonw.exe
sys.stdout.flush()
except:
log.warning(
"Couldn't flush stdout! (Not a problem if running under pythonw.exe)"
)
try:
daemon.requestLoop(loopCondition=loop_condition)
finally:
daemon.close()
if bcserver is not None:
bcserver.close()
log.info("NS shut down.")
[docs]
def start_ns(cfg: NameServerConfiguration = None):
"""
Utility fuction to quickly get a Nameserver daemon to be used in your own
event loops.
Returns
-------
nameserverUri, nameserverDaemon, broadcastServer
A tuple containing three pieces of information.
"""
return start_ns(
host=cfg.host,
port=cfg.ns_port,
enableBroadcast=cfg.broadcast,
bchost=cfg.bc_host,
bcport=cfg.bc_port,
storage=cfg.storage,
)