Changes in / [d85a75d:b088380] in trunk


Ignore:
Location:
src/allmydata
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • src/allmydata/test/test_iputil.py

    rd85a75d rb088380  
    11
    2 import re
     2import re, errno, subprocess, os
     3
    34from twisted.trial import unittest
     5
    46from allmydata.util import iputil
    57import allmydata.test.common_util as testutil
    68
     9
     10class Namespace:
     11    pass
     12
    713DOTTED_QUAD_RE=re.compile("^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$")
     14
     15MOCK_IPADDR_OUTPUT = """\
     161: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN \n\
     17    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
     18    inet 127.0.0.1/8 scope host lo
     19    inet6 ::1/128 scope host \n\
     20       valid_lft forever preferred_lft forever
     212: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
     22    link/ether d4:3d:7e:01:b4:3e brd ff:ff:ff:ff:ff:ff
     23    inet 192.168.0.6/24 brd 192.168.0.255 scope global eth1
     24    inet6 fe80::d63d:7eff:fe01:b43e/64 scope link \n\
     25       valid_lft forever preferred_lft forever
     263: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000
     27    link/ether 90:f6:52:27:15:0a brd ff:ff:ff:ff:ff:ff
     28    inet 192.168.0.2/24 brd 192.168.0.255 scope global wlan0
     29    inet6 fe80::92f6:52ff:fe27:150a/64 scope link \n\
     30       valid_lft forever preferred_lft forever
     31"""
     32
     33MOCK_IFCONFIG_OUTPUT = """\
     34eth1      Link encap:Ethernet  HWaddr d4:3d:7e:01:b4:3e  \n\
     35          inet addr:192.168.0.6  Bcast:192.168.0.255  Mask:255.255.255.0
     36          inet6 addr: fe80::d63d:7eff:fe01:b43e/64 Scope:Link
     37          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
     38          RX packets:154242234 errors:0 dropped:0 overruns:0 frame:0
     39          TX packets:155461891 errors:0 dropped:0 overruns:0 carrier:0
     40          collisions:0 txqueuelen:1000 \n\
     41          RX bytes:84367213640 (78.5 GiB)  TX bytes:73401695329 (68.3 GiB)
     42          Interrupt:20 Memory:f4f00000-f4f20000 \n\
     43
     44lo        Link encap:Local Loopback  \n\
     45          inet addr:127.0.0.1  Mask:255.0.0.0
     46          inet6 addr: ::1/128 Scope:Host
     47          UP LOOPBACK RUNNING  MTU:16436  Metric:1
     48          RX packets:27449267 errors:0 dropped:0 overruns:0 frame:0
     49          TX packets:27449267 errors:0 dropped:0 overruns:0 carrier:0
     50          collisions:0 txqueuelen:0 \n\
     51          RX bytes:192643017823 (179.4 GiB)  TX bytes:192643017823 (179.4 GiB)
     52
     53wlan0     Link encap:Ethernet  HWaddr 90:f6:52:27:15:0a  \n\
     54          inet addr:192.168.0.2  Bcast:192.168.0.255  Mask:255.255.255.0
     55          inet6 addr: fe80::92f6:52ff:fe27:150a/64 Scope:Link
     56          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
     57          RX packets:12352750 errors:0 dropped:0 overruns:0 frame:0
     58          TX packets:4501451 errors:0 dropped:0 overruns:0 carrier:0
     59          collisions:0 txqueuelen:1000 \n\
     60          RX bytes:3916475942 (3.6 GiB)  TX bytes:458353654 (437.1 MiB)
     61"""
     62
     63# This is actually from a VirtualBox VM running XP.
     64MOCK_ROUTE_OUTPUT = """\
     65===========================================================================
     66Interface List
     670x1 ........................... MS TCP Loopback interface
     680x2 ...08 00 27 c3 80 ad ...... AMD PCNET Family PCI Ethernet Adapter - Packet Scheduler Miniport
     69===========================================================================
     70===========================================================================
     71Active Routes:
     72Network Destination        Netmask          Gateway       Interface  Metric
     73          0.0.0.0          0.0.0.0         10.0.2.2       10.0.2.15       20
     74         10.0.2.0    255.255.255.0        10.0.2.15       10.0.2.15       20
     75        10.0.2.15  255.255.255.255        127.0.0.1       127.0.0.1       20
     76   10.255.255.255  255.255.255.255        10.0.2.15       10.0.2.15       20
     77        127.0.0.0        255.0.0.0        127.0.0.1       127.0.0.1       1
     78        224.0.0.0        240.0.0.0        10.0.2.15       10.0.2.15       20
     79  255.255.255.255  255.255.255.255        10.0.2.15       10.0.2.15       1
     80Default Gateway:          10.0.2.2
     81===========================================================================
     82Persistent Routes:
     83  None
     84"""
     85
     86UNIX_TEST_ADDRESSES = set(["127.0.0.1", "192.168.0.6", "192.168.0.2", "192.168.0.10"])
     87WINDOWS_TEST_ADDRESSES = set(["127.0.0.1", "10.0.2.15", "192.168.0.10"])
     88CYGWIN_TEST_ADDRESSES = set(["127.0.0.1", "192.168.0.10"])
     89
     90
     91class FakeProcess:
     92    def __init__(self, output, err):
     93        self.output = output
     94        self.err = err
     95    def communicate(self):
     96        return (self.output, self.err)
     97
    898
    999class ListAddresses(testutil.SignalMixin, unittest.TestCase):
     
    15105        d = iputil.get_local_addresses_async()
    16106        def _check(addresses):
    17             self.failUnless(len(addresses) >= 1) # always have localhost
    18             self.failUnless("127.0.0.1" in addresses, addresses)
    19             self.failIf("0.0.0.0" in addresses, addresses)
     107            self.failUnlessIn("127.0.0.1", addresses)
     108            self.failIfIn("0.0.0.0", addresses)
    20109        d.addCallbacks(_check)
    21110        return d
    22111    # David A.'s OpenSolaris box timed out on this test one time when it was at 2s.
    23112    test_list_async.timeout=4
     113
     114    def _test_list_async_mock(self, command, output, expected):
     115        ns = Namespace()
     116        ns.first = True
     117
     118        def call_Popen(args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None,
     119                       preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None,
     120                       universal_newlines=False, startupinfo=None, creationflags=0):
     121            if ns.first:
     122                ns.first = False
     123                e = OSError("EINTR")
     124                e.errno = errno.EINTR
     125                raise e
     126            elif os.path.basename(args[0]) == command:
     127                return FakeProcess(output, "")
     128            else:
     129                e = OSError("[Errno 2] No such file or directory")
     130                e.errno = errno.ENOENT
     131                raise e
     132        self.patch(subprocess, 'Popen', call_Popen)
     133
     134        def call_get_local_ip_for(target):
     135            if target in ("localhost", "127.0.0.1"):
     136                return "127.0.0.1"
     137            else:
     138                return "192.168.0.10"
     139        self.patch(iputil, 'get_local_ip_for', call_get_local_ip_for)
     140
     141        def call_which(name):
     142            return [name]
     143        self.patch(iputil, 'which', call_which)
     144
     145        d = iputil.get_local_addresses_async()
     146        def _check(addresses):
     147            self.failUnlessEquals(set(addresses), set(expected))
     148        d.addCallbacks(_check)
     149        return d
     150
     151    def test_list_async_mock_ip_addr(self):
     152        self.patch(iputil, 'platform', "linux2")
     153        return self._test_list_async_mock("ip", MOCK_IPADDR_OUTPUT, UNIX_TEST_ADDRESSES)
     154
     155    def test_list_async_mock_ifconfig(self):
     156        self.patch(iputil, 'platform', "linux2")
     157        return self._test_list_async_mock("ifconfig", MOCK_IFCONFIG_OUTPUT, UNIX_TEST_ADDRESSES)
     158
     159    def test_list_async_mock_route(self):
     160        self.patch(iputil, 'platform', "win32")
     161        return self._test_list_async_mock("route.exe", MOCK_ROUTE_OUTPUT, WINDOWS_TEST_ADDRESSES)
     162
     163    def test_list_async_mock_cygwin(self):
     164        self.patch(iputil, 'platform', "cygwin")
     165        return self._test_list_async_mock(None, None, CYGWIN_TEST_ADDRESSES)
  • src/allmydata/util/iputil.py

    rd85a75d rb088380  
    11# from the Python Standard Library
    2 import os, re, socket, sys, subprocess
     2import os, re, socket, subprocess, errno
     3
     4from sys import platform
    35
    46# from Twisted
     
    8284    addresses = []
    8385    local_ip = get_local_ip_for(target)
    84     if local_ip:
     86    if local_ip is not None:
    8587        addresses.append(local_ip)
    8688
    87     if sys.platform == "cygwin":
    88         d = _cygwin_hack_find_addresses(target)
     89    if platform == "cygwin":
     90        d = _cygwin_hack_find_addresses()
    8991    else:
    9092        d = _find_addresses_via_config()
     
    132134        # no route to that host
    133135        localip = None
    134     port.stopListening() # note, this returns a Deferred
     136    d = port.stopListening()
     137    d.addErrback(log.err)
    135138    return localip
    136139
    137 # k: result of sys.platform, v: which kind of IP configuration reader we use
    138 _platform_map = {
    139     "linux-i386": "linux", # redhat
    140     "linux-ppc": "linux",  # redhat
    141     "linux2": "linux",     # debian
    142     "linux3": "linux",     # debian
    143     "win32": "win32",
    144     "irix6-n32": "irix",
    145     "irix6-n64": "irix",
    146     "irix6": "irix",
    147     "openbsd2": "bsd",
    148     "openbsd3": "bsd",
    149     "openbsd4": "bsd",
    150     "openbsd5": "bsd",
    151     "darwin": "bsd",       # Mac OS X
    152     "freebsd4": "bsd",
    153     "freebsd5": "bsd",
    154     "freebsd6": "bsd",
    155     "freebsd7": "bsd",
    156     "freebsd8": "bsd",
    157     "freebsd9": "bsd",
    158     "netbsd1": "bsd",
    159     "netbsd2": "bsd",
    160     "netbsd3": "bsd",
    161     "netbsd4": "bsd",
    162     "netbsd5": "bsd",
    163     "netbsd6": "bsd",
    164     "sunos5": "sunos",
    165     "cygwin": "cygwin",
    166     }
    167 
    168 class UnsupportedPlatformError(Exception):
    169     pass
    170140
    171141# Wow, I'm really amazed at home much mileage we've gotten out of calling
     
    173143# versions so far.  Still, the real system calls would much be preferred...
    174144# ... thus wrote Greg Smith in time immemorial...
    175 _win32_path = 'route.exe'
    176 _win32_args = ('print',)
    177 _win32_re = re.compile('^\s*\d+\.\d+\.\d+\.\d+\s.+\s(?P<address>\d+\.\d+\.\d+\.\d+)\s+(?P<metric>\d+)\s*$', flags=re.M|re.I|re.S)
    178 
    179 # These work in Redhat 6.x and Debian 2.2 potato
    180 _linux_path = '/sbin/ifconfig'
    181 _linux_re = re.compile('^\s*inet [a-zA-Z]*:?(?P<address>\d+\.\d+\.\d+\.\d+)\s.+$', flags=re.M|re.I|re.S)
    182 
    183 # NetBSD 1.4 (submitted by Rhialto), Darwin, Mac OS X
    184 _netbsd_path = '/sbin/ifconfig'
    185 _netbsd_args = ('-a',)
    186 _netbsd_re = re.compile('^\s+inet [a-zA-Z]*:?(?P<address>\d+\.\d+\.\d+\.\d+)\s.+$', flags=re.M|re.I|re.S)
    187 
    188 # Irix 6.5
    189 _irix_path = '/usr/etc/ifconfig'
    190 
    191 # Solaris 2.x
    192 _sunos_path = '/usr/sbin/ifconfig'
    193 
    194 
    195 # k: platform string as provided in the value of _platform_map
    196 # v: tuple of (path_to_tool, args, regex,)
    197 _tool_map = {
    198     "linux": (_linux_path, (), _linux_re,),
    199     "win32": (_win32_path, _win32_args, _win32_re,),
    200     "cygwin": (_win32_path, _win32_args, _win32_re,),
    201     "bsd": (_netbsd_path, _netbsd_args, _netbsd_re,),
    202     "irix": (_irix_path, _netbsd_args, _netbsd_re,),
    203     "sunos": (_sunos_path, _netbsd_args, _netbsd_re,),
    204     }
     145_win32_re = re.compile(r'^\s*\d+\.\d+\.\d+\.\d+\s.+\s(?P<address>\d+\.\d+\.\d+\.\d+)\s+(?P<metric>\d+)\s*$', flags=re.M|re.I|re.S)
     146_win32_commands = (('route.exe', ('print',), _win32_re),)
     147
     148# These work in most Unices.
     149_addr_re = re.compile(r'^\s*inet [a-zA-Z]*:?(?P<address>\d+\.\d+\.\d+\.\d+)[\s/].+$', flags=re.M|re.I|re.S)
     150_unix_commands = (('/bin/ip', ('addr',), _addr_re),
     151                  ('/sbin/ifconfig', ('-a',), _addr_re),
     152                  ('/usr/sbin/ifconfig', ('-a',), _addr_re),
     153                  ('/usr/etc/ifconfig', ('-a',), _addr_re),
     154                  ('ifconfig', ('-a',), _addr_re),
     155                  ('/sbin/ifconfig', (), _addr_re),
     156                 )
     157
    205158
    206159def _find_addresses_via_config():
     
    208161
    209162def _synchronously_find_addresses_via_config():
    210     # originally by Greg Smith, hacked by Zooko to conform to Brian's API
    211 
    212     platform = _platform_map.get(sys.platform)
    213     if not platform:
    214         raise UnsupportedPlatformError(sys.platform)
    215 
    216     (pathtotool, args, regex,) = _tool_map[platform]
    217 
    218     # If pathtotool is a fully qualified path then we just try that.
    219     # If it is merely an executable name then we use Twisted's
    220     # "which()" utility and try each executable in turn until one
    221     # gives us something that resembles a dotted-quad IPv4 address.
    222 
    223     if os.path.isabs(pathtotool):
    224         return _query(pathtotool, args, regex)
     163    # originally by Greg Smith, hacked by Zooko and then Daira
     164
     165    # We don't reach here for cygwin.
     166    if platform == 'win32':
     167        commands = _win32_commands
    225168    else:
    226         exes_to_try = which(pathtotool)
     169        commands = _unix_commands
     170
     171    for (pathtotool, args, regex) in commands:
     172        # If pathtotool is a fully qualified path then we just try that.
     173        # If it is merely an executable name then we use Twisted's
     174        # "which()" utility and try each executable in turn until one
     175        # gives us something that resembles a dotted-quad IPv4 address.
     176
     177        if os.path.isabs(pathtotool):
     178            exes_to_try = [pathtotool]
     179        else:
     180            exes_to_try = which(pathtotool)
     181
    227182        for exe in exes_to_try:
    228183            try:
     
    232187            if addresses:
    233188                return addresses
    234         return []
     189
     190    return []
    235191
    236192def _query(path, args, regex):
    237193    env = {'LANG': 'en_US.UTF-8'}
    238     p = subprocess.Popen([path] + list(args), stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
    239     (output, err) = p.communicate()
     194    TRIES = 5
     195    for trial in xrange(TRIES):
     196        try:
     197            p = subprocess.Popen([path] + list(args), stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
     198            (output, err) = p.communicate()
     199            break
     200        except OSError, e:
     201            if e.errno == errno.EINTR and trial < TRIES-1:
     202                continue
     203            raise
    240204
    241205    addresses = []
     
    244208        m = regex.match(outline)
    245209        if m:
    246             addr = m.groupdict()['address']
     210            addr = m.group('address')
    247211            if addr not in addresses:
    248212                addresses.append(addr)
     
    250214    return addresses
    251215
    252 def _cygwin_hack_find_addresses(target):
     216def _cygwin_hack_find_addresses():
    253217    addresses = []
    254     for h in [target, "localhost", "127.0.0.1",]:
    255         try:
    256             addr = get_local_ip_for(h)
    257             if addr not in addresses:
    258                 addresses.append(addr)
    259         except socket.gaierror:
    260             pass
     218    for h in ["localhost", "127.0.0.1",]:
     219        addr = get_local_ip_for(h)
     220        if addr is not None and addr not in addresses:
     221            addresses.append(addr)
    261222
    262223    return defer.succeed(addresses)
Note: See TracChangeset for help on using the changeset viewer.