Search code examples
pythonpyro

Why does pyro4 fail to locate nameserver under 127.0.1.1 but succeed with 127.0.0.1?


I am trying to run a pyro4 server with a custom event loop on a Raspberry Pi running Raspbian 8 (jessie). When I create a nameserver using the hostname obtained from socket.gethostname(), specifically 'raspberrypi', my client script cannot find the nameserver. When I use 'localhost' as the hostname, my client script is able to find the hostname. In /etc/hosts, 'raspberrypi' is bound to 127.0.1.1, while 'localhost' is obviously bound to 127.0.0.1. I had thought that both of these addresses were bound to the loopback interface, so I don't understand why one should work and not the other.

For what it's worth, after some digging in the pyro4 code, it looks like at l.463 of Pyro4.naming.py, the call to proxy.ping() fails with 127.0.1.1 but not with 127.0.0.1, and this is ultimately what triggers the failure with the former address. Not being an expert in Pyro, it isn't clear to be whether this behavior is expected. Any thoughts? I assume this must be a common problem because most (all?) flavors of Debian include separate lines in /etc/hosts for these two addresses.

I have attached code below that reproduces the problem. This is basically just a slightly modified version of the "eventloop" example that ships with pyro.

server.py:

import socket
import select
import sys
import Pyro4.core
import Pyro4.naming

import MotorControl

Pyro4.config.SERVERTYPE="thread"
hostname=socket.gethostname()

print("initializing services... servertype=%s" % Pyro4.config.SERVERTYPE)
# start a name server with broadcast server as well
nameserverUri, nameserverDaemon, broadcastServer = Pyro4.naming.startNS(host=hostname)
pyrodaemon=Pyro4.core.Daemon(host=hostname)

motorcontroller = MotorControl.MotorControl()
serveruri=pyrodaemon.register(motorcontroller)
nameserverDaemon.nameserver.register("example.embedded.server",serveruri)

# below is our custom event loop.
while True:
    nameserverSockets = set(nameserverDaemon.sockets)
    pyroSockets = set(pyrodaemon.sockets)
    rs = []
    rs.extend(nameserverSockets)
    rs.extend(pyroSockets)

    rs,_,_ = select.select(rs,[],[], 0.001)

    eventsForNameserver=[]
    eventsForDaemon=[]
    for s in rs:
        if s in nameserverSockets:
            eventsForNameserver.append(s)
        elif s in pyroSockets:
            eventsForDaemon.append(s)
    if eventsForNameserver:
        nameserverDaemon.events(eventsForNameserver)
    if eventsForDaemon:
        pyrodaemon.events(eventsForDaemon)

    motorcontroller.increment_count()

nameserverDaemon.close()
broadcastServer.close()
pyrodaemon.close()

client.py:

from __future__ import print_function
import Pyro4

proxy=Pyro4.core.Proxy("PYRONAME:example.embedded.server")
print("count = %d" % proxy.get_count())

MotorControl.py

class MotorControl(object):
    def __init__(self):
        self.switches = 0

    def get_count(self):
        return self.switches

    def increment_count(self):
        self.switches = self.switches + 1

error:

Traceback (most recent call last):
  File "pyroclient.py", line 5, in <module>
    print("count = %d" % proxy.get_count())
  File "/usr/local/lib/python2.7/dist-packages/Pyro4/core.py", line 248, in __getattr__
    self._pyroGetMetadata()
  File "/usr/local/lib/python2.7/dist-packages/Pyro4/core.py", line 548, in _pyroGetMetadata
    self.__pyroCreateConnection()
  File "/usr/local/lib/python2.7/dist-packages/Pyro4/core.py", line 456, in __pyroCreateConnection
    uri = resolve(self._pyroUri, self._pyroHmacKey)
  File "/usr/local/lib/python2.7/dist-packages/Pyro4/naming.py", line 548, in resolve
    nameserver = locateNS(uri.host, uri.port, hmac_key=hmac_key)
  File "/usr/local/lib/python2.7/dist-packages/Pyro4/naming.py", line 528, in locateNS
    raise e
Pyro4.errors.NamingError: Failed to locate the nameserver

Solution

  • Pyro's name server lookup relies on two things:

    • broadcast lookup
    • direct lookup by hostname/ip-address

    The first is not available when you're using the loopback adapter to bind the name server on (loopback doesn't support broadcast sockets). So we're left with the second one. The answer to your question is then simple: the direct lookup is done on the value of the NS_HOST config item, which is by default set to 'localhost'. As localhost resolves to 127.0.0.1 it will never connect to 127.0.1.1.

    Suggestion: bind the name server on 0.0.0.0 or "" (empty hostname) and it should be able to start a broadcast responder as well. Then your clients won't have any problem locating it.

    Alternatively, simply set NS_HOST to 127.0.1.1 (or the hostname of your box) for your clients and they should be able to locate the name server as well if it's bound on 127.0.1.1