summaryrefslogtreecommitdiffstats
path: root/testing/mozbase/moznetwork
diff options
context:
space:
mode:
Diffstat (limited to 'testing/mozbase/moznetwork')
-rw-r--r--testing/mozbase/moznetwork/moznetwork/__init__.py26
-rw-r--r--testing/mozbase/moznetwork/moznetwork/moznetwork.py172
-rw-r--r--testing/mozbase/moznetwork/setup.py29
-rw-r--r--testing/mozbase/moznetwork/tests/manifest.ini1
-rw-r--r--testing/mozbase/moznetwork/tests/test.py85
5 files changed, 313 insertions, 0 deletions
diff --git a/testing/mozbase/moznetwork/moznetwork/__init__.py b/testing/mozbase/moznetwork/moznetwork/__init__.py
new file mode 100644
index 000000000..df2097cb0
--- /dev/null
+++ b/testing/mozbase/moznetwork/moznetwork/__init__.py
@@ -0,0 +1,26 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+"""
+moznetwork is a very simple module designed for one task: getting the
+network address of the current machine.
+
+Example usage:
+
+::
+
+ import moznetwork
+
+ try:
+ ip = moznetwork.get_ip()
+ print "The external IP of your machine is '%s'" % ip
+ except moznetwork.NetworkError:
+ print "Unable to determine IP address of machine"
+ raise
+
+"""
+
+from moznetwork import get_ip
+
+__all__ = ['get_ip']
diff --git a/testing/mozbase/moznetwork/moznetwork/moznetwork.py b/testing/mozbase/moznetwork/moznetwork/moznetwork.py
new file mode 100644
index 000000000..537649603
--- /dev/null
+++ b/testing/mozbase/moznetwork/moznetwork/moznetwork.py
@@ -0,0 +1,172 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import argparse
+import array
+import re
+import socket
+import struct
+import subprocess
+import sys
+
+import mozinfo
+import mozlog
+
+if mozinfo.isLinux:
+ import fcntl
+
+
+class NetworkError(Exception):
+ """Exception thrown when unable to obtain interface or IP."""
+
+
+def _get_logger():
+ logger = mozlog.get_default_logger(component='moznetwork')
+ if not logger:
+ logger = mozlog.unstructured.getLogger('moznetwork')
+ return logger
+
+
+def _get_interface_list():
+ """Provides a list of available network interfaces
+ as a list of tuples (name, ip)"""
+ logger = _get_logger()
+ logger.debug('Gathering interface list')
+ max_iface = 32 # Maximum number of interfaces(Aribtrary)
+ bytes = max_iface * 32
+ is_32bit = (8 * struct.calcsize("P")) == 32 # Set Architecture
+ struct_size = 32 if is_32bit else 40
+
+ try:
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ names = array.array('B', '\0' * bytes)
+ outbytes = struct.unpack('iL', fcntl.ioctl(
+ s.fileno(),
+ 0x8912, # SIOCGIFCONF
+ struct.pack('iL', bytes, names.buffer_info()[0])
+ ))[0]
+ namestr = names.tostring()
+ return [(namestr[i:i + 32].split('\0', 1)[0],
+ socket.inet_ntoa(namestr[i + 20:i + 24]))
+ for i in range(0, outbytes, struct_size)]
+
+ except IOError:
+ raise NetworkError('Unable to call ioctl with SIOCGIFCONF')
+
+
+def _proc_matches(args, regex):
+ """Helper returns the matches of regex in the output of a process created with
+ the given arguments"""
+ output = subprocess.Popen(args=args,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT).stdout.read()
+ return re.findall(regex, output)
+
+
+def _parse_ifconfig():
+ """Parse the output of running ifconfig on mac in cases other methods
+ have failed"""
+ logger = _get_logger()
+ logger.debug('Parsing ifconfig')
+
+ # Attempt to determine the default interface in use.
+ default_iface = _proc_matches(['route', '-n', 'get', 'default'],
+ 'interface: (\w+)')
+
+ if default_iface:
+ addr_list = _proc_matches(['ifconfig', default_iface[0]],
+ 'inet (\d+.\d+.\d+.\d+)')
+ if addr_list:
+ logger.debug('Default interface: [%s] %s' % (default_iface[0],
+ addr_list[0]))
+ if not addr_list[0].startswith('127.'):
+ return addr_list[0]
+
+ # Iterate over plausible interfaces if we didn't find a suitable default.
+ for iface in ['en%s' % i for i in range(10)]:
+ addr_list = _proc_matches(['ifconfig', iface],
+ 'inet (\d+.\d+.\d+.\d+)')
+ if addr_list:
+ logger.debug('Interface: [%s] %s' % (iface, addr_list[0]))
+ if not addr_list[0].startswith('127.'):
+ return addr_list[0]
+
+ # Just return any that isn't localhost. If we can't find one, we have
+ # failed.
+ addrs = _proc_matches(['ifconfig'],
+ 'inet (\d+.\d+.\d+.\d+)')
+ try:
+ return [addr for addr in addrs if not addr.startswith('127.')][0]
+ except IndexError:
+ return None
+
+
+def get_ip():
+ """Provides an available network interface address, for example
+ "192.168.1.3".
+
+ A `NetworkError` exception is raised in case of failure."""
+ logger = _get_logger()
+ try:
+ hostname = socket.gethostname()
+ try:
+ logger.debug('Retrieving IP for %s' % hostname)
+ ips = socket.gethostbyname_ex(hostname)[2]
+ except socket.gaierror: # for Mac OS X
+ hostname += '.local'
+ logger.debug('Retrieving IP for %s' % hostname)
+ ips = socket.gethostbyname_ex(hostname)[2]
+ if len(ips) == 1:
+ ip = ips[0]
+ elif len(ips) > 1:
+ logger.debug('Multiple addresses found: %s' % ips)
+ # no fallback on Windows so take the first address
+ ip = ips[0] if mozinfo.isWin else None
+ else:
+ ip = None
+ except socket.gaierror:
+ # sometimes the hostname doesn't resolve to an ip address, in which
+ # case this will always fail
+ ip = None
+
+ if ip is None or ip.startswith("127."):
+ if mozinfo.isLinux:
+ interfaces = _get_interface_list()
+ for ifconfig in interfaces:
+ logger.debug('Interface: [%s] %s' % (ifconfig[0], ifconfig[1]))
+ if ifconfig[0] == 'lo':
+ continue
+ else:
+ return ifconfig[1]
+ elif mozinfo.isMac:
+ ip = _parse_ifconfig()
+
+ if ip is None:
+ raise NetworkError('Unable to obtain network address')
+
+ return ip
+
+
+def get_lan_ip():
+ """Deprecated. Please use get_ip() instead."""
+ return get_ip()
+
+
+def cli(args=sys.argv[1:]):
+ parser = argparse.ArgumentParser(
+ description='Retrieve IP address')
+ mozlog.commandline.add_logging_group(
+ parser,
+ include_formatters=mozlog.commandline.TEXT_FORMATTERS
+ )
+
+ args = parser.parse_args()
+ mozlog.commandline.setup_logging(
+ 'mozversion', args, {'mach': sys.stdout})
+
+ _get_logger().info('IP address: %s' % get_ip())
+
+
+if __name__ == '__main__':
+ cli()
diff --git a/testing/mozbase/moznetwork/setup.py b/testing/mozbase/moznetwork/setup.py
new file mode 100644
index 000000000..2bc62f8dc
--- /dev/null
+++ b/testing/mozbase/moznetwork/setup.py
@@ -0,0 +1,29 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from setuptools import setup
+
+PACKAGE_VERSION = '0.27'
+
+deps = ['mozinfo',
+ 'mozlog >= 3.0',
+ ]
+
+setup(name='moznetwork',
+ version=PACKAGE_VERSION,
+ description="Library of network utilities for use in Mozilla testing",
+ long_description="see http://mozbase.readthedocs.org/",
+ classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
+ keywords='mozilla',
+ author='Mozilla Automation and Tools team',
+ author_email='tools@lists.mozilla.org',
+ url='https://wiki.mozilla.org/Auto-tools/Projects/Mozbase',
+ license='MPL',
+ packages=['moznetwork'],
+ include_package_data=True,
+ zip_safe=False,
+ install_requires=deps,
+ entry_points={'console_scripts': [
+ 'moznetwork = moznetwork:cli']},
+ )
diff --git a/testing/mozbase/moznetwork/tests/manifest.ini b/testing/mozbase/moznetwork/tests/manifest.ini
new file mode 100644
index 000000000..528fdea7b
--- /dev/null
+++ b/testing/mozbase/moznetwork/tests/manifest.ini
@@ -0,0 +1 @@
+[test.py]
diff --git a/testing/mozbase/moznetwork/tests/test.py b/testing/mozbase/moznetwork/tests/test.py
new file mode 100644
index 000000000..79eee6b03
--- /dev/null
+++ b/testing/mozbase/moznetwork/tests/test.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python
+"""
+Unit-Tests for moznetwork
+"""
+
+import os
+import mock
+import mozinfo
+import moznetwork
+import re
+import subprocess
+import unittest
+
+
+def verify_ip_in_list(ip):
+ """
+ Helper Method to check if `ip` is listed in Network Adresses
+ returned by ipconfig/ifconfig, depending on the platform in use
+
+ :param ip: IPv4 address in the xxx.xxx.xxx.xxx format as a string
+ Example Usage:
+ verify_ip_in_list('192.168.0.1')
+
+ returns True if the `ip` is in the list of IPs in ipconfig/ifconfig
+ """
+
+ # Regex to match IPv4 addresses.
+ # 0-255.0-255.0-255.0-255, note order is important here.
+ regexip = re.compile("((25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)\.){3}"
+ "(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)")
+
+ if mozinfo.isLinux or mozinfo.isMac or mozinfo.isBsd:
+ # if "/sbin/ifconfig" exist, use it because it may not be in the
+ # PATH (at least on some linux platforms)
+ if os.path.isfile('/sbin/ifconfig') and os.access('/sbin/ifconfig',
+ os.X_OK):
+ args = ['/sbin/ifconfig']
+ else:
+ args = ["ifconfig"]
+
+ if mozinfo.isWin:
+ args = ["ipconfig"]
+
+ ps = subprocess.Popen(args, stdout=subprocess.PIPE)
+ standardoutput, standarderror = ps.communicate()
+
+ # Generate a list of IPs by parsing the output of ip/ifconfig
+ ip_list = [x.group() for x in re.finditer(regexip, standardoutput)]
+
+ # Check if ip is in list
+ if ip in ip_list:
+ return True
+ else:
+ return False
+
+
+class TestGetIP(unittest.TestCase):
+
+ def test_get_ip(self):
+ """ Attempt to test the IP address returned by
+ moznetwork.get_ip() is valid """
+
+ ip = moznetwork.get_ip()
+
+ # Check the IP returned by moznetwork is in the list
+ self.assertTrue(verify_ip_in_list(ip))
+
+ def test_get_ip_using_get_interface(self):
+ """ Test that the control flow path for get_ip() using
+ _get_interface_list() is works """
+
+ if mozinfo.isLinux or mozinfo.isMac:
+
+ with mock.patch('socket.gethostbyname') as byname:
+ # Force socket.gethostbyname to return None
+ byname.return_value = None
+
+ ip = moznetwork.get_ip()
+
+ # Check the IP returned by moznetwork is in the list
+ self.assertTrue(verify_ip_in_list(ip))
+
+
+if __name__ == '__main__':
+ unittest.main()