blob: f856c7ac1fd1fbd0bca1ff2da72397cd33f85bde [file] [log] [blame]
# Copyright (c) 2013 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.
from autotest_lib.client.common_lib import error
from autotest_lib.client.cros import dhcp_handling_rule
from autotest_lib.client.cros import dhcp_packet
from autotest_lib.client.cros import dhcp_test_base
from autotest_lib.client.cros import shill_temporary_profile
# This hacks the path so that we can import shill_proxy.
# pylint: disable=W0611
from autotest_lib.client.cros import flimflam_test_path
# pylint: enable=W0611
import shill_proxy
class network_DhcpStaticIP(dhcp_test_base.DhcpTestBase):
"""DHCP test which confirms static IP functionality"""
# Length of time the lease from the DHCP server is valid.
LEASE_TIME_SECONDS = 60
# We'll fill in the subnet and give this address to the client over DHCP.
INTENDED_IP_SUFFIX = '0.0.0.101'
# We'll fill in the subnet and supply this as a static IP address.
STATIC_IP_SUFFIX = '0.0.0.201'
STATIC_IP_NAME_SERVERS = [ '1.1.2.2', '1.1.3.4' ]
# Time to wait for the DHCP negotiation protocol to complete.
DHCP_NEGOTIATION_TIMEOUT_SECONDS = 10
# Time to wait after DHCP negotiation completes until service is marked
# as connected.
DHCP_SETUP_TIMEOUT_SECONDS = 3
# Name given to the temporary shill profile we create for this test.
TEST_PROFILE_NAME = 'TestStaticIP'
# Various parameters that can be set statically.
CONFIGURE_STATIC_IP_ADDRESS = 'ip-address'
CONFIGURE_STATIC_IP_DNS_SERVERS = 'dns-servers'
def get_device(self, interface_name):
"""Finds the corresponding Device object for an interface with
the name |interface_name|.
@param interface_name string The name of the interface to check.
@return DBus interface object representing the associated device.
"""
device = self._shill_proxy.find_object('Device',
{'Name': interface_name})
if device is None:
raise error.TestFail('Device was not found.')
return device
def find_ethernet_service(self, interface_name):
"""Finds the corresponding service object for an ethernet
interface.
@param interface_name string The name of the associated interface
@return Service object representing the associated service.
"""
device = self.get_device(interface_name)
device_path = shill_proxy.ShillProxy.dbus2primitive(device.object_path)
return self._shill_proxy.find_object('Service', {'Device': device_path})
def configure_static_ip(self, service, params):
"""Configures the Static IP parameters for the Ethernet interface
|interface_name| and applies those parameters to the interface by
forcing a re-connect.
@param service object the Service DBus interface to configure.
@param params list of static parameters to set on the service.
"""
self._static_ip_options = {}
if self.CONFIGURE_STATIC_IP_ADDRESS in params:
subnet_mask = self.ethernet_pair.interface_subnet_mask
static_ip_address = dhcp_test_base.DhcpTestBase.rewrite_ip_suffix(
subnet_mask,
self.server_ip,
self.STATIC_IP_SUFFIX)
prefix_len = self.ethernet_pair.interface_prefix
service.SetProperty('StaticIP.Address', static_ip_address)
service.SetProperty('StaticIP.Prefixlen', prefix_len)
self._static_ip_options[dhcp_packet.OPTION_REQUESTED_IP] = (
static_ip_address)
if self.CONFIGURE_STATIC_IP_DNS_SERVERS in params:
service.SetProperty('StaticIP.NameServers',
','.join(self.STATIC_IP_NAME_SERVERS))
self._static_ip_options[dhcp_packet.OPTION_DNS_SERVERS] = (
self.STATIC_IP_NAME_SERVERS)
service.Disconnect()
service.Connect()
def clear_static_ip(self, service, params):
"""Clears configuration of Static IP parameters for the Ethernet
interface and forces a re-connect.
@param service object the Service DBus interface to clear properties.
@param params list of static parameters to clear from the service.
"""
if self.CONFIGURE_STATIC_IP_ADDRESS in params:
service.ClearProperty('StaticIP.Address')
service.ClearProperty('StaticIP.Prefixlen')
if self.CONFIGURE_STATIC_IP_DNS_SERVERS in params:
service.ClearProperty('StaticIP.NameServers')
service.Disconnect()
service.Connect()
def check_saved_ip(self, service, options):
"""Check the properties of the Ethernet service to make sure that
the address provided by the DHCP server is properly added to the
"Saved.Address".
@param service object the Service DBus interface to clear properties.
@param options dict parameters that were used to configure the DHCP
server.
"""
intended_ip = options[dhcp_packet.OPTION_REQUESTED_IP]
properties = service.GetProperties()
if intended_ip != properties['SavedIP.Address']:
raise error.TestFail('Saved IP address %s is not DHCP address %s' %
(properties['SavedIP.Address'], intended_ip))
def make_lease_negotiation_rules(self, options):
"""Generate a set of lease negotiation handling rules for a
server that will successfully return an IP address to the client.
@param options dict of options to be negotiated. In particular,
the dhcp_packet.OPTION_REQUESTED_IP element is used to configure
the address that will be returned to the client.
@return array of DhcpHandlingRule instances which implement the
negotiation.
"""
intended_ip = options[dhcp_packet.OPTION_REQUESTED_IP]
rules = []
rules.append(dhcp_handling_rule.DhcpHandlingRule_RespondToDiscovery(
intended_ip,
self.server_ip,
options,
{}))
rules.append(dhcp_handling_rule.DhcpHandlingRule_RespondToRequest(
intended_ip,
self.server_ip,
options,
{}))
return rules
def test_dhcp_negotiation(self, rules, service):
"""Perform a DHCP lease negotiation using handler rules from |rules|,
and ensure that |service| becomes connected as a result.
@param rules array of handling rules that must complete in order for
the negotiation to be considered successful.
@param service Service DBus object which should become connected as
a result of the DHCP negotiation.
"""
rules[-1].is_final_handler = True
self.server.start_test(rules, self.DHCP_NEGOTIATION_TIMEOUT_SECONDS)
self.server.wait_for_test_to_finish()
if not self.server.last_test_passed:
raise error.TestFail('Test server didn\'t get all the messages it '
'was told to expect during negotiation.')
# Wait for the service to enter a "good" state.
connect_result = self._shill_proxy.wait_for_property_in(
service,
self._shill_proxy.SERVICE_PROPERTY_STATE,
('ready', 'portal', 'online'),
self.DHCP_SETUP_TIMEOUT_SECONDS)
(successful, _, association_time) = connect_result
if not successful:
raise error.TestFail('Ethernet service did not become connected.')
def connect_dynamic_ip(self, options, service):
"""Perform a DHCP negotiation, using |options|. Then check that
the IP information configured on client matches the parameters
in |options|.
@param options dict containing DHCP packet options to be returned
to the client during negotiation, and then later checked for
consistency.
@param service DBus object of the service that should become
connected as a result of the negotiation.
"""
self.test_dhcp_negotiation(self.make_lease_negotiation_rules(options),
service)
self.check_dhcp_config(options)
def connect_static_ip(self, options, service, params):
"""Perform a DHCP negotiation, using |options|. Then check that
the IP information configured on client matches the parameters
in |options|, except that the client's IP address should be
|static_ip_address|.
@param options dict containing DHCP packet options to be returned
to the client during negotiation, and then later checked for
consistency.
@param service DBus object of the service that should become
connected as a result of the negotiation.
@param params list of static IP parameters we will be verifying.
"""
rules = self.make_lease_negotiation_rules(options)
if self.CONFIGURE_STATIC_IP_ADDRESS in params:
# Add a rule that expects the client to release the lease.
rules.append(dhcp_handling_rule.DhcpHandlingRule_AcceptRelease(
self.server_ip,
options,
{}))
self.test_dhcp_negotiation(rules, service)
# Check to make sure that the configured IP address of the client
# matches the configured static IP address.
static_ip_options = options.copy()
static_ip_options.update(self._static_ip_options)
self.check_dhcp_config(static_ip_options)
self.check_saved_ip(service, options)
def test_body(self):
"""The test main body"""
subnet_mask = self.ethernet_pair.interface_subnet_mask
intended_ip = dhcp_test_base.DhcpTestBase.rewrite_ip_suffix(
subnet_mask,
self.server_ip,
self.INTENDED_IP_SUFFIX)
# Two real name servers, and a bogus one to be unpredictable.
dns_servers = ['8.8.8.8', '8.8.4.4', '192.168.87.88']
domain_name = 'corp.google.com'
dns_search_list = [
'corgie.google.com',
'lies.google.com',
'that.is.a.tasty.burger.google.com',
]
# This is the pool of information the server will give out to the client
# upon request.
dhcp_options = {
dhcp_packet.OPTION_SERVER_ID : self.server_ip,
dhcp_packet.OPTION_SUBNET_MASK : subnet_mask,
dhcp_packet.OPTION_IP_LEASE_TIME : self.LEASE_TIME_SECONDS,
dhcp_packet.OPTION_REQUESTED_IP : intended_ip,
dhcp_packet.OPTION_DNS_SERVERS : dns_servers,
dhcp_packet.OPTION_DOMAIN_NAME : domain_name,
dhcp_packet.OPTION_DNS_DOMAIN_SEARCH_LIST : dns_search_list,
}
self._shill_proxy = shill_proxy.ShillProxy()
service = self.find_ethernet_service(
self.ethernet_pair.peer_interface_name)
manager = self._shill_proxy.manager
with shill_temporary_profile.ShillTemporaryProfile(
manager, profile_name=self.TEST_PROFILE_NAME):
self.connect_dynamic_ip(dhcp_options, service)
for params in self._static_param_list:
self.configure_static_ip(service, params)
self.connect_static_ip(dhcp_options, service, params)
self.clear_static_ip(service, params)
self.connect_dynamic_ip(dhcp_options, service)
def run_once(self, static_param_list):
"""Setup the static parameter list before calling the DhcpTestBase
main loop.
@param static_param_list list of iterable properties to configure
for each static IP test.
"""
self._static_param_list = static_param_list
super(network_DhcpStaticIP, self).run_once()