コード例 #1
0
 def test_scan_for_errors_is_dead(self, search_file_mock, is_alive_mock):
     is_alive_mock.return_value = False
     search_file_mock.return_value = False
     radvd_mock = Radvd('mock_runner', 'wlan0')
     with self.assertRaises(Error) as context:
         radvd_mock._scan_for_errors(True)
     self.assertTrue('Radvd failed to start' in str(context.exception))
コード例 #2
0
 def test_scan_for_errors_exited_prematurely(self, search_file_mock,
                                             is_alive_mock):
     is_alive_mock.return_value = True
     search_file_mock.return_value = True
     radvd_mock = Radvd('mock_runner', 'wlan0')
     with self.assertRaises(Error) as context:
         radvd_mock._scan_for_errors(True)
     self.assertTrue('Radvd exited prematurely.' in str(context.exception))
コード例 #3
0
 def test_write_configs_complex(self, write_file, delete_file):
     delete_file.side_effect = delete_file_mock
     write_file.side_effect = write_configs_mock
     complex_radvd_config = RadvdConfig(
         prefix=RADVD_PREFIX,
         clients=['fe80::c66d:3c75:2cec:1d72', 'fe80::c66d:3c75:2cec:1d73'],
         route=RADVD_PREFIX,
         rdnss=[
             '2401:fa00:480:7a00:4d56:5373:4549:1e29',
             '2401:fa00:480:7a00:4d56:5373:4549:1e30',
         ],
         ignore_if_missing=radvd_constants.IGNORE_IF_MISSING_ON,
         adv_send_advert=radvd_constants.ADV_SEND_ADVERT_OFF,
         unicast_only=radvd_constants.UNICAST_ONLY_ON,
         max_rtr_adv_interval=60,
         min_rtr_adv_interval=5,
         min_delay_between_ras=5,
         adv_managed_flag=radvd_constants.ADV_MANAGED_FLAG_OFF,
         adv_other_config_flag=radvd_constants.ADV_OTHER_CONFIG_FLAG_ON,
         adv_link_mtu=1400,
         adv_reachable_time=3600000,
         adv_retrans_timer=10,
         adv_cur_hop_limit=50,
         adv_default_lifetime=8000,
         adv_default_preference=radvd_constants.ADV_DEFAULT_PREFERENCE_OFF,
         adv_source_ll_address=radvd_constants.ADV_SOURCE_LL_ADDRESS_ON,
         adv_home_agent_flag=radvd_constants.ADV_HOME_AGENT_FLAG_OFF,
         adv_home_agent_info=radvd_constants.ADV_HOME_AGENT_INFO_ON,
         home_agent_lifetime=100,
         home_agent_preference=100,
         adv_mob_rtr_support_flag=radvd_constants.
         ADV_MOB_RTR_SUPPORT_FLAG_OFF,
         adv_interval_opt=radvd_constants.ADV_INTERVAL_OPT_ON,
         adv_on_link=radvd_constants.ADV_ON_LINK_OFF,
         adv_autonomous=radvd_constants.ADV_AUTONOMOUS_ON,
         adv_router_addr=radvd_constants.ADV_ROUTER_ADDR_OFF,
         adv_valid_lifetime=86400,
         adv_preferred_lifetime=14400,
         base_6to4_interface='NA',
         adv_route_lifetime=1024,
         adv_route_preference=radvd_constants.ADV_ROUTE_PREFERENCE_HIGH,
         adv_rdnss_preference=8,
         adv_rdnss_open=radvd_constants.ADV_RDNSS_OPEN_ON,
         adv_rdnss_lifetime=1025)
     radvd_mock = Radvd('mock_runner', 'wlan0')
     radvd_mock._write_configs(complex_radvd_config)
     radvd_config = radvd_mock._config_file
     with open(radvd_config, 'r') as radvd_config_fileId:
         config_data = radvd_config_fileId.read()
         self.assertTrue(CORRECT_COMPLEX_RADVD_CONFIG == config_data)
コード例 #4
0
 def test_write_configs_simple(self, write_file, delete_file):
     delete_file.side_effect = delete_file_mock
     write_file.side_effect = write_configs_mock
     basic_radvd_config = RadvdConfig(
         prefix=RADVD_PREFIX,
         adv_send_advert=radvd_constants.ADV_SEND_ADVERT_ON,
         adv_on_link=radvd_constants.ADV_ON_LINK_ON,
         adv_autonomous=radvd_constants.ADV_AUTONOMOUS_ON)
     radvd_mock = Radvd('mock_runner', 'wlan0')
     radvd_mock._write_configs(basic_radvd_config)
     radvd_config = radvd_mock._config_file
     with open(radvd_config, 'r') as radvd_config_fileId:
         config_data = radvd_config_fileId.read()
         self.assertTrue(CORRECT_SIMPLE_RADVD_CONFIG == config_data)
    def setup_ap(self, ssid, band, ipv4=True, ipv6=False):
        """Setup ap with basic config.

        Args:
            ssid: string, ssid to setup on ap
            band: string ('2g' or '5g') of band to setup.
            ipv4: True if using ipv4 (dhcp), else False.
            ipv6: True if using ipv6 (radvd), else False.
        """
        if band == BAND_2G:
            wlan_utils.setup_ap(access_point=self.access_point,
                                profile_name='whirlwind',
                                channel=11,
                                ssid=ssid)
        elif band == BAND_5G:
            wlan_utils.setup_ap(access_point=self.access_point,
                                profile_name='whirlwind',
                                channel=36,
                                ssid=ssid)

        if not ipv4:
            self.access_point.stop_dhcp()
        if ipv6:
            radvd_config = RadvdConfig(
                prefix='fd00::/64',
                adv_send_advert=radvd_constants.ADV_SEND_ADVERT_ON,
                adv_on_link=radvd_constants.ADV_ON_LINK_ON,
                adv_autonomous=radvd_constants.ADV_AUTONOMOUS_ON)

            if band == BAND_2G:
                self.router_adv_daemon = Radvd(self.access_point.ssh,
                                               self.access_point.wlan_2g)
            elif band == BAND_5G:
                self.router_adv_daemon = Radvd(self.access_point.ssh,
                                               self.access_point.wlan_5g)
            self.router_adv_daemon.start(radvd_config)

        self.log.info('Network (SSID: %s) is up.' % ssid)
コード例 #6
0
 def test_scan_for_errors_success(self, search_file_mock, is_alive_mock):
     is_alive_mock.return_value = True
     search_file_mock.return_value = False
     radvd_mock = Radvd('mock_runner', 'wlan0')
     self.assertIsNone(radvd_mock._scan_for_errors(True))
コード例 #7
0
 def test_wait_for_process_process_alive(self, is_alive_mock,
                                         _scan_for_errors_mock):
     is_alive_mock.return_value = True
     _scan_for_errors_mock.return_value = True
     radvd_mock = Radvd('mock_runner', 'wlan0')
     self.assertIsNone(radvd_mock._wait_for_process(timeout=2))
コード例 #8
0
 def test_radvd_is_alive_False(self, is_alive_mock):
     is_alive_mock.return_value = False
     radvd_mock = Radvd('mock_runner', 'wlan0')
     self.assertFalse(radvd_mock.is_alive())
コード例 #9
0
 def test_radvd_ikill(self, kill):
     kill.return_value = True
     radvd_mock = Radvd('mock_runner', 'wlan0')
     self.assertIsNone(radvd_mock.stop())
コード例 #10
0
    def run_rvr(self,
                ssid,
                password=None,
                band='2g',
                traffic_dir='tx',
                ip_version=4):
        """Setups and runs the RvR test

        Args:
            ssid: The SSID for the client to associate to.
            password: Password for the network, if necessary.
            band: 2g or 5g
            traffic_dir: rx or tx, bi is not supported by iperf3
            ip_version: 4 or 6

        Returns:
            The bokeh graph data.
        """
        throughput = []
        relative_attn = []
        self.check_if_has_private_local_ipv6_address = True
        if band == '2g':
            rvr_attenuators = self.attenuators_2g
        elif band == '5g':
            rvr_attenuators = self.attenuators_5g
        else:
            raise ValueError('Invalid WLAN band specified: %s' % band)
        if ip_version == 6:
            ravdvd_config = RadvdConfig(
                prefix=RADVD_PREFIX,
                adv_send_advert=radvd_constants.ADV_SEND_ADVERT_ON,
                adv_on_link=radvd_constants.ADV_ON_LINK_ON,
                adv_autonomous=radvd_constants.ADV_AUTONOMOUS_ON)
            self.router_adv_daemon = Radvd(
                self.access_point.ssh,
                self.access_point.interfaces.get_bridge_interface()[0])
            self.router_adv_daemon.start(ravdvd_config)

        for rvr_loop_counter in range(0, self.debug_loop_count):
            for rvr_attenuator in rvr_attenuators:
                rvr_attenuator.set_atten(self.starting_attn)

            associate_counter = 0
            associate_max_attempts = 3
            while associate_counter < associate_max_attempts:
                if associate(self.dut,
                             ssid,
                             password,
                             check_connectivity=False):
                    break
                else:
                    associate_counter += 1
            if associate_counter == associate_max_attempts:
                asserts.fail('Unable to associate at starting '
                             'attenuation: %s' % self.starting_attn)

            ip_address_checker_counter = 0
            ip_address_checker_max_attempts = 3
            while ip_address_checker_counter < ip_address_checker_max_attempts:
                self.iperf_server.renew_test_interface_ip_address()
                iperf_server_ip_addresses = (
                    self.iperf_server.get_interface_ip_addresses(
                        self.iperf_server.test_interface))
                dut_ip_addresses = self.dut.get_interface_ip_addresses(
                    self.dut_iperf_client.test_interface)
                self.log.info('IPerf server IP info: %s' %
                              iperf_server_ip_addresses)
                self.log.info('DUT IP info: %s' % dut_ip_addresses)
                if ip_version == 4:
                    if iperf_server_ip_addresses['ipv4_private']:
                        iperf_server_ip_address = (
                            iperf_server_ip_addresses['ipv4_private'][0])
                    if not dut_ip_addresses['ipv4_private']:
                        self.log.warn('Unable to get IPv4 address at starting '
                                      'attenuation: %s Retrying.' %
                                      self.starting_attn)
                        ip_address_checker_counter += 1
                        time.sleep(1)
                    else:
                        break
                elif ip_version == 6:
                    if iperf_server_ip_addresses['ipv6_private_local']:
                        iperf_server_ip_address = (
                            iperf_server_ip_addresses['ipv6_private_local'][0])
                    else:
                        self.check_if_has_private_local_ipv6_address = False
                        iperf_server_ip_address = (
                            '%s%%%s' %
                            (iperf_server_ip_addresses['ipv6_link_local'][0],
                             self.dut_iperf_client.test_interface))
                    if self.check_if_has_private_local_ipv6_address:
                        if not dut_ip_addresses['ipv6_private_local']:
                            self.log.warn('Unable to get IPv6 address at '
                                          'starting attenuation: %s' %
                                          self.starting_attn)
                            ip_address_checker_counter += 1
                            time.sleep(1)
                        else:
                            break
                    else:
                        break
                else:
                    raise ValueError('Invalid IP version: %s' % ip_version)
            if ip_address_checker_counter == ip_address_checker_max_attempts:
                if self.dut.ping(iperf_server_ip_address):
                    self.log.error('IPerf server is pingable. Continuing with '
                                   'test.  The missing IP address information '
                                   'should be marked as a bug.')
                else:
                    asserts.fail('DUT was unable to get IPv%s address and '
                                 'could not ping the IPerf server.' %
                                 str(ip_version))
            throughput, relative_attn = (self.rvr_loop(
                traffic_dir,
                rvr_attenuators,
                iperf_server_ip_address,
                ip_version,
                throughput=throughput,
                relative_attn=relative_attn))
            if self.reverse_rvr_after_forward:
                throughput, relative_attn = self.rvr_loop(
                    traffic_dir,
                    rvr_attenuators,
                    iperf_server_ip_address,
                    ip_version,
                    ssid=ssid,
                    password=password,
                    reverse=True,
                    throughput=throughput,
                    relative_attn=relative_attn)
            self.dut.disconnect()

        throughput_vs_attn = {
            'throughput': throughput,
            'relative_attn': relative_attn,
            'x_label': 'Attenuation(db)',
            'y_label': 'Throughput(%s)' % REPORTING_SPEED_UNITS
        }
        graph_data = {'throughput_vs_attn': throughput_vs_attn}
        return graph_data
コード例 #11
0
class WlanRvrTest(AbstractDeviceWlanDeviceBaseTest):
    """Tests running WLAN RvR.

    Test Bed Requirement:
    * One Android device or Fuchsia device
    * One Access Point
    * One attenuator
    * One Linux iPerf Server
    """
    def __init__(self, controllers):
        WifiBaseTest.__init__(self, controllers)
        self.rvr_graph_summary = []

    def setup_class(self):
        super(WlanRvrTest, self).setup_class()
        if 'dut' in self.user_params:
            if self.user_params['dut'] == 'fuchsia_devices':
                self.dut = create_wlan_device(self.fuchsia_devices[0])
            elif self.user_params['dut'] == 'android_devices':
                self.dut = create_wlan_device(self.android_devices[0])
            else:
                raise ValueError('Invalid DUT specified in config. (%s)' %
                                 self.user_params['dut'])
        else:
            # Default is an android device, just like the other tests
            self.dut = create_wlan_device(self.android_devices[0])

        self.starting_attn = (self.user_params['rvr_settings'].get(
            'starting_attn', 0))

        self.ending_attn = (self.user_params['rvr_settings'].get(
            'ending_attn', 95))

        self.step_size_in_db = (self.user_params['rvr_settings'].get(
            'step_size_in_db', 1))

        self.dwell_time_in_secs = (self.user_params['rvr_settings'].get(
            'dwell_time_in_secs', 10))

        self.reverse_rvr_after_forward = bool(
            (self.user_params['rvr_settings'].get('reverse_rvr_after_forward',
                                                  None)))

        self.iperf_flags = (self.user_params['rvr_settings'].get(
            'iperf_flags', '-i 1'))

        self.iperf_flags = '%s -t %s -J' % (self.iperf_flags,
                                            self.dwell_time_in_secs)

        self.debug_loop_count = (self.user_params['rvr_settings'].get(
            'debug_loop_count', 1))

        self.debug_pre_traffic_cmd = (self.user_params['rvr_settings'].get(
            'debug_pre_traffic_cmd', None))

        self.debug_post_traffic_cmd = (self.user_params['rvr_settings'].get(
            'debug_post_traffic_cmd', None))

        self.router_adv_daemon = None
        self.check_if_has_private_local_ipv6_address = True

        if self.ending_attn == 'auto':
            self.use_auto_end = True
            self.ending_attn = 100
            if self.step_size_in_db > 2:
                asserts.fail('When using an ending attenuation of \'auto\' '
                             'please use a value < 2db.  Larger jumps will '
                             'break the test reporting.')

        self.access_point = self.access_points[0]
        self.attenuators_2g = get_attenuators_for_device(
            self.controller_configs['AccessPoint'][0]['Attenuator'],
            self.attenuators, 'attenuator_ports_wifi_2g')
        self.attenuators_5g = get_attenuators_for_device(
            self.controller_configs['AccessPoint'][0]['Attenuator'],
            self.attenuators, 'attenuator_ports_wifi_5g')

        self.iperf_server = self.iperf_servers[0]
        self.dut_iperf_client = self.iperf_clients[0]

        self.access_point.stop_all_aps()

    def setup_test(self):
        if self.iperf_server:
            self.iperf_server.start()
        if hasattr(self, "android_devices"):
            for ad in self.android_devices:
                ad.droid.wakeLockAcquireBright()
                ad.droid.wakeUpNow()
        self.dut.wifi_toggle_state(True)

    def teardown_test(self):
        self.cleanup_tests()

    def teardown_class(self):
        if self.router_adv_daemon:
            self.router_adv_daemon.stop()
        try:
            output_path = context.get_current_context().get_base_output_path()
            test_class_name = context.get_current_context().test_class_name

            output_file(f'{output_path}/{test_class_name}/rvr_summary.html',
                        title='RvR Sumamry')
            save(list(self.rvr_graph_summary))
        except Exception as e:
            self.log.info('Unable to generate RvR summary file due '
                          'to Exception')
            self.log.info(e)

    def on_fail(self, test_name, begin_time):
        super().on_fail(test_name, begin_time)
        self.cleanup_tests()

    def cleanup_tests(self):
        """Cleans up all the dangling pieces of the tests, for example, the
        iperf server, radvd, all the currently running APs, and the various
        clients running during the tests.
        """

        if self.router_adv_daemon:
            self.router_adv_daemon.stop()
        if hasattr(self, "android_devices"):
            for ad in self.android_devices:
                ad.droid.wakeLockRelease()
                ad.droid.goToSleepNow()
        if self.iperf_server:
            self.iperf_server.stop()
        self.dut.turn_location_off_and_scan_toggle_off()
        self.dut.disconnect()
        self.dut.reset_wifi()
        self.access_point.stop_all_aps()

    def run_rvr(self,
                ssid,
                password=None,
                band='2g',
                traffic_dir='tx',
                ip_version=4):
        """Setups and runs the RvR test

        Args:
            ssid: The SSID for the client to associate to.
            password: Password for the network, if necessary.
            band: 2g or 5g
            traffic_dir: rx or tx, bi is not supported by iperf3
            ip_version: 4 or 6

        Returns:
            The bokeh graph data.
        """
        throughput = []
        relative_attn = []
        self.check_if_has_private_local_ipv6_address = True
        if band == '2g':
            rvr_attenuators = self.attenuators_2g
        elif band == '5g':
            rvr_attenuators = self.attenuators_5g
        else:
            raise ValueError('Invalid WLAN band specified: %s' % band)
        if ip_version == 6:
            ravdvd_config = RadvdConfig(
                prefix=RADVD_PREFIX,
                adv_send_advert=radvd_constants.ADV_SEND_ADVERT_ON,
                adv_on_link=radvd_constants.ADV_ON_LINK_ON,
                adv_autonomous=radvd_constants.ADV_AUTONOMOUS_ON)
            self.router_adv_daemon = Radvd(
                self.access_point.ssh,
                self.access_point.interfaces.get_bridge_interface()[0])
            self.router_adv_daemon.start(ravdvd_config)

        for rvr_loop_counter in range(0, self.debug_loop_count):
            for rvr_attenuator in rvr_attenuators:
                rvr_attenuator.set_atten(self.starting_attn)

            associate_counter = 0
            associate_max_attempts = 3
            while associate_counter < associate_max_attempts:
                if associate(self.dut,
                             ssid,
                             password,
                             check_connectivity=False):
                    break
                else:
                    associate_counter += 1
            if associate_counter == associate_max_attempts:
                asserts.fail('Unable to associate at starting '
                             'attenuation: %s' % self.starting_attn)

            ip_address_checker_counter = 0
            ip_address_checker_max_attempts = 3
            while ip_address_checker_counter < ip_address_checker_max_attempts:
                self.iperf_server.renew_test_interface_ip_address()
                iperf_server_ip_addresses = (
                    self.iperf_server.get_interface_ip_addresses(
                        self.iperf_server.test_interface))
                dut_ip_addresses = self.dut.get_interface_ip_addresses(
                    self.dut_iperf_client.test_interface)
                self.log.info('IPerf server IP info: %s' %
                              iperf_server_ip_addresses)
                self.log.info('DUT IP info: %s' % dut_ip_addresses)
                if ip_version == 4:
                    if iperf_server_ip_addresses['ipv4_private']:
                        iperf_server_ip_address = (
                            iperf_server_ip_addresses['ipv4_private'][0])
                    if not dut_ip_addresses['ipv4_private']:
                        self.log.warn('Unable to get IPv4 address at starting '
                                      'attenuation: %s Retrying.' %
                                      self.starting_attn)
                        ip_address_checker_counter += 1
                        time.sleep(1)
                    else:
                        break
                elif ip_version == 6:
                    if iperf_server_ip_addresses['ipv6_private_local']:
                        iperf_server_ip_address = (
                            iperf_server_ip_addresses['ipv6_private_local'][0])
                    else:
                        self.check_if_has_private_local_ipv6_address = False
                        iperf_server_ip_address = (
                            '%s%%%s' %
                            (iperf_server_ip_addresses['ipv6_link_local'][0],
                             self.dut_iperf_client.test_interface))
                    if self.check_if_has_private_local_ipv6_address:
                        if not dut_ip_addresses['ipv6_private_local']:
                            self.log.warn('Unable to get IPv6 address at '
                                          'starting attenuation: %s' %
                                          self.starting_attn)
                            ip_address_checker_counter += 1
                            time.sleep(1)
                        else:
                            break
                    else:
                        break
                else:
                    raise ValueError('Invalid IP version: %s' % ip_version)
            if ip_address_checker_counter == ip_address_checker_max_attempts:
                if self.dut.ping(iperf_server_ip_address):
                    self.log.error('IPerf server is pingable. Continuing with '
                                   'test.  The missing IP address information '
                                   'should be marked as a bug.')
                else:
                    asserts.fail('DUT was unable to get IPv%s address and '
                                 'could not ping the IPerf server.' %
                                 str(ip_version))
            throughput, relative_attn = (self.rvr_loop(
                traffic_dir,
                rvr_attenuators,
                iperf_server_ip_address,
                ip_version,
                throughput=throughput,
                relative_attn=relative_attn))
            if self.reverse_rvr_after_forward:
                throughput, relative_attn = self.rvr_loop(
                    traffic_dir,
                    rvr_attenuators,
                    iperf_server_ip_address,
                    ip_version,
                    ssid=ssid,
                    password=password,
                    reverse=True,
                    throughput=throughput,
                    relative_attn=relative_attn)
            self.dut.disconnect()

        throughput_vs_attn = {
            'throughput': throughput,
            'relative_attn': relative_attn,
            'x_label': 'Attenuation(db)',
            'y_label': 'Throughput(%s)' % REPORTING_SPEED_UNITS
        }
        graph_data = {'throughput_vs_attn': throughput_vs_attn}
        return graph_data

    def rvr_loop(self,
                 traffic_dir,
                 rvr_attenuators,
                 iperf_server_ip_address,
                 ip_version,
                 ssid=None,
                 password=None,
                 reverse=False,
                 throughput=None,
                 relative_attn=None):
        """The loop that goes through each attenuation level and runs the iperf
        throughput pair.
        Args:
            traffic_dir: The traffic direction from the perspective of the DUT.
            rvr_attenuators: A list of attenuators to set.
            iperf_server_ip_address: The IP address of the iperf server.
            ssid: The ssid of the wireless network that the should associated
                to.
            password: Password of the wireless network.
            reverse: Whether to run RvR test starting from the highest
                attenuation and going to the lowest.  This is run after the
                normal low attenuation to high attenuation RvR test.
            throughput: The list of throughput data for the test.
            relative_attn: The list of attenuation data for the test.

        Returns:
            throughput: The list of throughput data for the test.
            relative_attn: The list of attenuation data for the test.
            """
        iperf_flags = self.iperf_flags
        if traffic_dir == 'rx':
            iperf_flags = '%s -R' % self.iperf_flags
        starting_attn = self.starting_attn
        ending_attn = self.ending_attn
        step_size_in_db = self.step_size_in_db
        if reverse:
            starting_attn = self.ending_attn
            ending_attn = self.starting_attn
            step_size_in_db = step_size_in_db * -1
            self.dut.disconnect()
        for step in range(starting_attn, ending_attn, step_size_in_db):
            try:
                for attenuator in rvr_attenuators:
                    attenuator.set_atten(step)
            except ValueError:
                self.log.info('%s is beyond the max or min of the testbed '
                              'attenuator\'s capability. Stopping.')
                break
            self.log.info('Set relative attenuation to %s db' % step)

            associated = self.dut.is_connected()
            if associated:
                self.log.info('DUT is currently associated.')
            else:
                self.log.info('DUT is not currently associated.')

            if reverse:
                if not associated:
                    self.log.info('Trying to associate at relative '
                                  'attenuation of %s db' % step)
                    if associate(self.dut,
                                 ssid,
                                 password,
                                 check_connectivity=False):
                        associated = True
                        self.log.info('Successfully associated.')
                    else:
                        associated = False
                        self.log.info(
                            'Association failed. Marking a 0 %s for'
                            ' throughput. Skipping running traffic.' %
                            REPORTING_SPEED_UNITS)
            attn_value_inserted = False
            value_to_insert = str(step)
            while not attn_value_inserted:
                if value_to_insert in relative_attn:
                    value_to_insert = '%s ' % value_to_insert
                else:
                    relative_attn.append(value_to_insert)
                    attn_value_inserted = True

            dut_ip_addresses = self.dut.get_interface_ip_addresses(
                self.dut_iperf_client.test_interface)
            if ip_version == 4:
                if not dut_ip_addresses['ipv4_private']:
                    self.log.info('DUT does not have an IPv4 address. '
                                  'Traffic attempt to be run if the server '
                                  'is pingable.')
                else:
                    self.log.info('DUT has the following IPv4 address: "%s"' %
                                  dut_ip_addresses['ipv4_private'][0])
            elif ip_version == 6:
                if self.check_if_has_private_local_ipv6_address:
                    if not dut_ip_addresses['ipv6_private_local']:
                        self.log.info(
                            'DUT does not have an IPv6 address. '
                            'Traffic attempt to be run if the server '
                            'is pingable.')
                    else:
                        self.log.info(
                            'DUT has the following IPv6 address: "%s"' %
                            dut_ip_addresses['ipv6_private_local'][0])
                else:
                    self.log.info('DUT has the following IPv6 address: "%s"' %
                                  dut_ip_addresses['ipv6_link_local'][0])
            server_pingable = self.dut.ping(iperf_server_ip_address)
            if not server_pingable:
                self.log.info('Iperf server "%s" is not pingable. Marking '
                              'a 0 %s for throughput. Skipping running '
                              'traffic.' %
                              (iperf_server_ip_address, REPORTING_SPEED_UNITS))
            else:
                self.log.info('Iperf server "%s" is pingable.' %
                              iperf_server_ip_address)
            if self.debug_pre_traffic_cmd:
                self.log.info('\nDEBUG: Sending command \'%s\' to DUT' %
                              self.debug_pre_traffic_cmd)
                self.log.info(
                    '\n%s' % self.dut.send_command(self.debug_pre_traffic_cmd))
            if server_pingable:
                if traffic_dir == 'tx':
                    self.log.info('Running traffic DUT to %s at relative '
                                  'attenuation of %s' %
                                  (iperf_server_ip_address, step))
                elif traffic_dir == 'rx':
                    self.log.info('Running traffic %s to DUT at relative '
                                  'attenuation of %s' %
                                  (iperf_server_ip_address, step))
                else:
                    raise ValueError('Invalid traffic direction')
                try:
                    iperf_tag = 'decreasing'
                    if reverse:
                        iperf_tag = 'increasing'
                    iperf_results_file = self.dut_iperf_client.start(
                        iperf_server_ip_address,
                        iperf_flags,
                        '%s_%s_%s' %
                        (iperf_tag, traffic_dir, self.starting_attn),
                        timeout=(self.dwell_time_in_secs * 2))
                except TimeoutError:
                    iperf_results_file = None
                    self.log.info('Iperf traffic timed out. Marking 0 %s for '
                                  'throughput.' % REPORTING_SPEED_UNITS)

                if not iperf_results_file:
                    throughput.append(0)
                else:
                    try:
                        iperf_results = IPerfResult(
                            iperf_results_file,
                            reporting_speed_units=REPORTING_SPEED_UNITS)
                        if iperf_results.error:
                            self.iperf_server.stop()
                            self.iperf_server.start()
                            self.log.info('\nErrors in iperf logs:\n%s' %
                                          iperf_results.error)
                        if not iperf_results.avg_send_rate:
                            throughput.append(0)
                        else:
                            throughput.append(iperf_results.avg_send_rate)
                    except ValueError:
                        self.iperf_server.stop()
                        self.iperf_server.start()
                        self.log.info(
                            'No data in Iperf file. Marking 0 %s for '
                            'throughput.' % REPORTING_SPEED_UNITS)
                        throughput.append(0)
                    except Exception as e:
                        self.iperf_server.stop()
                        self.iperf_server.start()
                        self.log.info('Unknown exception. Marking 0 %s for '
                                      'throughput.' % REPORTING_SPEED_UNITS)
                        self.log.error(e)
                        throughput.append(0)

                self.log.info(
                    'Iperf traffic complete. %s traffic received at '
                    '%s %s at relative attenuation of %s db' %
                    (traffic_dir, throughput[-1], REPORTING_SPEED_UNITS,
                     str(relative_attn[-1]).strip()))

            else:
                self.log.debug('DUT Associated: %s' % associated)
                self.log.debug('%s pingable: %s' %
                               (iperf_server_ip_address, server_pingable))
                throughput.append(0)
            if self.debug_post_traffic_cmd:
                self.log.info('\nDEBUG: Sending command \'%s\' to DUT' %
                              self.debug_post_traffic_cmd)
                self.log.info(
                    '\n%s' %
                    self.dut.send_command(self.debug_post_traffic_cmd))
        return throughput, relative_attn

    def test_rvr_11ac_5g_80mhz_open_tx_ipv4(self):
        ssid = rand_ascii_str(20)
        setup_ap(access_point=self.access_point,
                 profile_name='whirlwind',
                 channel=hostapd_constants.AP_DEFAULT_CHANNEL_5G,
                 ssid=ssid,
                 setup_bridge=True)
        graph_data = self.run_rvr(ssid,
                                  band='5g',
                                  traffic_dir='tx',
                                  ip_version=4)
        for rvr_graph in create_rvr_graph(
                self.test_name,
                context.get_current_context().get_full_output_path(),
                graph_data):
            self.rvr_graph_summary.append(rvr_graph)
        write_csv_rvr_data(
            self.test_name,
            context.get_current_context().get_full_output_path(), graph_data)

    def test_rvr_11ac_5g_80mhz_open_rx_ipv4(self):
        ssid = rand_ascii_str(20)
        setup_ap(access_point=self.access_point,
                 profile_name='whirlwind',
                 channel=hostapd_constants.AP_DEFAULT_CHANNEL_5G,
                 ssid=ssid,
                 setup_bridge=True)
        graph_data = self.run_rvr(ssid,
                                  band='5g',
                                  traffic_dir='rx',
                                  ip_version=4)
        for rvr_graph in create_rvr_graph(
                self.test_name,
                context.get_current_context().get_full_output_path(),
                graph_data):
            self.rvr_graph_summary.append(rvr_graph)
        write_csv_rvr_data(
            self.test_name,
            context.get_current_context().get_full_output_path(), graph_data)

    def test_rvr_11ac_5g_80mhz_open_tx_ipv6(self):
        ssid = rand_ascii_str(20)
        setup_ap(access_point=self.access_point,
                 profile_name='whirlwind',
                 channel=hostapd_constants.AP_DEFAULT_CHANNEL_5G,
                 ssid=ssid,
                 setup_bridge=True)
        graph_data = self.run_rvr(ssid,
                                  band='5g',
                                  traffic_dir='tx',
                                  ip_version=6)
        for rvr_graph in create_rvr_graph(
                self.test_name,
                context.get_current_context().get_full_output_path(),
                graph_data):
            self.rvr_graph_summary.append(rvr_graph)
        write_csv_rvr_data(
            self.test_name,
            context.get_current_context().get_full_output_path(), graph_data)

    def test_rvr_11ac_5g_80mhz_open_rx_ipv6(self):
        ssid = rand_ascii_str(20)
        setup_ap(access_point=self.access_point,
                 profile_name='whirlwind',
                 channel=hostapd_constants.AP_DEFAULT_CHANNEL_5G,
                 ssid=ssid,
                 setup_bridge=True)
        graph_data = self.run_rvr(ssid,
                                  band='5g',
                                  traffic_dir='rx',
                                  ip_version=6)
        for rvr_graph in create_rvr_graph(
                self.test_name,
                context.get_current_context().get_full_output_path(),
                graph_data):
            self.rvr_graph_summary.append(rvr_graph)
        write_csv_rvr_data(
            self.test_name,
            context.get_current_context().get_full_output_path(), graph_data)

    def test_rvr_11ac_5g_80mhz_wpa2_tx_ipv4(self):
        ssid = rand_ascii_str(20)
        password = rand_ascii_str(20)
        security_profile = Security(security_mode='wpa2', password=password)
        setup_ap(access_point=self.access_point,
                 profile_name='whirlwind',
                 channel=hostapd_constants.AP_DEFAULT_CHANNEL_5G,
                 ssid=ssid,
                 security=security_profile,
                 setup_bridge=True)
        graph_data = self.run_rvr(ssid,
                                  password=password,
                                  band='5g',
                                  traffic_dir='tx',
                                  ip_version=4)
        for rvr_graph in create_rvr_graph(
                self.test_name,
                context.get_current_context().get_full_output_path(),
                graph_data):
            self.rvr_graph_summary.append(rvr_graph)
        write_csv_rvr_data(
            self.test_name,
            context.get_current_context().get_full_output_path(), graph_data)

    def test_rvr_11ac_5g_80mhz_wpa2_rx_ipv4(self):
        ssid = rand_ascii_str(20)
        password = rand_ascii_str(20)
        security_profile = Security(security_mode='wpa2', password=password)
        setup_ap(access_point=self.access_point,
                 profile_name='whirlwind',
                 channel=hostapd_constants.AP_DEFAULT_CHANNEL_5G,
                 ssid=ssid,
                 security=security_profile,
                 setup_bridge=True)
        graph_data = self.run_rvr(ssid,
                                  password=password,
                                  band='5g',
                                  traffic_dir='rx',
                                  ip_version=4)
        for rvr_graph in create_rvr_graph(
                self.test_name,
                context.get_current_context().get_full_output_path(),
                graph_data):
            self.rvr_graph_summary.append(rvr_graph)
        write_csv_rvr_data(
            self.test_name,
            context.get_current_context().get_full_output_path(), graph_data)

    def test_rvr_11ac_5g_80mhz_wpa2_tx_ipv6(self):
        ssid = rand_ascii_str(20)
        password = rand_ascii_str(20)
        security_profile = Security(security_mode='wpa2', password=password)
        setup_ap(access_point=self.access_point,
                 profile_name='whirlwind',
                 channel=hostapd_constants.AP_DEFAULT_CHANNEL_5G,
                 ssid=ssid,
                 security=security_profile,
                 setup_bridge=True)
        graph_data = self.run_rvr(ssid,
                                  password=password,
                                  band='5g',
                                  traffic_dir='tx',
                                  ip_version=6)
        for rvr_graph in create_rvr_graph(
                self.test_name,
                context.get_current_context().get_full_output_path(),
                graph_data):
            self.rvr_graph_summary.append(rvr_graph)
        write_csv_rvr_data(
            self.test_name,
            context.get_current_context().get_full_output_path(), graph_data)

    def test_rvr_11ac_5g_80mhz_wpa2_rx_ipv6(self):
        ssid = rand_ascii_str(20)
        password = rand_ascii_str(20)
        security_profile = Security(security_mode='wpa2', password=password)
        setup_ap(access_point=self.access_point,
                 profile_name='whirlwind',
                 channel=hostapd_constants.AP_DEFAULT_CHANNEL_5G,
                 ssid=ssid,
                 security=security_profile,
                 setup_bridge=True)
        graph_data = self.run_rvr(ssid,
                                  password=password,
                                  band='5g',
                                  traffic_dir='rx',
                                  ip_version=6)
        for rvr_graph in create_rvr_graph(
                self.test_name,
                context.get_current_context().get_full_output_path(),
                graph_data):
            self.rvr_graph_summary.append(rvr_graph)
        write_csv_rvr_data(
            self.test_name,
            context.get_current_context().get_full_output_path(), graph_data)

    def test_rvr_11n_2g_20mhz_open_tx_ipv4(self):
        ssid = rand_ascii_str(20)
        setup_ap(access_point=self.access_point,
                 profile_name='whirlwind',
                 channel=hostapd_constants.AP_DEFAULT_CHANNEL_2G,
                 ssid=ssid,
                 setup_bridge=True)
        graph_data = self.run_rvr(ssid,
                                  band='2g',
                                  traffic_dir='tx',
                                  ip_version=4)
        for rvr_graph in create_rvr_graph(
                self.test_name,
                context.get_current_context().get_full_output_path(),
                graph_data):
            self.rvr_graph_summary.append(rvr_graph)
        write_csv_rvr_data(
            self.test_name,
            context.get_current_context().get_full_output_path(), graph_data)

    def test_rvr_11n_2g_20mhz_open_rx_ipv4(self):
        ssid = rand_ascii_str(20)
        setup_ap(access_point=self.access_point,
                 profile_name='whirlwind',
                 channel=hostapd_constants.AP_DEFAULT_CHANNEL_2G,
                 ssid=ssid,
                 setup_bridge=True)
        graph_data = self.run_rvr(ssid,
                                  band='2g',
                                  traffic_dir='rx',
                                  ip_version=4)
        for rvr_graph in create_rvr_graph(
                self.test_name,
                context.get_current_context().get_full_output_path(),
                graph_data):
            self.rvr_graph_summary.append(rvr_graph)
        write_csv_rvr_data(
            self.test_name,
            context.get_current_context().get_full_output_path(), graph_data)

    def test_rvr_11n_2g_20mhz_open_tx_ipv6(self):
        ssid = rand_ascii_str(20)
        setup_ap(access_point=self.access_point,
                 profile_name='whirlwind',
                 channel=hostapd_constants.AP_DEFAULT_CHANNEL_2G,
                 ssid=ssid,
                 setup_bridge=True)
        graph_data = self.run_rvr(ssid,
                                  band='2g',
                                  traffic_dir='tx',
                                  ip_version=6)
        for rvr_graph in create_rvr_graph(
                self.test_name,
                context.get_current_context().get_full_output_path(),
                graph_data):
            self.rvr_graph_summary.append(rvr_graph)
        write_csv_rvr_data(
            self.test_name,
            context.get_current_context().get_full_output_path(), graph_data)

    def test_rvr_11n_2g_20mhz_open_rx_ipv6(self):
        ssid = rand_ascii_str(20)
        setup_ap(access_point=self.access_point,
                 profile_name='whirlwind',
                 channel=hostapd_constants.AP_DEFAULT_CHANNEL_2G,
                 ssid=ssid,
                 setup_bridge=True)
        graph_data = self.run_rvr(ssid,
                                  band='2g',
                                  traffic_dir='rx',
                                  ip_version=6)
        for rvr_graph in create_rvr_graph(
                self.test_name,
                context.get_current_context().get_full_output_path(),
                graph_data):
            self.rvr_graph_summary.append(rvr_graph)
        write_csv_rvr_data(
            self.test_name,
            context.get_current_context().get_full_output_path(), graph_data)

    def test_rvr_11n_2g_20mhz_wpa2_tx_ipv4(self):
        ssid = rand_ascii_str(20)
        password = rand_ascii_str(20)
        security_profile = Security(security_mode='wpa2', password=password)
        setup_ap(access_point=self.access_point,
                 profile_name='whirlwind',
                 channel=hostapd_constants.AP_DEFAULT_CHANNEL_2G,
                 ssid=ssid,
                 security=security_profile,
                 setup_bridge=True)
        graph_data = self.run_rvr(ssid,
                                  password=password,
                                  band='2g',
                                  traffic_dir='tx',
                                  ip_version=4)
        for rvr_graph in create_rvr_graph(
                self.test_name,
                context.get_current_context().get_full_output_path(),
                graph_data):
            self.rvr_graph_summary.append(rvr_graph)
        write_csv_rvr_data(
            self.test_name,
            context.get_current_context().get_full_output_path(), graph_data)

    def test_rvr_11n_2g_20mhz_wpa2_rx_ipv4(self):
        ssid = rand_ascii_str(20)
        password = rand_ascii_str(20)
        security_profile = Security(security_mode='wpa2', password=password)
        setup_ap(access_point=self.access_point,
                 profile_name='whirlwind',
                 channel=hostapd_constants.AP_DEFAULT_CHANNEL_2G,
                 ssid=ssid,
                 security=security_profile,
                 setup_bridge=True)
        graph_data = self.run_rvr(ssid,
                                  password=password,
                                  band='2g',
                                  traffic_dir='rx',
                                  ip_version=4)
        for rvr_graph in create_rvr_graph(
                self.test_name,
                context.get_current_context().get_full_output_path(),
                graph_data):
            self.rvr_graph_summary.append(rvr_graph)
        write_csv_rvr_data(
            self.test_name,
            context.get_current_context().get_full_output_path(), graph_data)

    def test_rvr_11n_2g_20mhz_wpa2_tx_ipv6(self):
        ssid = rand_ascii_str(20)
        password = rand_ascii_str(20)
        security_profile = Security(security_mode='wpa2', password=password)
        setup_ap(access_point=self.access_point,
                 profile_name='whirlwind',
                 channel=hostapd_constants.AP_DEFAULT_CHANNEL_2G,
                 ssid=ssid,
                 security=security_profile,
                 setup_bridge=True)
        graph_data = self.run_rvr(ssid,
                                  password=password,
                                  band='2g',
                                  traffic_dir='tx',
                                  ip_version=6)
        for rvr_graph in create_rvr_graph(
                self.test_name,
                context.get_current_context().get_full_output_path(),
                graph_data):
            self.rvr_graph_summary.append(rvr_graph)
        write_csv_rvr_data(
            self.test_name,
            context.get_current_context().get_full_output_path(), graph_data)

    def test_rvr_11n_2g_20mhz_wpa2_rx_ipv6(self):
        ssid = rand_ascii_str(20)
        password = rand_ascii_str(20)
        security_profile = Security(security_mode='wpa2', password=password)
        setup_ap(access_point=self.access_point,
                 profile_name='whirlwind',
                 channel=hostapd_constants.AP_DEFAULT_CHANNEL_2G,
                 ssid=ssid,
                 security=security_profile,
                 setup_bridge=True)
        graph_data = self.run_rvr(ssid,
                                  password=password,
                                  band='2g',
                                  traffic_dir='rx',
                                  ip_version=6)
        for rvr_graph in create_rvr_graph(
                self.test_name,
                context.get_current_context().get_full_output_path(),
                graph_data):
            self.rvr_graph_summary.append(rvr_graph)
        write_csv_rvr_data(
            self.test_name,
            context.get_current_context().get_full_output_path(), graph_data)
class WlanRebootTest(WifiBaseTest):
    """Tests wlan reconnects in different reboot scenarios.

    Testbed Requirement:
    * One ACTS compatible device (dut)
    * One Whirlwind Access Point (will also serve as iperf server)
    * One PduDevice
    """
    def __init__(self, controllers):
        WifiBaseTest.__init__(self, controllers)
        self.tests = [
            'test_soft_reboot_dut_ipv4_ipv6_2g_5g',
            'test_hard_reboot_dut_ipv4_ipv6_2g_5g',
            'test_soft_reboot_ap_ipv4_ipv6_2g_5g',
            'test_hard_reboot_ap_ipv4_ipv6_2g_5g'
        ]
        if 'reboot_stress_tests' in self.user_params:
            self.tests.append('test_reboot_stress')

    def setup_class(self):
        super().setup_class()

        self.android_devices = getattr(self, 'android_devices', [])
        self.fuchsia_devices = getattr(self, 'fuchsia_devices', [])

        if 'dut' in self.user_params:
            if self.user_params['dut'] == 'fuchsia_devices':
                self.dut = create_wlan_device(self.fuchsia_devices[0])
            elif self.user_params['dut'] == 'android_devices':
                self.dut = create_wlan_device(self.android_devices[0])
            else:
                raise ValueError('Invalid DUT specified in config. (%s)' %
                                 self.user_params['dut'])
        else:
            # Default is an android device, just like the other tests
            self.dut = create_wlan_device(self.android_devices[0])

        self.access_point = self.access_points[0]

        # IPerf Server is run on the AP and setup in the tests
        self.iperf_server_on_ap = None
        self.iperf_client_on_dut = self.iperf_clients[0]

        self.router_adv_daemon = None

        # Times (in seconds) to wait for DUT network connection and assigning an
        # ip address to the wlan interface.
        wlan_reboot_params = self.user_params.get('wlan_reboot_params', {})
        self.dut_network_connection_timeout = wlan_reboot_params.get(
            'dut_network_connection_timeout', DUT_NETWORK_CONNECTION_TIMEOUT)
        self.dut_ip_address_timeout = wlan_reboot_params.get(
            'dut_ip_address_timeout', DUT_IP_ADDRESS_TIMEOUT)

    def setup_test(self):
        self.access_point.stop_all_aps()
        if self.router_adv_daemon:
            self.router_adv_daemon.stop()
        self.dut.wifi_toggle_state(True)
        for ad in self.android_devices:
            ad.droid.wakeLockAcquireBright()
            ad.droid.wakeUpNow()
        for fd in self.fuchsia_devices:
            fd.wlan_policy_lib.wlanCreateClientController()
            fd.wlan_policy_lib.wlanStartClientConnections()
        self.dut.clear_saved_networks()
        self.dut.disconnect()
        self.router_adv_daemon = None
        self.ssid = utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_2G)

    def teardown_test(self):
        self.access_point.stop_all_aps()
        self.dut.clear_saved_networks()
        for fd in self.fuchsia_devices:
            fd.wlan_policy_lib.wlanStopClientConnections()
        self.dut.disconnect()
        for ad in self.android_devices:
            ad.droid.wakeLockRelease()
            ad.droid.goToSleepNow()
        self.dut.turn_location_off_and_scan_toggle_off()
        self.dut.reset_wifi()

    def on_fail(self, test_name, begin_time):
        self.dut.take_bug_report(test_name, begin_time)
        self.dut.get_log(test_name, begin_time)

    def setup_ap(self, ssid, band, ipv4=True, ipv6=False):
        """Setup ap with basic config.

        Args:
            ssid: string, ssid to setup on ap
            band: string ('2g' or '5g') of band to setup.
            ipv4: True if using ipv4 (dhcp), else False.
            ipv6: True if using ipv6 (radvd), else False.
        """
        if band == BAND_2G:
            wlan_utils.setup_ap(access_point=self.access_point,
                                profile_name='whirlwind',
                                channel=11,
                                ssid=ssid)
        elif band == BAND_5G:
            wlan_utils.setup_ap(access_point=self.access_point,
                                profile_name='whirlwind',
                                channel=36,
                                ssid=ssid)

        if not ipv4:
            self.access_point.stop_dhcp()
        if ipv6:
            radvd_config = RadvdConfig(
                prefix='fd00::/64',
                adv_send_advert=radvd_constants.ADV_SEND_ADVERT_ON,
                adv_on_link=radvd_constants.ADV_ON_LINK_ON,
                adv_autonomous=radvd_constants.ADV_AUTONOMOUS_ON)

            if band == BAND_2G:
                self.router_adv_daemon = Radvd(self.access_point.ssh,
                                               self.access_point.wlan_2g)
            elif band == BAND_5G:
                self.router_adv_daemon = Radvd(self.access_point.ssh,
                                               self.access_point.wlan_5g)
            self.router_adv_daemon.start(radvd_config)

        self.log.info('Network (SSID: %s) is up.' % ssid)

    def save_and_connect(self, ssid):
        """Associates the dut with the network running on the AP and saves
        network to device.

        Args:
            ssid: string, ssid to connect DUT to

        Raises:
            EnvironmentError, if saving network fails
            ConnectionError, if device fails to connect to network
        """
        self.dut.save_network(self.ssid)
        self.dut.associate(self.ssid)

    def setup_save_and_connect_to_network(self,
                                          ssid,
                                          band,
                                          ipv4=True,
                                          ipv6=False):
        """Setup ap with passed params, saves network, and connects the dut with
        the network running on the AP and saves network.

        Args:
            ssid: string, ssid to setup and connect to
            band: string ('2g' or '5g') of band to setup.
            ipv4: True if using ipv4 (dhcp), else False.
            ipv6: True if using ipv6 (radvd), else False.
        """
        self.setup_ap(ssid, band, ipv4, ipv6)
        self.save_and_connect(ssid)

    def wait_until_dut_gets_ipv4_addr(self, interface):
        """Checks if device has an ipv4 private address. Sleeps 1 second between
        retries.

        Args:
            interface: string, name of interface from which to get ipv4 address.

        Raises:
            ConnectionError, if DUT does not have an ipv4 address after all
            timeout.
        """
        self.log.info(
            'Checking if DUT has received an ipv4 addr. Will retry for %s '
            'seconds.' % self.dut_ip_address_timeout)
        timeout = time.time() + self.dut_ip_address_timeout
        while time.time() < timeout:
            ip_addrs = self.dut.get_interface_ip_addresses(interface)

            if len(ip_addrs['ipv4_private']) > 0:
                self.log.info('DUT has an ipv4 address: %s' %
                              ip_addrs['ipv4_private'][0])
                break
            else:
                self.log.debug(
                    'DUT does not yet have an ipv4 address...retrying in 1 '
                    'second.')
                time.sleep(1)
        else:
            raise ConnectionError('DUT failed to get an ipv4 address.')

    def wait_until_dut_gets_ipv6_addr(self, interface):
        """Checks if device has an ipv6 private local address. Sleeps 1 second
        between retries.

        Args:
            interface: string, name of interface from which to get ipv6 address.

        Raises:
            ConnectionError, if DUT does not have an ipv6 address after all
            timeout.
        """
        self.log.info(
            'Checking if DUT has received an ipv6 addr. Will retry for %s '
            'seconds.' % self.dut_ip_address_timeout)
        timeout = time.time() + self.dut_ip_address_timeout
        while time.time() < timeout:
            ip_addrs = self.dut.get_interface_ip_addresses(interface)
            if len(ip_addrs['ipv6_private_local']) > 0:
                self.log.info('DUT has an ipv6 private local address: %s' %
                              ip_addrs['ipv6_private_local'][0])
                break
            else:
                self.log.debug(
                    'DUT does not yet have an ipv6 address...retrying in 1 '
                    'second.')
                time.sleep(1)
        else:
            raise ConnectionError('DUT failed to get an ipv6 address.')

    def setup_iperf_server_on_ap(self, band):
        """Configures iperf server based on the tests band.

        Args:
            band: string ('2g' or '5g') of band to setup.
        """
        if band == BAND_2G:
            return iperf_server.IPerfServerOverSsh(
                self.user_params['AccessPoint'][0]['ssh_config'],
                5201,
                test_interface=self.access_point.wlan_2g)
        elif band == BAND_5G:
            return iperf_server.IPerfServerOverSsh(
                self.user_params['AccessPoint'][0]['ssh_config'],
                5201,
                test_interface=self.access_point.wlan_5g)

    def get_iperf_server_address(self, iperf_server_on_ap, ip_version):
        """Retrieves the ip address of the iperf server.

        Args:
            iperf_server_on_ap: IPerfServer object, linked to AP
            ip_version: string, the ip version (ipv4 or ipv6)

        Returns:
            String, the ip address of the iperf_server
        """
        iperf_server_addresses = iperf_server_on_ap.get_interface_ip_addresses(
            iperf_server_on_ap.test_interface)
        if ip_version == IPV4:
            iperf_server_ip_address = (
                iperf_server_addresses['ipv4_private'][0])
        elif ip_version == IPV6:
            if iperf_server_addresses['ipv6_private_local']:
                iperf_server_ip_address = (
                    iperf_server_addresses['ipv6_private_local'][0])
            else:
                iperf_server_ip_address = (
                    '%s%%%s' % (iperf_server_addresses['ipv6_link_local'][0],
                                self.iperf_client_on_dut.test_interface))
        else:
            raise ValueError('Invalid IP version: %s' % ip_version)

        return iperf_server_ip_address

    def verify_traffic_between_dut_and_ap(self,
                                          iperf_server_on_ap,
                                          iperf_client_on_dut,
                                          ip_version=IPV4):
        """Runs IPerf traffic from the iperf client (dut) and the iperf
        server (and vice versa) and verifies traffic was able to pass
        successfully.

        Args:
            iperf_server_on_ap: IPerfServer object, linked to AP
            iperf_client_on_dut: IPerfClient object, linked to DUT
            ip_version: string, the ip version (ipv4 or ipv6)

        Raises:
            ValueError, if invalid ip_version is passed.
            ConnectionError, if traffic is not passed successfully in both
                directions.
        """
        dut_ip_addresses = self.dut.get_interface_ip_addresses(
            iperf_client_on_dut.test_interface)

        iperf_server_ip_address = self.get_iperf_server_address(
            iperf_server_on_ap, ip_version)

        self.log.info(
            'Attempting to pass traffic from DUT to IPerf server (%s).' %
            iperf_server_ip_address)
        tx_file = iperf_client_on_dut.start(iperf_server_ip_address,
                                            '-i 1 -t 3 -J', 'reboot_tx')
        tx_results = iperf_server.IPerfResult(tx_file)
        if not tx_results.avg_receive_rate or tx_results.avg_receive_rate == 0:
            raise ConnectionError(
                'Failed to pass IPerf traffic from DUT to server (%s). TX '
                'Average Receive Rate: %s' %
                (iperf_server_ip_address, tx_results.avg_receive_rate))
        else:
            self.log.info(
                'Success: Traffic passed from DUT to IPerf server (%s).' %
                iperf_server_ip_address)
        self.log.info(
            'Attempting to pass traffic from IPerf server (%s) to DUT.' %
            iperf_server_ip_address)
        rx_file = iperf_client_on_dut.start(iperf_server_ip_address,
                                            '-i 1 -t 3 -R -J', 'reboot_rx')
        rx_results = iperf_server.IPerfResult(rx_file)
        if not rx_results.avg_receive_rate or rx_results.avg_receive_rate == 0:
            raise ConnectionError(
                'Failed to pass IPerf traffic from server (%s) to DUT. RX '
                'Average Receive Rate: %s' %
                (iperf_server_ip_address, rx_results.avg_receive_rate))
        else:
            self.log.info(
                'Success: Traffic passed from IPerf server (%s) to DUT.' %
                iperf_server_ip_address)

    def start_dut_ping_process(self, iperf_server_on_ap, ip_version=IPV4):
        """Creates a  process that pings the AP from the DUT.

        Runs in parallel for 15 seconds, so it can be interrupted by a reboot.
        Sleeps for a few seconds to ensure pings have started.

        Args:
            iperf_server_on_ap: IPerfServer object, linked to AP
            ip_version: string, the ip version (ipv4 or ipv6)
        """
        ap_address = self.get_iperf_server_address(iperf_server_on_ap,
                                                   ip_version)
        if ap_address:
            self.log.info(
                'Starting ping process to %s in parallel. Logs from this '
                'process will be suppressed, since it will be intentionally '
                'interrupted.' % ap_address)
            ping_proc = Process(target=self.dut.ping,
                                args=[ap_address],
                                kwargs={'count': 15})
            with utils.SuppressLogOutput():
                ping_proc.start()
            # Allow for a few seconds of pinging before allowing it to be
            # interrupted.
            time.sleep(3)
        else:
            raise ConnectionError('Failed to retrieve APs iperf address.')

    def prepare_dut_for_reconnection(self):
        """Perform any actions to ready DUT for reconnection.

        These actions will vary depending on the DUT. eg. android devices may
        need to be woken up, ambient devices should not require any interaction,
        etc.
        """
        self.dut.wifi_toggle_state(True)
        for ad in self.android_devices:
            ad.droid.wakeUpNow()
        for fd in self.fuchsia_devices:
            fd.wlan_policy_lib.wlanCreateClientController()

    def wait_for_dut_network_connection(self, ssid):
        """Checks if device is connected to given network. Sleeps 1 second
        between retries.

        Args:
            ssid: string of ssid
        Raises:
            ConnectionError, if DUT is not connected after all timeout.
        """
        self.log.info(
            'Checking if DUT is connected to %s network. Will retry for %s '
            'seconds.' % (ssid, self.dut_network_connection_timeout))
        timeout = time.time() + self.dut_network_connection_timeout
        while time.time() < timeout:
            try:
                is_connected = self.dut.is_connected(ssid=ssid)
            except Exception as err:
                self.log.debug('SL4* call failed. Retrying in 1 second.')
                is_connected = False
            finally:
                if is_connected:
                    self.log.info('Success: DUT has connected.')
                    break
                else:
                    self.log.debug(
                        'DUT not connected to network %s...retrying in 1 second.'
                        % ssid)
                    time.sleep(1)
        else:
            raise ConnectionError('DUT failed to connect to the network.')

    def write_csv_time_to_reconnect(self, test_name, time_to_reconnect):
        """Writes the time to reconnect to a csv file.
        Args:
            test_name: the name of the test case
            time_to_reconnect: the time from when the rebooted device came back
                up to when it reassociated (or 'FAIL'), if it failed to
                reconnect.
        """
        log_context = context.get_current_context()
        log_path = os.path.join(log_context.get_base_output_path(),
                                'WlanRebootTest/')
        csv_file_name = '%stime_to_reconnect.csv' % log_path
        self.log.info('Writing to %s' % csv_file_name)
        with open(csv_file_name, 'a') as csv_file:
            csv_file.write('%s,%s\n' % (test_name, time_to_reconnect))

    def log_and_continue(self, run, time_to_reconnect=None, error=None):
        """Writes the time to reconnect to the csv file before continuing, used
        in stress tests runs.

        Args:
            time_to_reconnect: the time from when the rebooted device came back
                ip to when reassociation occurred.
            run: the run number in a looped stress tested.,
            error: string, error message to log before continuing with the test
        """
        if error:
            self.log.info(
                'Device failed to reconnect to network %s on run %s. Error: %s'
                % (self.ssid, run, error))
            self.write_csv_time_to_reconnect(
                '%s_run_%s' % (self.test_name, run), 'FAIL')

        else:
            self.log.info(
                'Device successfully reconnected to network %s after %s seconds'
                ' on run %s.' % (self.ssid, time_to_reconnect, run))
            self.write_csv_time_to_reconnect(
                '%s_run_%s' % (self.test_name, run), time_to_reconnect)

    def run_reboot_test(self, settings):
        """Runs a reboot test based on a given config.
            1. Setups up a network, associates the dut, and saves the network.
            2. Verifies the dut receives ip address(es).
            3. Verifies traffic between DUT and AP (IPerf client and server).
            4. Reboots (hard or soft) the device (dut or ap).
                - If the ap was rebooted, setup the same network again.
            5. Wait for reassociation or timeout.
            6. If reassocation occurs:
                - Verifies the dut receives ip address(es).
                - Verifies traffic between DUT and AP (IPerf client and server).
            7. Logs time to reconnect (or failure to reconnect)
            8. If stress testing, repeats steps 4 - 7 for N loops.

        Args:
            settings: dictionary containing the following values:
                reboot_device: string ('dut' or 'ap') of the device to reboot.
                reboot_type: string ('soft' or 'hard') of how to reboot the
                    reboot_device.
                band: string ('2g' or '5g') of band to setup.
                ipv4: True if using ipv4 (dhcp), else False.
                ipv6: True if using ipv6 (radvd), else False.

                Optional:
                    interrupt: if True, the DUT will be pinging the AP in a
                        parallel process when the reboot occurs. This is used to
                        compare reconnect times when idle to active.
                    test_name: name of the test, used when stress testing.
                    loops: number of times to perform test, used when stress
                        testing.

        Raises:
            ValueError, if ipv4 and ipv6 are both False
            ValueError, if band is not '2g' or '5g'
            ValueError, if reboot_device is not 'dut' or 'ap'
            ValueError, if reboot_type is not 'soft' or 'hard'

        """
        loops = settings.get('loops', 1)
        passed_count = 0
        ipv4 = settings['ipv4']
        ipv6 = settings['ipv6']
        reboot_device = settings['reboot_device']
        reboot_type = settings['reboot_type']
        band = settings['band']
        interrupt = settings.get('interrupt', None)

        # Validate test settings.
        if not ipv4 and not ipv6:
            raise ValueError('Either ipv4, ipv6, or both must be True.')
        if reboot_device != DUT and reboot_device != AP:
            raise ValueError('Invalid reboot device: %s' % reboot_device)
        if reboot_type != SOFT and reboot_type != HARD:
            raise ValueError('Invalid reboot type: %s' % reboot_type)
        if band != BAND_2G and band != BAND_5G:
            raise ValueError('Invalid band: %s' % band)

        self.setup_save_and_connect_to_network(self.ssid,
                                               band,
                                               ipv4=ipv4,
                                               ipv6=ipv6)
        self.wait_for_dut_network_connection(self.ssid)

        dut_test_interface = self.iperf_client_on_dut.test_interface
        if ipv4:
            self.wait_until_dut_gets_ipv4_addr(dut_test_interface)
        if ipv6:
            self.wait_until_dut_gets_ipv6_addr(dut_test_interface)

        self.iperf_server_on_ap = self.setup_iperf_server_on_ap(band)
        self.iperf_server_on_ap.start()

        if ipv4:
            self.verify_traffic_between_dut_and_ap(self.iperf_server_on_ap,
                                                   self.iperf_client_on_dut)
        if ipv6:
            self.verify_traffic_between_dut_and_ap(self.iperf_server_on_ap,
                                                   self.iperf_client_on_dut,
                                                   ip_version=IPV6)

        # Looping reboots for stress testing
        for run in range(loops):
            run += 1
            self.log.info('Starting run %s of %s.' % (run, loops))

            # Ping from DUT to AP during AP reboot
            if interrupt:
                if ipv4:
                    self.start_dut_ping_process(self.iperf_server_on_ap)
                if ipv6:
                    self.start_dut_ping_process(self.iperf_server_on_ap,
                                                ip_version=IPV6)

            # DUT reboots
            if reboot_device == DUT:
                if type(self.iperf_client_on_dut
                        ) == iperf_client.IPerfClientOverSsh:
                    self.iperf_client_on_dut.close_ssh()
                if reboot_type == SOFT:
                    self.dut.device.reboot()
                elif reboot_type == HARD:
                    self.dut.hard_power_cycle(self.pdu_devices)

            # AP reboots
            elif reboot_device == AP:
                if reboot_type == SOFT:
                    self.log.info('Cleanly stopping ap.')
                    self.access_point.stop_all_aps()
                elif reboot_type == HARD:
                    self.iperf_server_on_ap.close_ssh()
                    self.access_point.hard_power_cycle(self.pdu_devices)
                self.setup_ap(self.ssid, band, ipv4=ipv4, ipv6=ipv6)

            self.prepare_dut_for_reconnection()
            uptime = time.time()
            try:
                self.wait_for_dut_network_connection(self.ssid)
                time_to_reconnect = time.time() - uptime
                if ipv4:
                    self.wait_until_dut_gets_ipv4_addr(dut_test_interface)
                if ipv6:
                    self.wait_until_dut_gets_ipv6_addr(dut_test_interface)
                self.iperf_server_on_ap.start()

                if ipv4:
                    self.verify_traffic_between_dut_and_ap(
                        self.iperf_server_on_ap, self.iperf_client_on_dut)
                if ipv6:
                    self.verify_traffic_between_dut_and_ap(
                        self.iperf_server_on_ap,
                        self.iperf_client_on_dut,
                        ip_version=IPV6)

            except ConnectionError as err:
                self.log_and_continue(run, error=err)
            else:
                passed_count += 1
                self.log_and_continue(run, time_to_reconnect=time_to_reconnect)

        if passed_count == loops:
            asserts.explicit_pass(
                'Test Summary: device successfully reconnected to network %s '
                '%s/%s times.' % (self.ssid, passed_count, loops))

        else:
            asserts.fail(
                'Test Summary: device failed reconnection test. Reconnected to '
                'network %s %s/%s times.' % (self.ssid, passed_count, loops))

    # 12 test cases
    def test_soft_reboot_ap_ipv4_ipv6_2g_5g(self):
        test_list = []
        for combination in itertools.product(IP_VERSIONS, BANDS, INTERRUPTS):
            test_settings = {
                'reboot_device': AP,
                'reboot_type': SOFT,
                'ipv4': combination[0][IPV4],
                'ipv6': combination[0][IPV6],
                'band': combination[1],
                'interrupt': combination[2]
            }
            test_list.append(test_settings)

        self.run_generated_testcases(self.run_reboot_test,
                                     settings=test_list,
                                     name_func=get_test_name)

    # 12 test cases
    def test_hard_reboot_ap_ipv4_ipv6_2g_5g(self):
        test_list = []
        for combination in itertools.product(IP_VERSIONS, BANDS, INTERRUPTS):
            test_settings = {
                'reboot_device': AP,
                'reboot_type': HARD,
                'ipv4': combination[0][IPV4],
                'ipv6': combination[0][IPV6],
                'band': combination[1],
                'interrupt': combination[2]
            }
            test_list.append(test_settings)

        self.run_generated_testcases(self.run_reboot_test,
                                     settings=test_list,
                                     name_func=get_test_name)

    # 6 test cases
    def test_soft_reboot_dut_ipv4_ipv6_2g_5g(self):
        test_list = []
        for combination in itertools.product(IP_VERSIONS, BANDS):
            test_settings = {
                'reboot_device': DUT,
                'reboot_type': SOFT,
                'ipv4': combination[0][IPV4],
                'ipv6': combination[0][IPV6],
                'band': combination[1]
            }
            test_list.append(test_settings)

        self.run_generated_testcases(self.run_reboot_test,
                                     settings=test_list,
                                     name_func=get_test_name)

    # 6 test cases
    def test_hard_reboot_dut_ipv4_ipv6_2g_5g(self):
        # Note: This may need to be removed if non-battery android devices
        # are added.
        asserts.skip_if(self.user_params['dut'] == 'android_devices',
                        'No hard reboots for android battery devices.')
        test_list = []
        for combination in itertools.product(IP_VERSIONS, BANDS):
            test_settings = {
                'reboot_device': DUT,
                'reboot_type': HARD,
                'ipv4': combination[0][IPV4],
                'ipv6': combination[0][IPV6],
                'band': combination[1]
            }
            test_list.append(test_settings)

        self.run_generated_testcases(self.run_reboot_test,
                                     settings=test_list,
                                     name_func=get_test_name)

    def test_reboot_stress(self):
        """Creates reboot test(s) and runs it repeatedly. Setup in config file.
        Eg.
            'reboot_stress_tests': ['test_soft_reboot_ap_ipv4_2g_loop_10]
                will run a a soft reboot ap test, using ipv4 on 2g, 10 times
                repeatedly. Time_to_reconnect logs occur after each run and will
                write to the same csv file with '<test_name>_run_N' as the tests
                name.
        """
        pattern = re.compile(
            r'.*?(hard|soft)_reboot_(dut|ap)_(interrupt_)?(ipv4_ipv6|ipv4|ipv6)_(2g|5g)(_loops?_([0-9]*))?',
            re.IGNORECASE)
        test_list = []
        for test_name in self.user_params['reboot_stress_tests']:
            test_match = re.match(pattern, test_name)
            if test_match:
                reboot_type = test_match.group(1)
                reboot_device = test_match.group(2)
                interrupt = True if test_match.group(3) else False
                ip_version = test_match.group(4)
                ipv4 = 'ipv4' in ip_version
                ipv6 = 'ipv6' in ip_version
                band = test_match.group(5)
                if test_match.group(6):
                    loops = test_match.group(7)
                else:
                    loops = 1
                settings = {
                    'test_name': test_name,
                    'reboot_type': reboot_type,
                    'reboot_device': reboot_device,
                    'band': band,
                    'ipv4': ipv4,
                    'ipv6': ipv6,
                    'interrupt': interrupt,
                    'loops': int(loops)
                }
                test_list.append(settings)
            else:
                self.log.info('Invalid test name: %s. Ignoring.' % test_name)
        self.run_generated_testcases(self.run_reboot_test,
                                     settings=test_list,
                                     name_func=get_test_name)