1 # from the Python Standard Library
2 import os, re, socket, sys, subprocess
5 from twisted.internet import defer, threads, reactor
6 from twisted.internet.protocol import DatagramProtocol
7 from twisted.python.procutils import which
8 from twisted.python import log
12 def increase_rlimits():
13 # We'd like to raise our soft resource.RLIMIT_NOFILE, since certain
14 # systems (OS-X, probably solaris) start with a relatively low limit
15 # (256), and some unit tests want to open up more sockets than this.
16 # Most linux systems start with both hard and soft limits at 1024,
19 # unfortunately the values to pass to setrlimit() vary widely from
20 # one system to another. OS-X reports (256, HUGE), but the real hard
21 # limit is 10240, and accepts (-1,-1) to mean raise it to the
22 # maximum. Cygwin reports (256, -1), then ignores a request of
23 # (-1,-1): instead you have to guess at the hard limit (it appears to
24 # be 3200), so using (3200,-1) seems to work. Linux reports a
25 # sensible (1024,1024), then rejects (-1,-1) as trying to raise the
26 # maximum limit, so you could set it to (1024,1024) but you might as
27 # well leave it alone.
30 current = resource.getrlimit(resource.RLIMIT_NOFILE)
31 except AttributeError:
32 # we're probably missing RLIMIT_NOFILE
35 if current[0] >= 1024:
36 # good enough, leave it alone
40 if current[1] > 0 and current[1] < 1000000:
41 # solaris reports (256, 65536)
42 resource.setrlimit(resource.RLIMIT_NOFILE,
43 (current[1], current[1]))
45 # this one works on OS-X (bsd), and gives us 10240, but
46 # it doesn't work on linux (on which both the hard and
47 # soft limits are set to 1024 by default).
48 resource.setrlimit(resource.RLIMIT_NOFILE, (-1,-1))
49 new = resource.getrlimit(resource.RLIMIT_NOFILE)
50 if new[0] == current[0]:
51 # probably cygwin, which ignores -1. Use a real value.
52 resource.setrlimit(resource.RLIMIT_NOFILE, (3200,-1))
55 log.msg("unable to set RLIMIT_NOFILE: current value %s"
56 % (resource.getrlimit(resource.RLIMIT_NOFILE),))
58 # who knows what. It isn't very important, so log it and continue
61 def _increase_rlimits():
62 # TODO: implement this for Windows. Although I suspect the
63 # solution might be "be running under the iocp reactor and
64 # make this function be a no-op".
66 # pyflakes complains about two 'def FOO' statements in the same time,
67 # since one might be shadowing the other. This hack appeases pyflakes.
68 increase_rlimits = _increase_rlimits
71 def get_local_addresses_async(target="198.41.0.4"): # A.ROOT-SERVERS.NET
73 Return a Deferred that fires with a list of IPv4 addresses (as dotted-quad
74 strings) that are currently configured on this host, sorted in descending
75 order of how likely we think they are to work.
77 @param target: we want to learn an IP address they could try using to
78 connect to us; The default value is fine, but it might help if you
79 pass the address of a host that you are actually trying to be
83 local_ip = get_local_ip_for(target)
85 addresses.append(local_ip)
87 if sys.platform == "cygwin":
88 d = _cygwin_hack_find_addresses(target)
90 d = _find_addresses_via_config()
94 if addr != "0.0.0.0" and not addr in addresses:
95 addresses.append(addr)
97 d.addCallback(_collect)
101 def get_local_ip_for(target):
102 """Find out what our IP address is for use by a given target.
104 @return: the IP address as a dotted-quad string which could be used by
105 to connect to us. It might work for them, it might not. If
106 there is no suitable address (perhaps we don't currently have an
107 externally-visible interface), this will return None.
111 target_ipaddr = socket.gethostbyname(target)
112 except socket.gaierror:
113 # DNS isn't running, or somehow we encountered an error
115 # note: if an interface is configured and up, but nothing is
116 # connected to it, gethostbyname("A.ROOT-SERVERS.NET") will take 20
117 # seconds to raise socket.gaierror . This is synchronous and occurs
118 # for each node being started, so users of
119 # test.common.SystemTestMixin (like test_system) will see something
120 # like 120s of delay, which may be enough to hit the default trial
121 # timeouts. For that reason, get_local_addresses_async() was changed
122 # to default to the numerical ip address for A.ROOT-SERVERS.NET, to
123 # avoid this DNS lookup. This also makes node startup fractionally
126 udpprot = DatagramProtocol()
127 port = reactor.listenUDP(0, udpprot)
129 udpprot.transport.connect(target_ipaddr, 7)
130 localip = udpprot.transport.getHost().host
132 # no route to that host
134 port.stopListening() # note, this returns a Deferred
137 # k: result of sys.platform, v: which kind of IP configuration reader we use
139 "linux-i386": "linux", # redhat
140 "linux-ppc": "linux", # redhat
141 "linux2": "linux", # debian
149 "darwin": "bsd", # Mac OS X
166 class UnsupportedPlatformError(Exception):
169 # Wow, I'm really amazed at home much mileage we've gotten out of calling
170 # the external route.exe program on windows... It appears to work on all
171 # versions so far. Still, the real system calls would much be preferred...
172 # ... thus wrote Greg Smith in time immemorial...
173 _win32_path = 'route.exe'
174 _win32_args = ('print',)
175 _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)
177 # These work in Redhat 6.x and Debian 2.2 potato
178 _linux_path = '/sbin/ifconfig'
179 _linux_re = re.compile('^\s*inet [a-zA-Z]*:?(?P<address>\d+\.\d+\.\d+\.\d+)\s.+$', flags=re.M|re.I|re.S)
181 # NetBSD 1.4 (submitted by Rhialto), Darwin, Mac OS X
182 _netbsd_path = '/sbin/ifconfig'
183 _netbsd_args = ('-a',)
184 _netbsd_re = re.compile('^\s+inet [a-zA-Z]*:?(?P<address>\d+\.\d+\.\d+\.\d+)\s.+$', flags=re.M|re.I|re.S)
187 _irix_path = '/usr/etc/ifconfig'
190 _sunos_path = '/usr/sbin/ifconfig'
193 # k: platform string as provided in the value of _platform_map
194 # v: tuple of (path_to_tool, args, regex,)
196 "linux": (_linux_path, (), _linux_re,),
197 "win32": (_win32_path, _win32_args, _win32_re,),
198 "cygwin": (_win32_path, _win32_args, _win32_re,),
199 "bsd": (_netbsd_path, _netbsd_args, _netbsd_re,),
200 "irix": (_irix_path, _netbsd_args, _netbsd_re,),
201 "sunos": (_sunos_path, _netbsd_args, _netbsd_re,),
204 def _find_addresses_via_config():
205 return threads.deferToThread(_synchronously_find_addresses_via_config)
207 def _synchronously_find_addresses_via_config():
208 # originally by Greg Smith, hacked by Zooko to conform to Brian's API
210 platform = _platform_map.get(sys.platform)
212 raise UnsupportedPlatformError(sys.platform)
214 (pathtotool, args, regex,) = _tool_map[platform]
216 # If pathtotool is a fully qualified path then we just try that.
217 # If it is merely an executable name then we use Twisted's
218 # "which()" utility and try each executable in turn until one
219 # gives us something that resembles a dotted-quad IPv4 address.
221 if os.path.isabs(pathtotool):
222 return _query(pathtotool, args, regex)
224 exes_to_try = which(pathtotool)
225 for exe in exes_to_try:
227 addresses = _query(exe, args, regex)
234 def _query(path, args, regex):
235 env = {'LANG': 'en_US.UTF-8'}
236 p = subprocess.Popen([path] + list(args), stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
237 (output, err) = p.communicate()
240 outputsplit = output.split('\n')
241 for outline in outputsplit:
242 m = regex.match(outline)
244 addr = m.groupdict()['address']
245 if addr not in addresses:
246 addresses.append(addr)
250 def _cygwin_hack_find_addresses(target):
252 for h in [target, "localhost", "127.0.0.1",]:
254 addr = get_local_ip_for(h)
255 if addr not in addresses:
256 addresses.append(addr)
257 except socket.gaierror:
260 return defer.succeed(addresses)