Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(60)

Side by Side Diff: tools/telemetry/third_party/webpagereplay/platformsettings.py

Issue 1647513002: Delete tools/telemetry. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright 2010 Google Inc. All Rights Reserved.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16 """Provides cross-platform utility functions.
17
18 Example:
19 import platformsettings
20 ip = platformsettings.get_server_ip_address()
21
22 Functions with "_temporary_" in their name automatically clean-up upon
23 termination (via the atexit module).
24
25 For the full list of functions, see the bottom of the file.
26 """
27
28 import atexit
29 import distutils.spawn
30 import distutils.version
31 import fileinput
32 import logging
33 import os
34 import platform
35 import re
36 import socket
37 import stat
38 import subprocess
39 import sys
40 import time
41 import urlparse
42
43
44 class PlatformSettingsError(Exception):
45 """Module catch-all error."""
46 pass
47
48
49 class DnsReadError(PlatformSettingsError):
50 """Raised when unable to read DNS settings."""
51 pass
52
53
54 class DnsUpdateError(PlatformSettingsError):
55 """Raised when unable to update DNS settings."""
56 pass
57
58
59 class NotAdministratorError(PlatformSettingsError):
60 """Raised when not running as administrator."""
61 pass
62
63
64 class CalledProcessError(PlatformSettingsError):
65 """Raised when a _check_output() process returns a non-zero exit status."""
66 def __init__(self, returncode, cmd):
67 super(CalledProcessError, self).__init__()
68 self.returncode = returncode
69 self.cmd = cmd
70
71 def __str__(self):
72 return 'Command "%s" returned non-zero exit status %d' % (
73 ' '.join(self.cmd), self.returncode)
74
75
76 def FindExecutable(executable):
77 """Finds the given executable in PATH.
78
79 Since WPR may be invoked as sudo, meaning PATH is empty, we also hardcode a
80 few common paths.
81
82 Returns:
83 The fully qualified path with .exe appended if appropriate or None if it
84 doesn't exist.
85 """
86 return distutils.spawn.find_executable(executable,
87 os.pathsep.join([os.environ['PATH'],
88 '/sbin',
89 '/usr/bin',
90 '/usr/sbin/',
91 '/usr/local/sbin',
92 ]))
93
94 def HasSniSupport():
95 try:
96 import OpenSSL
97 return (distutils.version.StrictVersion(OpenSSL.__version__) >=
98 distutils.version.StrictVersion('0.13'))
99 except ImportError:
100 return False
101
102
103 def SupportsFdLimitControl():
104 """Whether the platform supports changing the process fd limit."""
105 return os.name is 'posix'
106
107
108 def GetFdLimit():
109 """Returns a tuple of (soft_limit, hard_limit)."""
110 import resource
111 return resource.getrlimit(resource.RLIMIT_NOFILE)
112
113
114 def AdjustFdLimit(new_soft_limit, new_hard_limit):
115 """Sets a new soft and hard limit for max number of fds."""
116 import resource
117 resource.setrlimit(resource.RLIMIT_NOFILE, (new_soft_limit, new_hard_limit))
118
119
120 class SystemProxy(object):
121 """A host/port pair for a HTTP or HTTPS proxy configuration."""
122
123 def __init__(self, host, port):
124 """Initialize a SystemProxy instance.
125
126 Args:
127 host: a host name or IP address string (e.g. "example.com" or "1.1.1.1").
128 port: a port string or integer (e.g. "8888" or 8888).
129 """
130 self.host = host
131 self.port = int(port) if port else None
132
133 def __nonzero__(self):
134 """True if the host is set."""
135 return bool(self.host)
136
137 @classmethod
138 def from_url(cls, proxy_url):
139 """Create a SystemProxy instance.
140
141 If proxy_url is None, an empty string, or an invalid URL, the
142 SystemProxy instance with have None and None for the host and port
143 (no exception is raised).
144
145 Args:
146 proxy_url: a proxy url string such as "http://proxy.com:8888/".
147 Returns:
148 a System proxy instance.
149 """
150 if proxy_url:
151 parse_result = urlparse.urlparse(proxy_url)
152 return cls(parse_result.hostname, parse_result.port)
153 return cls(None, None)
154
155
156 class _BasePlatformSettings(object):
157
158 def get_system_logging_handler(self):
159 """Return a handler for the logging module (optional)."""
160 return None
161
162 def rerun_as_administrator(self):
163 """If needed, rerun the program with administrative privileges.
164
165 Raises NotAdministratorError if unable to rerun.
166 """
167 pass
168
169 def timer(self):
170 """Return the current time in seconds as a floating point number."""
171 return time.time()
172
173 def get_server_ip_address(self, is_server_mode=False):
174 """Returns the IP address to use for dnsproxy and ipfw."""
175 if is_server_mode:
176 return socket.gethostbyname(socket.gethostname())
177 return '127.0.0.1'
178
179 def get_httpproxy_ip_address(self, is_server_mode=False):
180 """Returns the IP address to use for httpproxy."""
181 if is_server_mode:
182 return '0.0.0.0'
183 return '127.0.0.1'
184
185 def get_system_proxy(self, use_ssl):
186 """Returns the system HTTP(S) proxy host, port."""
187 del use_ssl
188 return SystemProxy(None, None)
189
190 def _ipfw_cmd(self):
191 raise NotImplementedError
192
193 def ipfw(self, *args):
194 ipfw_cmd = (self._ipfw_cmd(), ) + args
195 return self._check_output(*ipfw_cmd, elevate_privilege=True)
196
197 def has_ipfw(self):
198 try:
199 self.ipfw('list')
200 return True
201 except AssertionError as e:
202 logging.warning('Failed to start ipfw command. '
203 'Error: %s' % e.message)
204 return False
205
206 def _get_cwnd(self):
207 return None
208
209 def _set_cwnd(self, args):
210 pass
211
212 def _elevate_privilege_for_cmd(self, args):
213 return args
214
215 def _check_output(self, *args, **kwargs):
216 """Run Popen(*args) and return its output as a byte string.
217
218 Python 2.7 has subprocess.check_output. This is essentially the same
219 except that, as a convenience, all the positional args are used as
220 command arguments and the |elevate_privilege| kwarg is supported.
221
222 Args:
223 *args: sequence of program arguments
224 elevate_privilege: Run the command with elevated privileges.
225 Raises:
226 CalledProcessError if the program returns non-zero exit status.
227 Returns:
228 output as a byte string.
229 """
230 command_args = [str(a) for a in args]
231
232 if os.path.sep not in command_args[0]:
233 qualified_command = FindExecutable(command_args[0])
234 assert qualified_command, 'Failed to find %s in path' % command_args[0]
235 command_args[0] = qualified_command
236
237 if kwargs.get('elevate_privilege'):
238 command_args = self._elevate_privilege_for_cmd(command_args)
239
240 logging.debug(' '.join(command_args))
241 process = subprocess.Popen(
242 command_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
243 output = process.communicate()[0]
244 retcode = process.poll()
245 if retcode:
246 raise CalledProcessError(retcode, command_args)
247 return output
248
249 def set_temporary_tcp_init_cwnd(self, cwnd):
250 cwnd = int(cwnd)
251 original_cwnd = self._get_cwnd()
252 if original_cwnd is None:
253 raise PlatformSettingsError('Unable to get current tcp init_cwnd.')
254 if cwnd == original_cwnd:
255 logging.info('TCP init_cwnd already set to target value: %s', cwnd)
256 else:
257 self._set_cwnd(cwnd)
258 if self._get_cwnd() == cwnd:
259 logging.info('Changed cwnd to %s', cwnd)
260 atexit.register(self._set_cwnd, original_cwnd)
261 else:
262 logging.error('Unable to update cwnd to %s', cwnd)
263
264 def setup_temporary_loopback_config(self):
265 """Setup the loopback interface similar to real interface.
266
267 We use loopback for much of our testing, and on some systems, loopback
268 behaves differently from real interfaces.
269 """
270 logging.error('Platform does not support loopback configuration.')
271
272 def _save_primary_interface_properties(self):
273 self._orig_nameserver = self.get_original_primary_nameserver()
274
275 def _restore_primary_interface_properties(self):
276 self._set_primary_nameserver(self._orig_nameserver)
277
278 def _get_primary_nameserver(self):
279 raise NotImplementedError
280
281 def _set_primary_nameserver(self, _):
282 raise NotImplementedError
283
284 def get_original_primary_nameserver(self):
285 if not hasattr(self, '_original_nameserver'):
286 self._original_nameserver = self._get_primary_nameserver()
287 logging.info('Saved original primary DNS nameserver: %s',
288 self._original_nameserver)
289 return self._original_nameserver
290
291 def set_temporary_primary_nameserver(self, nameserver):
292 self._save_primary_interface_properties()
293 self._set_primary_nameserver(nameserver)
294 if self._get_primary_nameserver() == nameserver:
295 logging.info('Changed temporary primary nameserver to %s', nameserver)
296 atexit.register(self._restore_primary_interface_properties)
297 else:
298 raise self._get_dns_update_error()
299
300
301 class _PosixPlatformSettings(_BasePlatformSettings):
302
303 # pylint: disable=abstract-method
304 # Suppress lint check for _get_primary_nameserver & _set_primary_nameserver
305
306 def rerun_as_administrator(self):
307 """If needed, rerun the program with administrative privileges.
308
309 Raises NotAdministratorError if unable to rerun.
310 """
311 if os.geteuid() != 0:
312 logging.warn('Rerunning with sudo: %s', sys.argv)
313 os.execv('/usr/bin/sudo', ['--'] + sys.argv)
314
315 def _elevate_privilege_for_cmd(self, args):
316 def IsSetUID(path):
317 return (os.stat(path).st_mode & stat.S_ISUID) == stat.S_ISUID
318
319 def IsElevated():
320 p = subprocess.Popen(
321 ['sudo', '-nv'], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
322 stderr=subprocess.STDOUT)
323 stdout = p.communicate()[0]
324 # Some versions of sudo set the returncode based on whether sudo requires
325 # a password currently. Other versions return output when password is
326 # required and no output when the user is already authenticated.
327 return not p.returncode and not stdout
328
329 if not IsSetUID(args[0]):
330 args = ['sudo'] + args
331
332 if not IsElevated():
333 print 'WPR needs to run %s under sudo. Please authenticate.' % args[1]
334 subprocess.check_call(['sudo', '-v']) # Synchronously authenticate.
335
336 prompt = ('Would you like to always allow %s to run without sudo '
337 '(via `sudo chmod +s %s`)? (y/N)' % (args[1], args[1]))
338 if raw_input(prompt).lower() == 'y':
339 subprocess.check_call(['sudo', 'chmod', '+s', args[1]])
340 return args
341
342 def get_system_proxy(self, use_ssl):
343 """Returns the system HTTP(S) proxy host, port."""
344 proxy_url = os.environ.get('https_proxy' if use_ssl else 'http_proxy')
345 return SystemProxy.from_url(proxy_url)
346
347 def _ipfw_cmd(self):
348 return 'ipfw'
349
350 def _get_dns_update_error(self):
351 return DnsUpdateError('Did you run under sudo?')
352
353 def _sysctl(self, *args, **kwargs):
354 sysctl_args = [FindExecutable('sysctl')]
355 if kwargs.get('use_sudo'):
356 sysctl_args = self._elevate_privilege_for_cmd(sysctl_args)
357 sysctl_args.extend(str(a) for a in args)
358 sysctl = subprocess.Popen(
359 sysctl_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
360 stdout = sysctl.communicate()[0]
361 return sysctl.returncode, stdout
362
363 def has_sysctl(self, name):
364 if not hasattr(self, 'has_sysctl_cache'):
365 self.has_sysctl_cache = {}
366 if name not in self.has_sysctl_cache:
367 self.has_sysctl_cache[name] = self._sysctl(name)[0] == 0
368 return self.has_sysctl_cache[name]
369
370 def set_sysctl(self, name, value):
371 rv = self._sysctl('%s=%s' % (name, value), use_sudo=True)[0]
372 if rv != 0:
373 logging.error('Unable to set sysctl %s: %s', name, rv)
374
375 def get_sysctl(self, name):
376 rv, value = self._sysctl('-n', name)
377 if rv == 0:
378 return value
379 else:
380 logging.error('Unable to get sysctl %s: %s', name, rv)
381 return None
382
383
384 class _OsxPlatformSettings(_PosixPlatformSettings):
385 LOCAL_SLOWSTART_MIB_NAME = 'net.inet.tcp.local_slowstart_flightsize'
386
387 def _scutil(self, cmd):
388 scutil = subprocess.Popen([FindExecutable('scutil')],
389 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
390 return scutil.communicate(cmd)[0]
391
392 def _ifconfig(self, *args):
393 return self._check_output('ifconfig', *args, elevate_privilege=True)
394
395 def set_sysctl(self, name, value):
396 rv = self._sysctl('-w', '%s=%s' % (name, value), use_sudo=True)[0]
397 if rv != 0:
398 logging.error('Unable to set sysctl %s: %s', name, rv)
399
400 def _get_cwnd(self):
401 return int(self.get_sysctl(self.LOCAL_SLOWSTART_MIB_NAME))
402
403 def _set_cwnd(self, size):
404 self.set_sysctl(self.LOCAL_SLOWSTART_MIB_NAME, size)
405
406 def _get_loopback_mtu(self):
407 config = self._ifconfig('lo0')
408 match = re.search(r'\smtu\s+(\d+)', config)
409 return int(match.group(1)) if match else None
410
411 def setup_temporary_loopback_config(self):
412 """Configure loopback to temporarily use reasonably sized frames.
413
414 OS X uses jumbo frames by default (16KB).
415 """
416 TARGET_LOOPBACK_MTU = 1500
417 original_mtu = self._get_loopback_mtu()
418 if original_mtu is None:
419 logging.error('Unable to read loopback mtu. Setting left unchanged.')
420 return
421 if original_mtu == TARGET_LOOPBACK_MTU:
422 logging.debug('Loopback MTU already has target value: %d', original_mtu)
423 else:
424 self._ifconfig('lo0', 'mtu', TARGET_LOOPBACK_MTU)
425 if self._get_loopback_mtu() == TARGET_LOOPBACK_MTU:
426 logging.debug('Set loopback MTU to %d (was %d)',
427 TARGET_LOOPBACK_MTU, original_mtu)
428 atexit.register(self._ifconfig, 'lo0', 'mtu', original_mtu)
429 else:
430 logging.error('Unable to change loopback MTU from %d to %d',
431 original_mtu, TARGET_LOOPBACK_MTU)
432
433 def _get_dns_service_key(self):
434 output = self._scutil('show State:/Network/Global/IPv4')
435 lines = output.split('\n')
436 for line in lines:
437 key_value = line.split(' : ')
438 if key_value[0] == ' PrimaryService':
439 return 'State:/Network/Service/%s/DNS' % key_value[1]
440 raise DnsReadError('Unable to find DNS service key: %s', output)
441
442 def _get_primary_nameserver(self):
443 output = self._scutil('show %s' % self._get_dns_service_key())
444 match = re.search(
445 br'ServerAddresses\s+:\s+<array>\s+{\s+0\s+:\s+((\d{1,3}\.){3}\d{1,3})',
446 output)
447 if match:
448 return match.group(1)
449 else:
450 raise DnsReadError('Unable to find primary DNS server: %s', output)
451
452 def _set_primary_nameserver(self, dns):
453 command = '\n'.join([
454 'd.init',
455 'd.add ServerAddresses * %s' % dns,
456 'set %s' % self._get_dns_service_key()
457 ])
458 self._scutil(command)
459
460
461 class _FreeBSDPlatformSettings(_PosixPlatformSettings):
462 """Partial implementation for FreeBSD. Does not allow a DNS server to be
463 launched nor ipfw to be used.
464 """
465 RESOLV_CONF = '/etc/resolv.conf'
466
467 def _get_default_route_line(self):
468 raise NotImplementedError
469
470 def _set_cwnd(self, cwnd):
471 raise NotImplementedError
472
473 def _get_cwnd(self):
474 raise NotImplementedError
475
476 def setup_temporary_loopback_config(self):
477 raise NotImplementedError
478
479 def _write_resolve_conf(self, dns):
480 raise NotImplementedError
481
482 def _get_primary_nameserver(self):
483 try:
484 resolv_file = open(self.RESOLV_CONF)
485 except IOError:
486 raise DnsReadError()
487 for line in resolv_file:
488 if line.startswith('nameserver '):
489 return line.split()[1]
490 raise DnsReadError()
491
492 def _set_primary_nameserver(self, dns):
493 raise NotImplementedError
494
495
496 class _LinuxPlatformSettings(_PosixPlatformSettings):
497 """The following thread recommends a way to update DNS on Linux:
498
499 http://ubuntuforums.org/showthread.php?t=337553
500
501 sudo cp /etc/dhcp3/dhclient.conf /etc/dhcp3/dhclient.conf.bak
502 sudo gedit /etc/dhcp3/dhclient.conf
503 #prepend domain-name-servers 127.0.0.1;
504 prepend domain-name-servers 208.67.222.222, 208.67.220.220;
505
506 prepend domain-name-servers 208.67.222.222, 208.67.220.220;
507 request subnet-mask, broadcast-address, time-offset, routers,
508 domain-name, domain-name-servers, host-name,
509 netbios-name-servers, netbios-scope;
510 #require subnet-mask, domain-name-servers;
511
512 sudo /etc/init.d/networking restart
513
514 The code below does not try to change dchp and does not restart networking.
515 Update this as needed to make it more robust on more systems.
516 """
517 RESOLV_CONF = '/etc/resolv.conf'
518 ROUTE_RE = re.compile('initcwnd (\d+)')
519 TCP_BASE_MSS = 'net.ipv4.tcp_base_mss'
520 TCP_MTU_PROBING = 'net.ipv4.tcp_mtu_probing'
521
522 def _get_default_route_line(self):
523 stdout = self._check_output('ip', 'route')
524 for line in stdout.split('\n'):
525 if line.startswith('default'):
526 return line
527 return None
528
529 def _set_cwnd(self, cwnd):
530 default_line = self._get_default_route_line()
531 self._check_output(
532 'ip', 'route', 'change', default_line, 'initcwnd', str(cwnd))
533
534 def _get_cwnd(self):
535 default_line = self._get_default_route_line()
536 m = self.ROUTE_RE.search(default_line)
537 if m:
538 return int(m.group(1))
539 # If 'initcwnd' wasn't found, then 0 means it's the system default.
540 return 0
541
542 def setup_temporary_loopback_config(self):
543 """Setup Linux to temporarily use reasonably sized frames.
544
545 Linux uses jumbo frames by default (16KB), using the combination
546 of MTU probing and a base MSS makes it use normal sized packets.
547
548 The reason this works is because tcp_base_mss is only used when MTU
549 probing is enabled. And since we're using the max value, it will
550 always use the reasonable size. This is relevant for server-side realism.
551 The client-side will vary depending on the client TCP stack config.
552 """
553 ENABLE_MTU_PROBING = 2
554 original_probing = self.get_sysctl(self.TCP_MTU_PROBING)
555 self.set_sysctl(self.TCP_MTU_PROBING, ENABLE_MTU_PROBING)
556 atexit.register(self.set_sysctl, self.TCP_MTU_PROBING, original_probing)
557
558 TCP_FULL_MSS = 1460
559 original_mss = self.get_sysctl(self.TCP_BASE_MSS)
560 self.set_sysctl(self.TCP_BASE_MSS, TCP_FULL_MSS)
561 atexit.register(self.set_sysctl, self.TCP_BASE_MSS, original_mss)
562
563 def _write_resolve_conf(self, dns):
564 is_first_nameserver_replaced = False
565 # The fileinput module uses sys.stdout as the edited file output.
566 for line in fileinput.input(self.RESOLV_CONF, inplace=1, backup='.bak'):
567 if line.startswith('nameserver ') and not is_first_nameserver_replaced:
568 print 'nameserver %s' % dns
569 is_first_nameserver_replaced = True
570 else:
571 print line,
572 if not is_first_nameserver_replaced:
573 raise DnsUpdateError('Could not find a suitable nameserver entry in %s' %
574 self.RESOLV_CONF)
575
576 def _get_primary_nameserver(self):
577 try:
578 resolv_file = open(self.RESOLV_CONF)
579 except IOError:
580 raise DnsReadError()
581 for line in resolv_file:
582 if line.startswith('nameserver '):
583 return line.split()[1]
584 raise DnsReadError()
585
586 def _set_primary_nameserver(self, dns):
587 """Replace the first nameserver entry with the one given."""
588 try:
589 self._write_resolve_conf(dns)
590 except OSError, e:
591 if 'Permission denied' in e:
592 raise self._get_dns_update_error()
593 raise
594
595
596 class _WindowsPlatformSettings(_BasePlatformSettings):
597
598 # pylint: disable=abstract-method
599 # Suppress lint check for _ipfw_cmd
600
601 def get_system_logging_handler(self):
602 """Return a handler for the logging module (optional).
603
604 For Windows, output can be viewed with DebugView.
605 http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx
606 """
607 import ctypes
608 output_debug_string = ctypes.windll.kernel32.OutputDebugStringA
609 output_debug_string.argtypes = [ctypes.c_char_p]
610 class DebugViewHandler(logging.Handler):
611 def emit(self, record):
612 output_debug_string('[wpr] ' + self.format(record))
613 return DebugViewHandler()
614
615 def rerun_as_administrator(self):
616 """If needed, rerun the program with administrative privileges.
617
618 Raises NotAdministratorError if unable to rerun.
619 """
620 import ctypes
621 if not ctypes.windll.shell32.IsUserAnAdmin():
622 raise NotAdministratorError('Rerun with administrator privileges.')
623 #os.execv('runas', sys.argv) # TODO: replace needed Windows magic
624
625 def timer(self):
626 """Return the current time in seconds as a floating point number.
627
628 From time module documentation:
629 On Windows, this function [time.clock()] returns wall-clock
630 seconds elapsed since the first call to this function, as a
631 floating point number, based on the Win32 function
632 QueryPerformanceCounter(). The resolution is typically better
633 than one microsecond.
634 """
635 return time.clock()
636
637 def _arp(self, *args):
638 return self._check_output('arp', *args)
639
640 def _route(self, *args):
641 return self._check_output('route', *args)
642
643 def _ipconfig(self, *args):
644 return self._check_output('ipconfig', *args)
645
646 def _get_mac_address(self, ip):
647 """Return the MAC address for the given ip."""
648 ip_re = re.compile(r'^\s*IP(?:v4)? Address[ .]+:\s+([0-9.]+)')
649 for line in self._ipconfig('/all').splitlines():
650 if line[:1].isalnum():
651 current_ip = None
652 current_mac = None
653 elif ':' in line:
654 line = line.strip()
655 ip_match = ip_re.match(line)
656 if ip_match:
657 current_ip = ip_match.group(1)
658 elif line.startswith('Physical Address'):
659 current_mac = line.split(':', 1)[1].lstrip()
660 if current_ip == ip and current_mac:
661 return current_mac
662 return None
663
664 def setup_temporary_loopback_config(self):
665 """On Windows, temporarily route the server ip to itself."""
666 ip = self.get_server_ip_address()
667 mac_address = self._get_mac_address(ip)
668 if self.mac_address:
669 self._arp('-s', ip, self.mac_address)
670 self._route('add', ip, ip, 'mask', '255.255.255.255')
671 atexit.register(self._arp, '-d', ip)
672 atexit.register(self._route, 'delete', ip, ip, 'mask', '255.255.255.255')
673 else:
674 logging.warn('Unable to configure loopback: MAC address not found.')
675 # TODO(slamm): Configure cwnd, MTU size
676
677 def _get_dns_update_error(self):
678 return DnsUpdateError('Did you run as administrator?')
679
680 def _netsh_show_dns(self):
681 """Return DNS information:
682
683 Example output:
684 Configuration for interface "Local Area Connection 3"
685 DNS servers configured through DHCP: None
686 Register with which suffix: Primary only
687
688 Configuration for interface "Wireless Network Connection 2"
689 DNS servers configured through DHCP: 192.168.1.1
690 Register with which suffix: Primary only
691 """
692 return self._check_output('netsh', 'interface', 'ip', 'show', 'dns')
693
694 def _netsh_set_dns(self, iface_name, addr):
695 """Modify DNS information on the primary interface."""
696 output = self._check_output('netsh', 'interface', 'ip', 'set', 'dns',
697 iface_name, 'static', addr)
698
699 def _netsh_set_dns_dhcp(self, iface_name):
700 """Modify DNS information on the primary interface."""
701 output = self._check_output('netsh', 'interface', 'ip', 'set', 'dns',
702 iface_name, 'dhcp')
703
704 def _get_interfaces_with_dns(self):
705 output = self._netsh_show_dns()
706 lines = output.split('\n')
707 iface_re = re.compile(r'^Configuration for interface \"(?P<name>.*)\"')
708 dns_re = re.compile(r'(?P<kind>.*):\s+(?P<dns>\d+\.\d+\.\d+\.\d+)')
709 iface_name = None
710 iface_dns = None
711 iface_kind = None
712 ifaces = []
713 for line in lines:
714 iface_match = iface_re.match(line)
715 if iface_match:
716 iface_name = iface_match.group('name')
717 dns_match = dns_re.match(line)
718 if dns_match:
719 iface_dns = dns_match.group('dns')
720 iface_dns_config = dns_match.group('kind').strip()
721 if iface_dns_config == "Statically Configured DNS Servers":
722 iface_kind = "static"
723 elif iface_dns_config == "DNS servers configured through DHCP":
724 iface_kind = "dhcp"
725 if iface_name and iface_dns and iface_kind:
726 ifaces.append((iface_dns, iface_name, iface_kind))
727 iface_name = None
728 iface_dns = None
729 return ifaces
730
731 def _save_primary_interface_properties(self):
732 # TODO(etienneb): On windows, an interface can have multiple DNS server
733 # configured. We should save/restore all of them.
734 ifaces = self._get_interfaces_with_dns()
735 self._primary_interfaces = ifaces
736
737 def _restore_primary_interface_properties(self):
738 for iface in self._primary_interfaces:
739 (iface_dns, iface_name, iface_kind) = iface
740 self._netsh_set_dns(iface_name, iface_dns)
741 if iface_kind == "dhcp":
742 self._netsh_set_dns_dhcp(iface_name)
743
744 def _get_primary_nameserver(self):
745 ifaces = self._get_interfaces_with_dns()
746 if not len(ifaces):
747 raise DnsUpdateError("Interface with valid DNS configured not found.")
748 (iface_dns, iface_name, iface_kind) = ifaces[0]
749 return iface_dns
750
751 def _set_primary_nameserver(self, dns):
752 for iface in self._primary_interfaces:
753 (iface_dns, iface_name, iface_kind) = iface
754 self._netsh_set_dns(iface_name, dns)
755
756
757 class _WindowsXpPlatformSettings(_WindowsPlatformSettings):
758 def _ipfw_cmd(self):
759 return (r'third_party\ipfw_win32\ipfw.exe',)
760
761
762 def _new_platform_settings(system, release):
763 """Make a new instance of PlatformSettings for the current system."""
764 if system == 'Darwin':
765 return _OsxPlatformSettings()
766 if system == 'Linux':
767 return _LinuxPlatformSettings()
768 if system == 'Windows' and release == 'XP':
769 return _WindowsXpPlatformSettings()
770 if system == 'Windows':
771 return _WindowsPlatformSettings()
772 if system == 'FreeBSD':
773 return _FreeBSDPlatformSettings()
774 raise NotImplementedError('Sorry %s %s is not supported.' % (system, release))
775
776
777 # Create one instance of the platform-specific settings and
778 # make the functions available at the module-level.
779 _inst = _new_platform_settings(platform.system(), platform.release())
780
781 get_system_logging_handler = _inst.get_system_logging_handler
782 rerun_as_administrator = _inst.rerun_as_administrator
783 timer = _inst.timer
784
785 get_server_ip_address = _inst.get_server_ip_address
786 get_httpproxy_ip_address = _inst.get_httpproxy_ip_address
787 get_system_proxy = _inst.get_system_proxy
788 ipfw = _inst.ipfw
789 has_ipfw = _inst.has_ipfw
790 set_temporary_tcp_init_cwnd = _inst.set_temporary_tcp_init_cwnd
791 setup_temporary_loopback_config = _inst.setup_temporary_loopback_config
792
793 get_original_primary_nameserver = _inst.get_original_primary_nameserver
794 set_temporary_primary_nameserver = _inst.set_temporary_primary_nameserver
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698