Example #1
0
 def __init__(self, options, browsers):
     from internal.browsers import Browsers
     from internal.webpagetest import WebPageTest
     from internal.traffic_shaping import TrafficShaper
     from internal.adb import Adb
     from internal.ios_device import iOSDevice
     self.must_exit = False
     self.options = options
     self.capture_display = None
     self.job = None
     self.task = None
     self.xvfb = None
     self.root_path = os.path.abspath(os.path.dirname(__file__))
     self.wpt = WebPageTest(options, os.path.join(self.root_path, "work"))
     self.persistent_work_dir = self.wpt.get_persistent_dir()
     self.adb = Adb(
         self.options,
         self.persistent_work_dir) if self.options.android else None
     self.ios = iOSDevice(self.options.device) if self.options.iOS else None
     self.browsers = Browsers(options, browsers, self.adb, self.ios)
     self.shaper = TrafficShaper(options)
     atexit.register(self.cleanup)
     signal.signal(signal.SIGTERM, self.signal_handler)
     signal.signal(signal.SIGINT, self.signal_handler)
     self.image_magick = {
         'convert': 'convert',
         'compare': 'compare',
         'mogrify': 'mogrify'
     }
     if platform.system() == "Windows":
         paths = [os.getenv('ProgramFiles'), os.getenv('ProgramFiles(x86)')]
         for path in paths:
             if path is not None and os.path.isdir(path):
                 dirs = sorted(os.listdir(path), reverse=True)
                 for subdir in dirs:
                     if subdir.lower().startswith('imagemagick'):
                         convert = os.path.join(path, subdir, 'convert.exe')
                         compare = os.path.join(path, subdir, 'compare.exe')
                         mogrify = os.path.join(path, subdir, 'mogrify.exe')
                         if os.path.isfile(convert) and \
                                 os.path.isfile(compare) and \
                                 os.path.isfile(mogrify):
                             if convert.find(' ') >= 0:
                                 convert = '"{0}"'.format(convert)
                             if compare.find(' ') >= 0:
                                 compare = '"{0}"'.format(compare)
                             if mogrify.find(' ') >= 0:
                                 mogrify = '"{0}"'.format(mogrify)
                             self.image_magick['convert'] = convert
                             self.image_magick['compare'] = compare
                             self.image_magick['mogrify'] = mogrify
                             break
Example #2
0
 def __init__(self, options, browsers):
     from internal.browsers import Browsers
     from internal.webpagetest import WebPageTest
     from internal.traffic_shaping import TrafficShaper
     from internal.adb import Adb
     from internal.ios_device import iOSDevice
     self.must_exit = False
     self.options = options
     self.capture_display = None
     self.job = None
     self.task = None
     self.xvfb = None
     self.root_path = os.path.abspath(os.path.dirname(__file__))
     self.wpt = WebPageTest(options, os.path.join(self.root_path, "work"))
     self.persistent_work_dir = self.wpt.get_persistent_dir()
     self.adb = Adb(
         self.options,
         self.persistent_work_dir) if self.options.android else None
     self.ios = iOSDevice(self.options.device) if self.options.iOS else None
     self.browsers = Browsers(options, browsers, self.adb, self.ios)
     self.shaper = TrafficShaper(options)
     atexit.register(self.cleanup)
     signal.signal(signal.SIGTERM, self.signal_handler)
     signal.signal(signal.SIGINT, self.signal_handler)
Example #3
0
def main():
    """Startup and initialization"""
    import argparse
    parser = argparse.ArgumentParser(description='WebPageTest Agent.', prog='wpt-agent')
    # Basic agent config
    parser.add_argument('-v', '--verbose', action='count',
                        help="Increase verbosity (specify multiple times for more)."
                        " -vvvv for full debug output.")
    parser.add_argument('--name', help="Agent name (for the work directory).")
    parser.add_argument('--exit', type=int, default=0,
                        help='Exit after the specified number of minutes.\n'
                        '    Useful for running in a shell script that does some maintenence\n'
                        '    or updates periodically (like hourly).')
    parser.add_argument('--dockerized', action='store_true', default=False,
                        help="Agent is running in a docker container.")
    parser.add_argument('--ec2', action='store_true', default=False,
                        help="Load config settings from EC2 user data.")
    parser.add_argument('--gce', action='store_true', default=False,
                        help="Load config settings from GCE user data.")
    parser.add_argument('--alive',
                        help="Watchdog file to update when successfully connected.")
    parser.add_argument('--log',
                        help="Log critical errors to the given file.")
    parser.add_argument('--noidle', action='store_true', default=False,
                        help="Do not wait for system idle at startup.")
    parser.add_argument('--collectversion', action='store_true', default=False,
                        help="Collection browser versions and submit to controller.")                        

    # Video capture/display settings
    parser.add_argument('--xvfb', action='store_true', default=False,
                        help="Use an xvfb virtual display (Linux only).")
    parser.add_argument('--fps', type=int, choices=xrange(1, 61), default=10,
                        help='Video capture frame rate (defaults to 10). '
                             'Valid range is 1-60 (Linux only).')

    # Server/location configuration
    parser.add_argument('--server',
                        help="URL for WebPageTest work (i.e. http://www.webpagetest.org/work/).")
    parser.add_argument('--validcertificate', action='store_true', default=False,
                        help="Validate server certificates (HTTPS server, defaults to False).")
    parser.add_argument('--location',
                        help="Location ID (as configured in locations.ini on the server).")
    parser.add_argument('--key', help="Location key (optional).")
    parser.add_argument('--polling', type=int, default=5,
                        help='Polling interval for work (defaults to 5 seconds).')

    # Traffic-shaping options (defaults to host-based)
    parser.add_argument('--shaper', help='Override default traffic shaper. '
                        'Current supported values are:\n'
                        '    none - Disable traffic-shaping (i.e. when root is not available)\n.'
                        '    netem,<interface> - Use NetEm for bridging rndis traffic '
                        '(specify outbound interface).  i.e. --shaper netem,eth0\n'
                        '    remote,<server>,<down pipe>,<up pipe> - Connect to the remote server '
                        'over ssh and use pre-configured dummynet pipes (ssh keys for root user '
                        'should be pre-authorized).')
    parser.add_argument('--tcpdump', help='Specify an interface to use for tcpdump.')

    # CPU Throttling
    parser.add_argument('--throttle', action='store_true', default=False,
                        help='Enable cgroup-based CPU throttling for mobile emulation '
                        '(Linux only).')

    # Android options
    parser.add_argument('--android', action='store_true', default=False,
                        help="Run tests on an attached android device.")
    parser.add_argument('--device',
                        help="Device ID (only needed if more than one android device attached).")
    parser.add_argument('--simplert',
                        help="Use SimpleRT for reverse-tethering.  The APK should "
                        "be installed manually (adb install simple-rt/simple-rt-1.1.apk) and "
                        "tested once manually (./simple-rt -i eth0 then disconnect and re-connect"
                        " phone) to dismiss any system dialogs.  The ethernet interface and DNS "
                        "server should be passed as options:\n"
                        "    <interface>,<dns1>: i.e. --simplert eth0,8.8.8.8")
    parser.add_argument('--gnirehtet',
                        help="Use gnirehtet for reverse-tethering. You will need to manually "
                        "approve the vpn once per mobile device. Valid options are:\n"
                        "   <interface>,<dns>: i.e. --gnirehtet eth0,8.8.8.8")
    parser.add_argument('--vpntether',
                        help="Use vpn-reverse-tether for reverse-tethering. This is the "
                        "recommended way to reverse-tether devices. You will need to manually "
                        "approve the vpn once per mobile device. Valid options are:\n"
                        "   <interface>,<dns>: i.e. --vpntether eth0,8.8.8.8")
    parser.add_argument('--rndis',
                        help="(deprecated) Enable reverse-tethering over rndis. "
                        "Valid options are:\n"
                        "    dhcp: Configure interface for DHCP\n"
                        "    <ip>/<network>,<gateway>,<dns1>,<dns2>: Static Address.  \n"
                        "        i.e. 192.168.0.8/24,192.168.0.1,8.8.8.8,8.8.4.4")
    parser.add_argument('--ping', type=str, default='8.8.8.8',
                        help="Set custom IP or domain to ping for checking network connection "
                        "when using Android devices. Default is 8.8.8.8")
    parser.add_argument('--temperature', type=int, default=36,
                        help="set custom temperature treshold for device as int")

    # iOS options
    parser.add_argument('--iOS', action='store_true', default=False,
                        help="Run tests on an attached iOS device "
                        "(specify serial number in --device).")
    parser.add_argument('--list', action='store_true', default=False,
                        help="List available iOS devices.")

    # Options for authenticating the agent with the server
    parser.add_argument('--username',
                        help="User name if using HTTP Basic auth with WebPageTest server.")
    parser.add_argument('--password',
                        help="Password if using HTTP Basic auth with WebPageTest server.")
    parser.add_argument('--cert', help="Client certificate if using certificates to "
                        "authenticate the WebPageTest server connection.")
    parser.add_argument('--certkey', help="Client-side private key (if not embedded in the cert).")
    options, _ = parser.parse_known_args()

    # Make sure we are running python 2.7.11 or newer (required for Windows 8.1)
    if platform.system() == "Windows":
        if sys.version_info[0] != 2 or \
                sys.version_info[1] != 7 or \
                sys.version_info[2] < 11:
            print "Requires python 2.7.11 (or later)"
            exit(1)
    elif sys.version_info[0] != 2 or sys.version_info[1] != 7:
        print "Requires python 2.7"
        exit(1)

    if options.list:
        from internal.ios_device import iOSDevice
        ios = iOSDevice()
        devices = ios.get_devices()
        print "Available iOS devices:"
        for device in devices:
            print device
        exit(1)

    # Set up logging
    log_level = logging.CRITICAL
    if options.verbose == 1:
        log_level = logging.ERROR
    elif options.verbose == 2:
        log_level = logging.WARNING
    elif options.verbose == 3:
        log_level = logging.INFO
    elif options.verbose >= 4:
        log_level = logging.DEBUG
    logging.basicConfig(level=log_level, format="%(asctime)s.%(msecs)03d - %(message)s",
                        datefmt="%H:%M:%S")

    if options.log:
        err_log = logging.handlers.RotatingFileHandler(options.log, maxBytes=1000000,
                                                       backupCount=5, delay=True)
        err_log.setLevel(logging.ERROR)
        logging.getLogger().addHandler(err_log)

    if options.ec2 or options.gce:
        upgrade_pip_modules()
    elif platform.system() == "Windows":
        # recovery for a busted Windows install
        try:
            import win32api
        except ImportError:
            subprocess.call([sys.executable, '-m', 'pip', 'uninstall', '-y',
                             'pywin32', 'pypiwin32'])
            subprocess.call([sys.executable, '-m', 'pip', 'install', 'pywin32', 'pypiwin32'])

    browsers = None
    if not options.android and not options.iOS:
        browsers = find_browsers()
        if len(browsers) == 0:
            print "No browsers configured. Check that browsers.ini is present and correct."
            exit(1)

    if options.collectversion and platform.system() == "Windows":
        get_browser_versions(browsers)

    agent = WPTAgent(options, browsers)
    if agent.startup():
        # Create a work directory relative to where we are running
        print "Running agent, hit Ctrl+C to exit"
        agent.run_testing()
        print "Done"