1# Copyright (c) 2011 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import datetime 6import collections 7import logging 8import os 9import random 10import time 11 12from autotest_lib.client.common_lib import error 13from autotest_lib.client.common_lib.cros import path_utils 14from autotest_lib.client.common_lib.cros import virtual_ethernet_pair 15from autotest_lib.client.common_lib.cros.network import interface 16from autotest_lib.client.common_lib.cros.network import iw_runner 17from autotest_lib.client.common_lib.cros.network import ping_runner 18from autotest_lib.server.cros.network import packet_capturer 19 20NetDev = collections.namedtuple('NetDev', 21 ['inherited', 'phy', 'if_name', 'if_type']) 22 23class LinuxSystem(object): 24 """Superclass for test machines running Linux. 25 26 Provides a common point for routines that use the cfg80211 userspace tools 27 to manipulate the wireless stack, regardless of the role they play. 28 Currently the commands shared are the init, which queries for wireless 29 devices, along with start_capture and stop_capture. More commands may 30 migrate from site_linux_router as appropriate to share. 31 32 """ 33 34 CAPABILITY_5GHZ = '5ghz' 35 CAPABILITY_MULTI_AP = 'multi_ap' 36 CAPABILITY_MULTI_AP_SAME_BAND = 'multi_ap_same_band' 37 CAPABILITY_IBSS = 'ibss_supported' 38 CAPABILITY_SEND_MANAGEMENT_FRAME = 'send_management_frame' 39 CAPABILITY_TDLS = 'tdls' 40 CAPABILITY_VHT = 'vht' 41 CAPABILITY_SME = 'sme' 42 CAPABILITY_SUPPLICANT_ROAMING = "supplicant_roaming" 43 BRIDGE_INTERFACE_NAME = 'br0' 44 HOSTAP_BRIDGE_INTERFACE_PREFIX = 'hostapbr' 45 MIN_SPATIAL_STREAMS = 2 46 MAC_BIT_LOCAL = 0x2 # Locally administered. 47 MAC_BIT_MULTICAST = 0x1 48 MAC_RETRY_LIMIT = 1000 49 50 51 @property 52 def capabilities(self): 53 """@return iterable object of AP capabilities for this system.""" 54 if self._capabilities is None: 55 self._capabilities = self.get_capabilities() 56 logging.info('%s system capabilities: %r', 57 self.role, self._capabilities) 58 return self._capabilities 59 60 61 @property 62 def board(self): 63 """@return string self reported board of this device.""" 64 if self._board is None: 65 # Remove 'board:' prefix. 66 self._board = self.host.get_board().split(':')[1] 67 return self._board 68 69 70 def __init__(self, host, role, inherit_interfaces=False): 71 self.host = host 72 self.role = role 73 self.inherit_interfaces = inherit_interfaces 74 self.__setup() 75 76 77 def __setup(self): 78 """Set up this system. 79 80 Can be used either to complete initialization of a LinuxSystem object, 81 or to re-establish a good state after a reboot. 82 83 """ 84 # Command locations. 85 cmd_iw = path_utils.must_be_installed('/usr/sbin/iw', host=self.host) 86 self.cmd_ip = path_utils.must_be_installed('/usr/sbin/ip', 87 host=self.host) 88 self.cmd_readlink = '%s -l' % path_utils.must_be_installed( 89 '/bin/ls', host=self.host) 90 91 self._packet_capturer = packet_capturer.get_packet_capturer( 92 self.host, host_description=self.role, cmd_ip=self.cmd_ip, 93 cmd_iw=cmd_iw, ignore_failures=True) 94 self.iw_runner = iw_runner.IwRunner(remote_host=self.host, 95 command_iw=cmd_iw) 96 97 self._phy_list = None 98 self.phys_for_frequency, self.phy_bus_type = self._get_phy_info() 99 logging.debug('Current regulatory domain %r', 100 self.iw_runner.get_regulatory_domain()) 101 self._interfaces = [] 102 self._brif_index = 0 103 for interface in self.iw_runner.list_interfaces(): 104 if self.inherit_interfaces: 105 self._interfaces.append(NetDev(inherited=True, 106 if_name=interface.if_name, 107 if_type=interface.if_type, 108 phy=interface.phy)) 109 else: 110 self.iw_runner.remove_interface(interface.if_name) 111 112 self._wlanifs_in_use = [] 113 self._local_macs_in_use = set() 114 self._capture_interface = None 115 self._board = None 116 # Some uses of LinuxSystem don't use the interface allocation facility. 117 # Don't force us to remove all the existing interfaces if this facility 118 # is not desired. 119 self._wlanifs_initialized = False 120 self._capabilities = None 121 self._ping_runner = ping_runner.PingRunner(host=self.host) 122 self._bridge_interface = None 123 self._virtual_ethernet_pair = None 124 125 126 @property 127 def phy_list(self): 128 """@return iterable object of PHY descriptions for this system.""" 129 if self._phy_list is None: 130 self._phy_list = self.iw_runner.list_phys() 131 return self._phy_list 132 133 134 def _phy_by_name(self, phy_name): 135 """@return IwPhy for PHY with name |phy_name|, or None.""" 136 for phy in self._phy_list: 137 if phy.name == phy_name: 138 return phy 139 else: 140 return None 141 142 143 def _get_phy_info(self): 144 """Get information about WiFi devices. 145 146 Parse the output of 'iw list' and some of sysfs and return: 147 148 A dict |phys_for_frequency| which maps from each frequency to a 149 list of phys that support that channel. 150 151 A dict |phy_bus_type| which maps from each phy to the bus type for 152 each phy. 153 154 @return phys_for_frequency, phy_bus_type tuple as described. 155 156 """ 157 phys_for_frequency = {} 158 phy_caps = {} 159 phy_list = [] 160 for phy in self.phy_list: 161 phy_list.append(phy.name) 162 for band in phy.bands: 163 for mhz in band.frequencies: 164 if mhz not in phys_for_frequency: 165 phys_for_frequency[mhz] = [phy.name] 166 else: 167 phys_for_frequency[mhz].append(phy.name) 168 169 phy_bus_type = {} 170 for phy in phy_list: 171 phybus = 'unknown' 172 command = '%s /sys/class/ieee80211/%s' % (self.cmd_readlink, phy) 173 devpath = self.host.run(command).stdout 174 if '/usb' in devpath: 175 phybus = 'usb' 176 elif '/mmc' in devpath: 177 phybus = 'sdio' 178 elif '/pci' in devpath: 179 phybus = 'pci' 180 phy_bus_type[phy] = phybus 181 logging.debug('Got phys for frequency: %r', phys_for_frequency) 182 return phys_for_frequency, phy_bus_type 183 184 185 def _create_bridge_interface(self): 186 """Create a bridge interface.""" 187 self.host.run('%s link add name %s type bridge' % 188 (self.cmd_ip, self.BRIDGE_INTERFACE_NAME)) 189 self.host.run('%s link set dev %s up' % 190 (self.cmd_ip, self.BRIDGE_INTERFACE_NAME)) 191 self._bridge_interface = self.BRIDGE_INTERFACE_NAME 192 193 194 def _create_virtual_ethernet_pair(self): 195 """Create a virtual ethernet pair.""" 196 self._virtual_ethernet_pair = virtual_ethernet_pair.VirtualEthernetPair( 197 interface_ip=None, peer_interface_ip=None, host=self.host) 198 self._virtual_ethernet_pair.setup() 199 200 201 def _get_unique_mac(self): 202 """Get a MAC address that is likely to be unique. 203 204 Generates a MAC address that is a) guaranteed not to be in use 205 on this host, and b) likely to be unique within the test cell. 206 207 @return string MAC address. 208 209 """ 210 # We use SystemRandom to reduce the likelyhood of coupling 211 # across systems. (The default random class might, e.g., seed 212 # itself based on wall-clock time.) 213 sysrand = random.SystemRandom() 214 for tries in xrange(0, self.MAC_RETRY_LIMIT): 215 mac_addr = '%02x:%02x:%02x:%02x:%02x:%02x' % ( 216 (sysrand.getrandbits(8) & ~self.MAC_BIT_MULTICAST) | 217 self.MAC_BIT_LOCAL, 218 sysrand.getrandbits(8), 219 sysrand.getrandbits(8), 220 sysrand.getrandbits(8), 221 sysrand.getrandbits(8), 222 sysrand.getrandbits(8)) 223 if mac_addr not in self._local_macs_in_use: 224 self._local_macs_in_use.add(mac_addr) 225 return mac_addr 226 else: 227 raise error.TestError('Failed to find a new MAC address') 228 229 230 def _phy_in_use(self, phy_name): 231 """Determine whether or not a PHY is used by an active DEV 232 233 @return bool True iff PHY is in use. 234 """ 235 for net_dev in self._wlanifs_in_use: 236 if net_dev.phy == phy_name: 237 return True 238 return False 239 240 241 def remove_interface(self, interface): 242 """Remove an interface from a WiFi device. 243 244 @param interface string interface to remove (e.g. wlan0). 245 246 """ 247 self.release_interface(interface) 248 self.host.run('%s link set %s down' % (self.cmd_ip, interface)) 249 self.iw_runner.remove_interface(interface) 250 for net_dev in self._interfaces: 251 if net_dev.if_name == interface: 252 self._interfaces.remove(net_dev) 253 break 254 255 256 def close(self): 257 """Close global resources held by this system.""" 258 logging.debug('Cleaning up host object for %s', self.role) 259 self._packet_capturer.close() 260 # Release and remove any interfaces that we create. 261 for net_dev in self._wlanifs_in_use: 262 self.release_interface(net_dev.if_name) 263 for net_dev in self._interfaces: 264 if net_dev.inherited: 265 continue 266 self.remove_interface(net_dev.if_name) 267 if self._bridge_interface is not None: 268 self.remove_bridge_interface() 269 if self._virtual_ethernet_pair is not None: 270 self.remove_ethernet_pair_interface() 271 self.host.close() 272 self.host = None 273 274 275 def reboot(self, timeout): 276 """Reboot this system, and restore it to a known-good state. 277 278 @param timeout Maximum seconds to wait for system to return. 279 280 """ 281 self.host.reboot(timeout=timeout, wait=True) 282 self.__setup() 283 284 285 def get_capabilities(self): 286 caps = set() 287 phymap = self.phys_for_frequency 288 if [freq for freq in phymap.iterkeys() if freq > 5000]: 289 # The frequencies are expressed in megaherz 290 caps.add(self.CAPABILITY_5GHZ) 291 if [freq for freq in phymap.iterkeys() if len(phymap[freq]) > 1]: 292 caps.add(self.CAPABILITY_MULTI_AP_SAME_BAND) 293 caps.add(self.CAPABILITY_MULTI_AP) 294 elif len(self.phy_bus_type) > 1: 295 caps.add(self.CAPABILITY_MULTI_AP) 296 for phy in self.phy_list: 297 if ('tdls_mgmt' in phy.commands or 298 'tdls_oper' in phy.commands or 299 'T-DLS' in phy.features): 300 caps.add(self.CAPABILITY_TDLS) 301 if 'authenticate' in phy.commands: 302 caps.add(self.CAPABILITY_SME) 303 if phy.support_vht: 304 caps.add(self.CAPABILITY_VHT) 305 if 'roaming' not in phy.features: 306 caps.add(self.CAPABILITY_SUPPLICANT_ROAMING) 307 if any([iw_runner.DEV_MODE_IBSS in phy.modes 308 for phy in self.phy_list]): 309 caps.add(self.CAPABILITY_IBSS) 310 return caps 311 312 313 def start_capture(self, frequency, 314 ht_type=None, snaplen=None, filename=None): 315 """Start a packet capture. 316 317 @param frequency int frequency of channel to capture on. 318 @param ht_type string one of (None, 'HT20', 'HT40+', 'HT40-'). 319 @param snaplen int number of bytes to retain per capture frame. 320 @param filename string filename to write capture to. 321 322 """ 323 if self._packet_capturer.capture_running: 324 self.stop_capture() 325 self._capture_interface = self.get_wlanif(frequency, 'monitor') 326 full_interface = [net_dev for net_dev in self._interfaces 327 if net_dev.if_name == self._capture_interface][0] 328 # If this is the only interface on this phy, we ought to configure 329 # the phy with a channel and ht_type. Otherwise, inherit the settings 330 # of the phy as they stand. 331 if len([net_dev for net_dev in self._interfaces 332 if net_dev.phy == full_interface.phy]) == 1: 333 self._packet_capturer.configure_raw_monitor( 334 self._capture_interface, frequency, ht_type=ht_type) 335 else: 336 self.host.run('%s link set %s up' % 337 (self.cmd_ip, self._capture_interface)) 338 339 # Start the capture. 340 if filename: 341 remote_path = os.path.join('/tmp', os.path.basename(filename)) 342 else: 343 remote_path = None 344 self._packet_capturer.start_capture( 345 self._capture_interface, './debug/', snaplen=snaplen, 346 remote_file=remote_path) 347 348 349 def stop_capture(self, save_dir=None, save_filename=None): 350 """Stop a packet capture. 351 352 @param save_dir string path to directory to save pcap files in. 353 @param save_filename string basename of file to save pcap in locally. 354 355 """ 356 if not self._packet_capturer.capture_running: 357 return 358 results = self._packet_capturer.stop_capture( 359 local_save_dir=save_dir, local_pcap_filename=save_filename) 360 self.release_interface(self._capture_interface) 361 self._capture_interface = None 362 return results 363 364 365 def sync_host_times(self): 366 """Set time on our DUT to match local time.""" 367 epoch_seconds = time.time() 368 busybox_format = '%Y%m%d%H%M.%S' 369 busybox_date = datetime.datetime.utcnow().strftime(busybox_format) 370 self.host.run('date -u --set=@%s 2>/dev/null || date -u %s' % 371 (epoch_seconds, busybox_date)) 372 373 374 def _get_phy_for_frequency(self, frequency, phytype, spatial_streams): 375 """Get a phy appropriate for a frequency and phytype. 376 377 Return the most appropriate phy interface for operating on the 378 frequency |frequency| in the role indicated by |phytype|. Prefer idle 379 phys to busy phys if any exist. Secondarily, show affinity for phys 380 that use the bus type associated with this phy type. 381 382 @param frequency int WiFi frequency of phy. 383 @param phytype string key of phytype registered at construction time. 384 @param spatial_streams int number of spatial streams required. 385 @return string name of phy to use. 386 387 """ 388 phy_objs = [] 389 for phy_name in self.phys_for_frequency[frequency]: 390 phy_obj = self._phy_by_name(phy_name) 391 num_antennas = min(phy_obj.avail_rx_antennas, 392 phy_obj.avail_tx_antennas) 393 if num_antennas >= spatial_streams: 394 phy_objs.append(phy_obj) 395 elif num_antennas == 0: 396 logging.warning( 397 'Allowing use of %s, which reports zero antennas', phy_name) 398 phy_objs.append(phy_obj) 399 else: 400 logging.debug( 401 'Filtering out %s, which reports only %d antennas', 402 phy_name, num_antennas) 403 404 busy_phys = set(net_dev.phy for net_dev in self._wlanifs_in_use) 405 idle_phy_objs = [phy_obj for phy_obj in phy_objs 406 if phy_obj.name not in busy_phys] 407 phy_objs = idle_phy_objs or phy_objs 408 phy_objs.sort(key=lambda phy_obj: min(phy_obj.avail_rx_antennas, 409 phy_obj.avail_tx_antennas), 410 reverse=True) 411 phys = [phy_obj.name for phy_obj in phy_objs] 412 413 preferred_bus = {'monitor': 'usb', 'managed': 'pci'}.get(phytype) 414 preferred_phys = [phy for phy in phys 415 if self.phy_bus_type[phy] == preferred_bus] 416 phys = preferred_phys or phys 417 418 return phys[0] 419 420 421 def _get_wlanif(self, phytype, spatial_streams, frequency, same_phy_as): 422 """Get a WiFi device that supports the given frequency and phytype. 423 424 We simply find or create a suitable DEV. It is left to the 425 caller to actually configure the frequency and bring up the 426 interface. 427 428 @param phytype string type of phy (e.g. 'monitor'). 429 @param spatial_streams int number of spatial streams required. 430 @param frequency int WiFi frequency to support. 431 @param same_phy_as string create the interface on the same phy as this. 432 @return NetDev WiFi device. 433 434 """ 435 if frequency and same_phy_as: 436 raise error.TestError( 437 'Can not combine |frequency| and |same_phy_as|') 438 439 if not (frequency or same_phy_as): 440 raise error.TestError( 441 'Must specify one of |frequency| or |same_phy_as|') 442 443 if spatial_streams is None: 444 spatial_streams = self.MIN_SPATIAL_STREAMS 445 # We don't want to use the 3rd radio on Whirlwind. Reject it if someone 446 # tries to add a test that uses it. 447 elif spatial_streams < self.MIN_SPATIAL_STREAMS and \ 448 self.board == 'whirlwind': 449 raise error.TestError('Requested spatial streams: %d; minimum %d' \ 450 % (spatial_streams, self.MIN_SPATIAL_STREAMS)) 451 452 if same_phy_as: 453 for net_dev in self._interfaces: 454 if net_dev.if_name == same_phy_as: 455 phy = net_dev.phy 456 break 457 else: 458 raise error.TestFail('Unable to find phy for interface %s' % 459 same_phy_as) 460 elif frequency in self.phys_for_frequency: 461 phy = self._get_phy_for_frequency( 462 frequency, phytype, spatial_streams) 463 else: 464 raise error.TestFail('Unable to find phy for frequency %d' % 465 frequency) 466 467 # If we have a suitable unused interface sitting around on this 468 # phy, reuse it. 469 for net_dev in set(self._interfaces) - set(self._wlanifs_in_use): 470 if net_dev.phy == phy and net_dev.if_type == phytype: 471 break 472 else: 473 # Because we can reuse interfaces, we have to iteratively find a 474 # good interface name. 475 name_exists = lambda name: bool([net_dev 476 for net_dev in self._interfaces 477 if net_dev.if_name == name]) 478 if_name = lambda index: '%s%d' % (phytype, index) 479 if_index = len(self._interfaces) 480 while name_exists(if_name(if_index)): 481 if_index += 1 482 net_dev = NetDev(phy=phy, if_name=if_name(if_index), 483 if_type=phytype, inherited=False) 484 self._interfaces.append(net_dev) 485 self.iw_runner.add_interface(phy, net_dev.if_name, phytype) 486 487 # Link must be down to reconfigure MAC address. 488 self.host.run('%s link set dev %s down' % ( 489 self.cmd_ip, net_dev.if_name)) 490 if same_phy_as: 491 self.clone_mac_address(src_dev=same_phy_as, 492 dst_dev=net_dev.if_name) 493 else: 494 self.ensure_unique_mac(net_dev) 495 496 return net_dev 497 498 499 def get_brif(self): 500 brif_name = '%s%d' % (self.HOSTAP_BRIDGE_INTERFACE_PREFIX, 501 self._brif_index) 502 self._brif_index += 1 503 return brif_name 504 505 506 def get_configured_interface(self, phytype, spatial_streams=None, 507 frequency=None, same_phy_as=None): 508 """Get a WiFi device that supports the given frequency and phytype. 509 510 The device's link state will be UP, and (where possible) the device 511 will be configured to operate on |frequency|. 512 513 @param phytype string type of phy (e.g. 'monitor'). 514 @param spatial_streams int number of spatial streams required. 515 @param frequency int WiFi frequency to support. 516 @param same_phy_as string create the interface on the same phy as this. 517 @return string WiFi device. 518 519 """ 520 net_dev = self._get_wlanif( 521 phytype, spatial_streams, frequency, same_phy_as) 522 523 self.host.run('%s link set dev %s up' % (self.cmd_ip, net_dev.if_name)) 524 525 if frequency: 526 if phytype == 'managed': 527 logging.debug('Skipped setting frequency for DEV %s ' 528 'since managed mode DEVs roam across APs.', 529 net_dev.if_name) 530 elif same_phy_as or self._phy_in_use(net_dev.phy): 531 logging.debug('Skipped setting frequency for DEV %s ' 532 'since PHY %s is already in use', 533 net_dev.if_name, net_dev.phy) 534 else: 535 self.iw_runner.set_freq(net_dev.if_name, frequency) 536 537 self._wlanifs_in_use.append(net_dev) 538 return net_dev.if_name 539 540 541 # TODO(quiche): Deprecate this, in favor of get_configured_interface(). 542 # crbug.com/512169. 543 def get_wlanif(self, frequency, phytype, 544 spatial_streams=None, same_phy_as=None): 545 """Get a WiFi device that supports the given frequency and phytype. 546 547 We simply find or create a suitable DEV. It is left to the 548 caller to actually configure the frequency and bring up the 549 interface. 550 551 @param frequency int WiFi frequency to support. 552 @param phytype string type of phy (e.g. 'monitor'). 553 @param spatial_streams int number of spatial streams required. 554 @param same_phy_as string create the interface on the same phy as this. 555 @return string WiFi device. 556 557 """ 558 net_dev = self._get_wlanif( 559 phytype, spatial_streams, frequency, same_phy_as) 560 self._wlanifs_in_use.append(net_dev) 561 return net_dev.if_name 562 563 564 def ensure_unique_mac(self, net_dev): 565 """Ensure MAC address of |net_dev| meets uniqueness requirements. 566 567 The Linux kernel does not allow multiple APs with the same 568 BSSID on the same PHY (at least, with some drivers). Hence, we 569 want to ensure that the DEVs for a PHY have unique MAC 570 addresses. 571 572 Note that we do not attempt to make the MACs unique across 573 PHYs, because some tests deliberately create such scenarios. 574 575 @param net_dev NetDev to uniquify. 576 577 """ 578 if net_dev.if_type == 'monitor': 579 return 580 581 our_ifname = net_dev.if_name 582 our_phy = net_dev.phy 583 our_mac = interface.Interface(our_ifname, self.host).mac_address 584 sibling_devs = [dev for dev in self._interfaces 585 if (dev.phy == our_phy and 586 dev.if_name != our_ifname and 587 dev.if_type != 'monitor')] 588 sibling_macs = ( 589 interface.Interface(sib_dev.if_name, self.host).mac_address 590 for sib_dev in sibling_devs) 591 if our_mac in sibling_macs: 592 self.configure_interface_mac(our_ifname, 593 self._get_unique_mac()) 594 595 596 def configure_interface_mac(self, wlanif, new_mac): 597 """Change the MAC address for an interface. 598 599 @param wlanif string name of device to reconfigure. 600 @param new_mac string MAC address to assign (e.g. '00:11:22:33:44:55') 601 602 """ 603 self.host.run('%s link set %s address %s' % 604 (self.cmd_ip, wlanif, new_mac)) 605 606 607 def clone_mac_address(self, src_dev=None, dst_dev=None): 608 """Copy the MAC address from one interface to another. 609 610 @param src_dev string name of device to copy address from. 611 @param dst_dev string name of device to copy address to. 612 613 """ 614 self.configure_interface_mac( 615 dst_dev, 616 interface.Interface(src_dev, self.host).mac_address) 617 618 619 def release_interface(self, wlanif): 620 """Release a device allocated throuhg get_wlanif(). 621 622 @param wlanif string name of device to release. 623 624 """ 625 for net_dev in self._wlanifs_in_use: 626 if net_dev.if_name == wlanif: 627 self._wlanifs_in_use.remove(net_dev) 628 629 630 def get_bridge_interface(self): 631 """Return the bridge interface, create one if it is not created yet. 632 633 @return string name of bridge interface. 634 """ 635 if self._bridge_interface is None: 636 self._create_bridge_interface() 637 return self._bridge_interface 638 639 640 def remove_bridge_interface(self): 641 """Remove the bridge interface that's been created.""" 642 if self._bridge_interface is not None: 643 self.host.run('%s link delete %s type bridge' % 644 (self.cmd_ip, self._bridge_interface)) 645 self._bridge_interface = None 646 647 648 def add_interface_to_bridge(self, interface): 649 """Add an interface to the bridge interface. 650 651 This will create the bridge interface if it is not created yet. 652 653 @param interface string name of the interface to add to the bridge. 654 """ 655 if self._bridge_interface is None: 656 self._create_bridge_interface() 657 self.host.run('%s link set dev %s master %s' % 658 (self.cmd_ip, interface, self._bridge_interface)) 659 660 661 def get_virtual_ethernet_master_interface(self): 662 """Return the master interface of the virtual ethernet pair. 663 664 @return string name of the master interface of the virtual ethernet 665 pair. 666 """ 667 if self._virtual_ethernet_pair is None: 668 self._create_virtual_ethernet_pair() 669 return self._virtual_ethernet_pair.interface_name 670 671 672 def get_virtual_ethernet_peer_interface(self): 673 """Return the peer interface of the virtual ethernet pair. 674 675 @return string name of the peer interface of the virtual ethernet pair. 676 """ 677 if self._virtual_ethernet_pair is None: 678 self._create_virtual_ethernet_pair() 679 return self._virtual_ethernet_pair.peer_interface_name 680 681 682 def remove_ethernet_pair_interface(self): 683 """Remove the virtual ethernet pair that's been created.""" 684 if self._virtual_ethernet_pair is not None: 685 self._virtual_ethernet_pair.teardown() 686 self._virtual_ethernet_pair = None 687 688 689 def require_capabilities(self, requirements): 690 """Require capabilities of this LinuxSystem. 691 692 Check that capabilities in |requirements| exist on this system. 693 Raise an exception to skip but not fail the test if said 694 capabilities are not found. 695 696 @param requirements list of CAPABILITY_* defined above. 697 698 """ 699 missing = [cap for cap in requirements if not cap in self.capabilities] 700 if missing: 701 raise error.TestNAError('%s is missing required capabilites: %r' 702 % (self.role, missing)) 703 704 705 def disable_antennas_except(self, permitted_antennas): 706 """Disable unwanted antennas. 707 708 Disable all antennas except those specified in |permitted_antennas|. 709 Note that one or more of them may remain disabled if the underlying 710 hardware does not support them. 711 712 @param permitted_antennas int bitmask specifying antennas that we should 713 attempt to enable. 714 715 """ 716 for phy in self.phy_list: 717 if not phy.supports_setting_antenna_mask: 718 continue 719 # Determine valid bitmap values based on available antennas. 720 self.iw_runner.set_antenna_bitmap(phy.name, 721 permitted_antennas & phy.avail_tx_antennas, 722 permitted_antennas & phy.avail_rx_antennas) 723 724 725 def enable_all_antennas(self): 726 """Enable all antennas on all phys.""" 727 for phy in self.phy_list: 728 if not phy.supports_setting_antenna_mask: 729 continue 730 self.iw_runner.set_antenna_bitmap(phy.name, phy.avail_tx_antennas, 731 phy.avail_rx_antennas) 732 733 734 def ping(self, ping_config): 735 """Ping an IP from this system. 736 737 @param ping_config PingConfig object describing the ping command to run. 738 @return a PingResult object. 739 740 """ 741 logging.info('Pinging from the %s.', self.role) 742 return self._ping_runner.ping(ping_config) 743