[Checkins] SVN: zc.zk/trunk/s Improved ``register_server`` in the case when an empty host is
Jim Fulton
jim at zope.com
Wed Jan 25 20:20:28 UTC 2012
Log message for revision 124172:
Improved ``register_server`` in the case when an empty host is
passed. If `netifaces
<http://alastairs-place.net/projects/netifaces/>`_ is installed,
``register_server`` registers all of the IPv4 addresses [#ifaces]_.
Changed:
U zc.zk/trunk/setup.py
U zc.zk/trunk/src/zc/zk/README.txt
U zc.zk/trunk/src/zc/zk/__init__.py
U zc.zk/trunk/src/zc/zk/tests.py
-=-
Modified: zc.zk/trunk/setup.py
===================================================================
--- zc.zk/trunk/setup.py 2012-01-25 15:14:41 UTC (rev 124171)
+++ zc.zk/trunk/setup.py 2012-01-25 20:20:27 UTC (rev 124172)
@@ -16,7 +16,7 @@
install_requires = ['setuptools', 'zc.thread']
extras_require = dict(
test=['zope.testing', 'zc-zookeeper-static', 'mock', 'manuel',
- 'zope.event'],
+ 'zope.event', 'netifaces'],
static=['zc-zookeeper-static'],
)
Modified: zc.zk/trunk/src/zc/zk/README.txt
===================================================================
--- zc.zk/trunk/src/zc/zk/README.txt 2012-01-25 15:14:41 UTC (rev 124171)
+++ zc.zk/trunk/src/zc/zk/README.txt 2012-01-25 20:20:27 UTC (rev 124172)
@@ -636,7 +636,6 @@
ommitted, then the refering name is used. For example, the name could
be left off of the property link above.
-
Node deletion
=============
@@ -652,16 +651,14 @@
==========================================
It's common to use an empty string for a host name when calling bind
-to listen on all IPv4 interfaces. If you pass an empty host name to
-``register_server``, the result of calling ``socket.getfqdn()`` will
-be registered::
+to listen on all IPv4 interfaces. If you pass an address with an
+empty host to ``register_server`` and `netifaces
+<http://alastairs-place.net/projects/netifaces/>`_ is installed, then
+all of the IPv4 addresses [#ifaces]_ (for the given port) will be
+registered. If netifaces isn't installed and you pass an empty host
+name, then the fully-qualified domain name, as returned by
+``socket.getfqdn()`` will be used for the host.
- >>> zk.register_server('/fooservice/providers', ('', 42))
- addresses changed
- ['192.168.0.42:8080', '192.168.0.42:8081', '192.168.0.42:8082',
- 'server.example.com:42']
-
-
Server-registration events
==========================
@@ -713,8 +710,6 @@
pid = 7981
/192.168.0.42:8082
pid = 7981
- /server.example.com:42
- pid = 7981
.. -> sh
@@ -1026,6 +1021,11 @@
0.6.0 (2012-01-??)
------------------
+- Improved ``register_server`` in the case when an empty host is
+ passed. If `netifaces
+ <http://alastairs-place.net/projects/netifaces/>`_ is installed,
+ ``register_server`` registers all of the IPv4 addresses [#ifaces]_.
+
- ``delete_recursive`` now has a ``force`` argument to force deletion of
ephemeral nodes.
@@ -1132,3 +1132,15 @@
.. test cleanup
>>> zk.close()
+
+
+----------------------------------------------------------------------
+
+.. [#ifaces] It's a little more complicated. If there are non-local
+ interfaces, then only non-local addresses are registered. In
+ normal production, there's really no point in registering local
+ addresses, as clients on other machines can't make any sense of
+ them. If *only* local interfaces are found, then local addresses
+ are registered, under the assumption that someone is developing on
+ a disconnected computer.
+
Modified: zc.zk/trunk/src/zc/zk/__init__.py
===================================================================
--- zc.zk/trunk/src/zc/zk/__init__.py 2012-01-25 15:14:41 UTC (rev 124171)
+++ zc.zk/trunk/src/zc/zk/__init__.py 2012-01-25 20:20:27 UTC (rev 124172)
@@ -207,21 +207,44 @@
raise FailedConnect(connection_string)
+ def _findallipv4addrs(self, tail):
+ try:
+ import netifaces
+ except ImportError:
+ return [socket.getfqdn()+tail]
+
+ addrs = set()
+ loopaddrs = set()
+ for iface in netifaces.interfaces():
+ for info in netifaces.ifaddresses(iface).get(2, ()):
+ addr = info.get('addr')
+ if addr:
+ if addr.startswith('127.'):
+ loopaddrs.add(addr+tail)
+ else:
+ addrs.add(addr+tail)
+
+ return addrs or loopaddrs
+
def register_server(self, path, addr, acl=READ_ACL_UNSAFE, **kw):
kw['pid'] = os.getpid()
- if isinstance(addr, str):
- if addr[:1] == ':':
- addr = socket.getfqdn()+addr
+
+ if not isinstance(addr, str):
+ addr = '%s:%s' % tuple(addr)
+
+ if addr[:1] == ':':
+ addrs = self._findallipv4addrs(addr)
else:
- if addr[0] == '':
- addr = socket.getfqdn(), addr[1]
- addr = '%s:%s' % addr
+ addrs = (addr,)
+
path = self.resolve(path)
zc.zk.event.notify(RegisteringServer(addr, path, kw))
if path != '/':
path += '/'
- self.create(path + addr, encode(kw), acl, zookeeper.EPHEMERAL)
+ for addr in addrs:
+ self.create(path + addr, encode(kw), acl, zookeeper.EPHEMERAL)
+
test_sleep = 0
def _async(self, completion, meth, *args):
post = getattr(self, '_post_'+meth)
Modified: zc.zk/trunk/src/zc/zk/tests.py
===================================================================
--- zc.zk/trunk/src/zc/zk/tests.py 2012-01-25 15:14:41 UTC (rev 124171)
+++ zc.zk/trunk/src/zc/zk/tests.py 2012-01-25 20:20:27 UTC (rev 124172)
@@ -21,6 +21,7 @@
import mock
import os
import re
+import socket
import StringIO
import sys
import threading
@@ -135,7 +136,124 @@
self.assertEqual(flags, zookeeper.EPHEMERAL)
self.__zk.register_server('/foo', ('127.0.0.1', 8080), a=1)
+ self.__zk.register_server('/foo', '127.0.0.1:8080', a=1)
+ @mock.patch('netifaces.ifaddresses')
+ @mock.patch('netifaces.interfaces')
+ @mock.patch('zookeeper.create')
+ def test_register_server_blank(self, create, interfaces, ifaddresses):
+ addrs = {
+ 'eth0': {2: [{'addr': '192.168.24.60',
+ 'broadcast': '192.168.24.255',
+ 'netmask': '255.255.255.0'}],
+ 10: [{'addr': 'fe80::21c:c0ff:fe1a:d12%eth0',
+ 'netmask': 'ffff:ffff:ffff:ffff::'}],
+ 17: [{'addr': '00:1c:c0:1a:0d:12',
+ 'broadcast': 'ff:ff:ff:ff:ff:ff'}]},
+ 'foo': {2: [{'addr': '192.168.24.61',
+ 'broadcast': '192.168.24.255',
+ 'netmask': '255.255.255.0'}],
+ },
+ 'lo': {2: [{'addr': '127.0.0.1',
+ 'netmask': '255.0.0.0',
+ 'peer': '127.0.0.1'}],
+ 10: [{'addr': '::1',
+ 'netmask': 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'}],
+ 17: [{'addr': '00:00:00:00:00:00',
+ 'peer': '00:00:00:00:00:00'}],
+ },
+ }
+
+ @side_effect(interfaces)
+ def _():
+ return list(addrs)
+
+ @side_effect(ifaddresses)
+ def _(iface):
+ return addrs[iface]
+
+ @side_effect(create)
+ def _(handle, path_, data, acl, flags):
+ self.assertEqual(handle, 0)
+ paths.append(path_)
+ self.assertEqual(json.loads(data), dict(pid=os.getpid(), a=1))
+ self.assertEqual(acl, [zc.zk.world_permission()])
+ self.assertEqual(flags, zookeeper.EPHEMERAL)
+
+ paths = []
+ self.__zk.register_server('/foo', ('', 8080), a=1)
+ self.assertEqual(sorted(paths),
+ ['/foo/192.168.24.60:8080', '/foo/192.168.24.61:8080'])
+
+ paths = []
+ self.__zk.register_server('/foo', ':8080', a=1)
+ self.assertEqual(sorted(paths),
+ ['/foo/192.168.24.60:8080', '/foo/192.168.24.61:8080'])
+
+ @mock.patch('netifaces.ifaddresses')
+ @mock.patch('netifaces.interfaces')
+ @mock.patch('zookeeper.create')
+ def test_register_server_blank_nonet(self, create, interfaces, ifaddresses):
+ addrs = {
+ 'lo': {2: [{'addr': '127.0.0.1',
+ 'netmask': '255.0.0.0',
+ 'peer': '127.0.0.1'}],
+ 10: [{'addr': '::1',
+ 'netmask': 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'}],
+ 17: [{'addr': '00:00:00:00:00:00',
+ 'peer': '00:00:00:00:00:00'}]},
+ }
+
+ @side_effect(interfaces)
+ def _():
+ return list(addrs)
+
+ @side_effect(ifaddresses)
+ def _(iface):
+ return addrs[iface]
+
+ @side_effect(create)
+ def _(handle, path_, data, acl, flags):
+ self.assertEqual(handle, 0)
+ paths.append(path_)
+ self.assertEqual(json.loads(data), dict(pid=os.getpid(), a=1))
+ self.assertEqual(acl, [zc.zk.world_permission()])
+ self.assertEqual(flags, zookeeper.EPHEMERAL)
+
+ paths = []
+ self.__zk.register_server('/foo', ('', 8080), a=1)
+ self.assertEqual(sorted(paths), ['/foo/127.0.0.1:8080'])
+
+ paths = []
+ self.__zk.register_server('/foo', ':8080', a=1)
+ self.assertEqual(sorted(paths), ['/foo/127.0.0.1:8080'])
+
+ @mock.patch('zookeeper.create')
+ @mock.patch('socket.getfqdn')
+ def test_register_server_blank_nonetifaces(self, getfqdn, create):
+
+ netifaces = sys.modules['netifaces']
+ try:
+ sys.modules['netifaces'] = None
+
+ @side_effect(getfqdn)
+ def _():
+ return 'nonexistenttestserver.zope.com'
+
+ @side_effect(create)
+ def _(handle, path_, data, acl, flags):
+ self.assertEqual(
+ (handle, path_),
+ (0, '/foo/nonexistenttestserver.zope.com:8080'))
+ self.assertEqual(json.loads(data), dict(pid=os.getpid(), a=1))
+ self.assertEqual(acl, [zc.zk.world_permission()])
+ self.assertEqual(flags, zookeeper.EPHEMERAL)
+
+ self.__zk.register_server('/foo', ('', 8080), a=1)
+ self.__zk.register_server('/foo', ':8080', a=1)
+ finally:
+ sys.modules['netifaces'] = netifaces
+
@mock.patch('zookeeper.close')
@mock.patch('zookeeper.init')
@mock.patch('zookeeper.state')
More information about the checkins
mailing list