blob: 10baaa5e7082164ac97de37421cbc07c5fd4c345 [file] [log] [blame]
# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import logging
import os
import time
from autotest_lib.client.bin import utils
from autotest_lib.client.common_lib import error
# Flag file used to tell backchannel script it's okay to run.
BACKCHANNEL_FILE = '/mnt/stateful_partition/etc/enable_backchannel_network'
class Backchannel(object):
"""Wrap backchannel in a context manager so it can be used with with.
Example usage:
with backchannel.Backchannel():
The backchannel will be torn down whether or not 'block' throws.
def __init__(self, *args, **kwargs):
self.args = args
self.kwargs = kwargs
self.gateway = None
self.interface = None
def __enter__(self):
self.setup(*self.args, **self.kwargs)
return self
def __exit__(self, exception, value, traceback):
return False
def setup(self, create_ssh_routes=True):
Enables the backchannel interface.
@param create_ssh_routes: If True set up routes so that all existing
SSH sessions will remain open.
@returns True if the backchannel is already set up, or was set up by
this call, otherwise False.
# If the backchannel interface is already up there's nothing
# for us to do.
if is_network_iface_running('eth_test'):
return True
# Retrieve the gateway for the default route.
# Poll here until we have route information.
# If shill was recently started, it will take some time before
# DHCP gives us an address.
lambda: get_route_information(),
'Timed out waiting for route information'),
line = get_route_information()
self.gateway, self.interface = line.strip().split(' ')
# Retrieve list of open ssh sessions so we can reopen
# routes afterward.
if create_ssh_routes:
out = utils.system_output(
"netstat -tanp | grep :22 | "
"grep ESTABLISHED | awk '{print $5}'")
# Extract IP from IP:PORT listing. Uses set to remove
# duplicates.
open_ssh = list(set(item.strip().split(':')[0] for item in
out.split('\n') if item.strip()))
backchannel('setup %s' % self.interface)
# Create routes so existing SSH sessions will stay open.
if create_ssh_routes:
for ip in open_ssh:
# Add route using the pre-backchannel gateway.
backchannel('reach %s %s' % (ip, self.gateway))
# Make sure we have a route to the gateway before continuing.'Waiting for route to gateway %s', self.gateway)
lambda: is_route_ready(self.gateway),
exception=utils.TimeoutError('Timed out waiting for route'),
except Exception, e:
return False
# Remove backchannel file flag so system reverts to normal
# on reboot.
if os.path.isfile(BACKCHANNEL_FILE):
return True
def teardown(self):
"""Tears down the backchannel."""
if self.interface:
backchannel('teardown %s' % self.interface)
# Hack around broken Asix network adaptors that may flake out when we
# bring them up and down (
# TODO(thieule): Remove this when the adaptor/driver is fixed
# (
if self.gateway:'Waiting for route restore to gateway %s',
lambda: is_route_ready(self.gateway),
'Timed out waiting for route'),
except utils.TimeoutError:
def _reset_usb_ethernet_device(self):
# Use the absolute path to the USB device instead of accessing it
# via the path with the interface name because once we
# deauthorize the USB device, the interface name will be gone.
usb_authorized_path = os.path.realpath(
'/sys/class/net/%s/device/../authorized' % self.interface)'Reset ethernet device at %s', usb_authorized_path)
utils.system('echo 0 > %s' % usb_authorized_path)
utils.system('echo 1 > %s' % usb_authorized_path)
except error.CmdError:
def backchannel(args):
"""Launches the backchannel script that does the heavy lifting."""
# TODO(pprabhu): Switch to use python version of backchannel
# (
utils.system('/usr/local/lib/flimflam/test/backchannel %s' % args)
def is_network_iface_running(name):
Checks to see if the interface is running.
@param name: Name of the interface to check.
@returns True if the interface is running.
out = utils.system_output('ifconfig %s' % name)
except error.CmdError, e:
return False
return out.find('RUNNING') >= 0
def get_route_information():
Retrieves the default route information.
@returns a string that contains the gateway address and the interface.
If no route information is available, returns an empty string.
return utils.system_output(
"route -n | awk '/^ { print $2, $8 }'").split('\n')[0]
def is_route_ready(dest):
Checks to see if there is a route to the specified destination.
@param dest: IP address of the destination to check.
@returns True if there is a route to |dest|.
utils.system_output('ping -c 1 %s' % dest)'Route to %s is ready.', dest)
except error.CmdError, e:
logging.warning('Route to %s is not ready.', dest)
return False
return True