diff options
-rw-r--r-- | etc/tempest.conf.sample | 74 | ||||
-rwxr-xr-x | tempest/cmd/javelin.py | 17 | ||||
-rwxr-xr-x | tempest/cmd/verify_tempest_config.py | 10 | ||||
-rw-r--r-- | tempest/common/utils/linux/remote_client.py | 5 | ||||
-rw-r--r-- | tempest/config.py | 74 | ||||
-rw-r--r-- | tempest/scenario/manager.py | 9 | ||||
-rw-r--r-- | tempest/scenario/test_network_basic_ops.py | 14 | ||||
-rw-r--r-- | tempest/tests/common/utils/linux/test_remote_client.py | 12 |
8 files changed, 161 insertions, 54 deletions
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample index 175f0d965..1010ba531 100644 --- a/etc/tempest.conf.sample +++ b/etc/tempest.conf.sample @@ -253,10 +253,6 @@ # image. (string value) #image_alt_ssh_user = root -# Password used to authenticate to an instance using the alternate -# image. (string value) -#image_alt_ssh_password = password - # Time in seconds between build status checks. (integer value) #build_interval = 1 @@ -269,16 +265,16 @@ #run_ssh = false # Auth method used for authenticate to the instance. Valid choices -# are: keypair, configured, adminpass. keypair: start the servers with -# an ssh keypair. configured: use the configured user and password. -# adminpass: use the injected adminPass. disabled: avoid using ssh -# when it is an option. (string value) +# are: keypair, configured, adminpass and disabled. Keypair: start the +# servers with a ssh keypair. Configured: use the configured user and +# password. Adminpass: use the injected adminPass. Disabled: avoid +# using ssh when it is an option. (string value) #ssh_auth_method = keypair # How to connect to the instance? fixed: using the first ip belongs -# the fixed network floating: creating and using a floating ip (string -# value) -#ssh_connect_method = fixed +# the fixed network floating: creating and using a floating ip. +# (string value) +#ssh_connect_method = floating # User name used to authenticate to an instance. (string value) #ssh_user = root @@ -286,6 +282,14 @@ # Timeout in seconds to wait for ping to succeed. (integer value) #ping_timeout = 120 +# The packet size for ping packets originating from remote linux hosts +# (integer value) +#ping_size = 56 + +# The number of ping packets originating from remote linux hosts +# (integer value) +#ping_count = 1 + # Timeout in seconds to wait for authentication to succeed. (integer # value) #ssh_timeout = 300 @@ -301,7 +305,8 @@ # Name of the fixed network that is visible to all test tenants. If # multiple networks are available for a tenant this is the network # which will be used for creating servers if tempest does not create a -# network or a network is not specified elsewhere (string value) +# network or a network is not specified elsewhere. It may be used for +# ssh validation only if floating IPs are disabled. (string value) #fixed_network_name = <None> # Network used for SSH connections. Ignored if @@ -326,10 +331,6 @@ # Allowed values: public, admin, internal, publicURL, adminURL, internalURL #endpoint_type = publicURL -# Path to a private key file for SSH access to remote hosts (string -# value) -#path_to_private_key = <None> - # Expected device name when a volume is attached to an instance # (string value) #volume_device_name = vdb @@ -746,14 +747,19 @@ # The mask bits for tenant ipv6 subnets (integer value) #tenant_network_v6_mask_bits = 64 -# Whether tenant network connectivity should be evaluated directly -# (boolean value) +# Whether tenant networks can be reached directly from the test +# client. This must be set to True when the 'fixed' ssh_connect_method +# is selected. (boolean value) #tenant_networks_reachable = false # Id of the public network that provides external connectivity (string # value) #public_network_id = +# Default floating network name. Used to allocate floating IPs when +# neutron is enabled. (string value) +#floating_network_name = <None> + # Id of the public router that provides external connectivity. This # should only be used when Neutron's 'allow_overlapping_ips' is set to # 'False' in neutron.conf. usually not needed past 'Grizzly' release @@ -1071,6 +1077,38 @@ #too_slow_to_test = true +[validation] + +# +# From tempest.config +# + +# Default IP type used for validation: -fixed: uses the first IP +# belonging to the fixed network -floating: creates and uses a +# floating IP (string value) +# Allowed values: fixed, floating +#connect_method = floating + +# Default authentication method to the instance. Only ssh via keypair +# is supported for now. Additional methods will be handled in a +# separate spec. (string value) +# Allowed values: keypair +#auth_method = keypair + +# Default IP version for ssh connections. (integer value) +#ip_version_for_ssh = 4 + +# Timeout in seconds to wait for ping to succeed. (integer value) +#ping_timeout = 120 + +# Timeout in seconds to wait for the TCP connection to be successful. +# (integer value) +#connect_timeout = 60 + +# Timeout in seconds to wait for the ssh banner. (integer value) +#ssh_timeout = 300 + + [volume] # diff --git a/tempest/cmd/javelin.py b/tempest/cmd/javelin.py index fec3bd4a2..f84771fd8 100755 --- a/tempest/cmd/javelin.py +++ b/tempest/cmd/javelin.py @@ -77,7 +77,8 @@ resource file:: - name: javelin_cirros owner: javelin file: cirros-0.3.2-x86_64-blank.img - format: ami + disk_format: ami + container_format: ami aki: cirros-0.3.2-x86_64-vmlinuz ari: cirros-0.3.2-x86_64-initrd @@ -629,6 +630,15 @@ def create_images(images): for image in images: client = client_for_user(image['owner']) + # DEPRECATED: 'format' was used for ami images + # Use 'disk_format' and 'container_format' instead + if 'format' in image: + LOG.warning("Deprecated: 'format' is deprecated for images " + "description. Please use 'disk_format' and 'container_" + "format' instead.") + image['disk_format'] = image['format'] + image['container_format'] = image['format'] + # only upload a new image if the name isn't there if _get_image_by_name(client, image['name']): LOG.info("Image '%s' already exists" % image['name']) @@ -636,7 +646,7 @@ def create_images(images): # special handling for 3 part image extras = {} - if image['format'] == 'ami': + if image['disk_format'] == 'ami': name, fname = _resolve_image(image, 'aki') aki = client.images.create_image( 'javelin_' + name, 'aki', 'aki') @@ -651,7 +661,8 @@ def create_images(images): _, fname = _resolve_image(image, 'file') body = client.images.create_image( - image['name'], image['format'], image['format'], **extras) + image['name'], image['container_format'], + image['disk_format'], **extras) image_id = body.get('id') client.images.store_image(image_id, open(fname, 'r')) diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py index 3c71e072e..b61f286ee 100755 --- a/tempest/cmd/verify_tempest_config.py +++ b/tempest/cmd/verify_tempest_config.py @@ -24,6 +24,7 @@ import httplib2 from six import moves from tempest import clients +from tempest.common import credentials from tempest import config @@ -254,7 +255,11 @@ def check_service_availability(os, update): } # Get catalog list for endpoints to use for validation _token, auth_data = os.auth_provider.get_auth() - for entry in auth_data['serviceCatalog']: + if os.auth_version == 'v2': + catalog_key = 'serviceCatalog' + else: + catalog_key = 'catalog' + for entry in auth_data[catalog_key]: services.append(entry['type']) # Pull all catalog types from config file and compare against endpoint list for cfgname in dir(CONF._config): @@ -329,7 +334,8 @@ def main(): CONF_PARSER = moves.configparser.SafeConfigParser() CONF_PARSER.optionxform = str CONF_PARSER.readfp(conf_file) - os = clients.Manager() + icreds = credentials.get_isolated_credentials('verify_tempest_config') + os = clients.Manager(icreds.get_primary_creds()) services = check_service_availability(os, update) results = {} for service in ['nova', 'cinder', 'neutron', 'swift']: diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py index b19faefab..29fb493ed 100644 --- a/tempest/common/utils/linux/remote_client.py +++ b/tempest/common/utils/linux/remote_client.py @@ -87,10 +87,11 @@ class RemoteClient(object): cmd = 'sudo sh -c "echo \\"%s\\" >/dev/console"' % message return self.exec_command(cmd) - def ping_host(self, host): + def ping_host(self, host, count=CONF.compute.ping_count, + size=CONF.compute.ping_size): addr = netaddr.IPAddress(host) cmd = 'ping6' if addr.version == 6 else 'ping' - cmd += ' -c1 -w1 {0}'.format(host) + cmd += ' -c{0} -w{0} -s{1} {2}'.format(count, size, host) return self.exec_command(cmd) def get_mac_address(self): diff --git a/tempest/config.py b/tempest/config.py index 3725f5843..6b8113efe 100644 --- a/tempest/config.py +++ b/tempest/config.py @@ -187,10 +187,6 @@ ComputeGroup = [ default="root", help="User name used to authenticate to an instance using " "the alternate image."), - cfg.StrOpt('image_alt_ssh_password', - default="password", - help="Password used to authenticate to an instance using " - "the alternate image."), cfg.IntOpt('build_interval', default=1, help="Time in seconds between build status checks."), @@ -205,16 +201,17 @@ ComputeGroup = [ cfg.StrOpt('ssh_auth_method', default='keypair', help="Auth method used for authenticate to the instance. " - "Valid choices are: keypair, configured, adminpass. " - "keypair: start the servers with an ssh keypair. " - "configured: use the configured user and password. " - "adminpass: use the injected adminPass. " - "disabled: avoid using ssh when it is an option."), + "Valid choices are: keypair, configured, adminpass " + "and disabled. " + "Keypair: start the servers with a ssh keypair. " + "Configured: use the configured user and password. " + "Adminpass: use the injected adminPass. " + "Disabled: avoid using ssh when it is an option."), cfg.StrOpt('ssh_connect_method', - default='fixed', + default='floating', help="How to connect to the instance? " "fixed: using the first ip belongs the fixed network " - "floating: creating and using a floating ip"), + "floating: creating and using a floating ip."), cfg.StrOpt('ssh_user', default='root', help="User name used to authenticate to an instance."), @@ -222,6 +219,14 @@ ComputeGroup = [ default=120, help="Timeout in seconds to wait for ping to " "succeed."), + cfg.IntOpt('ping_size', + default=56, + help="The packet size for ping packets originating " + "from remote linux hosts"), + cfg.IntOpt('ping_count', + default=1, + help="The number of ping packets originating from remote " + "linux hosts"), cfg.IntOpt('ssh_timeout', default=300, help="Timeout in seconds to wait for authentication to " @@ -239,7 +244,8 @@ ComputeGroup = [ "tenants. If multiple networks are available for a tenant" " this is the network which will be used for creating " "servers if tempest does not create a network or a " - "network is not specified elsewhere"), + "network is not specified elsewhere. It may be used for " + "ssh validation only if floating IPs are disabled."), cfg.StrOpt('network_for_ssh', default='public', help="Network used for SSH connections. Ignored if " @@ -264,9 +270,6 @@ ComputeGroup = [ choices=['public', 'admin', 'internal', 'publicURL', 'adminURL', 'internalURL'], help="The endpoint type to use for the compute service."), - cfg.StrOpt('path_to_private_key', - help="Path to a private key file for SSH access to remote " - "hosts"), cfg.StrOpt('volume_device_name', default='vdb', help="Expected device name when a volume is attached to " @@ -449,12 +452,16 @@ NetworkGroup = [ help="The mask bits for tenant ipv6 subnets"), cfg.BoolOpt('tenant_networks_reachable', default=False, - help="Whether tenant network connectivity should be " - "evaluated directly"), + help="Whether tenant networks can be reached directly from " + "the test client. This must be set to True when the " + "'fixed' ssh_connect_method is selected."), cfg.StrOpt('public_network_id', default="", help="Id of the public network that provides external " "connectivity"), + cfg.StrOpt('floating_network_name', + help="Default floating network name. Used to allocate floating " + "IPs when neutron is enabled."), cfg.StrOpt('public_router_id', default="", help="Id of the public router that provides external " @@ -536,6 +543,37 @@ MessagingGroup = [ help='The maximum grace period for a claim'), ] +validation_group = cfg.OptGroup(name='validation', + title='SSH Validation options') + +ValidationGroup = [ + cfg.StrOpt('connect_method', + default='floating', + choices=['fixed', 'floating'], + help='Default IP type used for validation: ' + '-fixed: uses the first IP belonging to the fixed network ' + '-floating: creates and uses a floating IP'), + cfg.StrOpt('auth_method', + default='keypair', + choices=['keypair'], + help='Default authentication method to the instance. ' + 'Only ssh via keypair is supported for now. ' + 'Additional methods will be handled in a separate spec.'), + cfg.IntOpt('ip_version_for_ssh', + default=4, + help='Default IP version for ssh connections.'), + cfg.IntOpt('ping_timeout', + default=120, + help='Timeout in seconds to wait for ping to succeed.'), + cfg.IntOpt('connect_timeout', + default=60, + help='Timeout in seconds to wait for the TCP connection to be ' + 'successful.'), + cfg.IntOpt('ssh_timeout', + default=300, + help='Timeout in seconds to wait for the ssh banner.'), +] + volume_group = cfg.OptGroup(name='volume', title='Block Storage Options') @@ -1088,6 +1126,7 @@ _opts = [ (network_group, NetworkGroup), (network_feature_group, NetworkFeaturesGroup), (messaging_group, MessagingGroup), + (validation_group, ValidationGroup), (volume_group, VolumeGroup), (volume_feature_group, VolumeFeaturesGroup), (object_storage_group, ObjectStoreGroup), @@ -1148,6 +1187,7 @@ class TempestConfigPrivate(object): self.image_feature_enabled = _CONF['image-feature-enabled'] self.network = _CONF.network self.network_feature_enabled = _CONF['network-feature-enabled'] + self.validation = _CONF.validation self.volume = _CONF.volume self.volume_feature_enabled = _CONF['volume-feature-enabled'] self.object_storage = _CONF['object-storage'] diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py index 65d516f03..d2c41f08f 100644 --- a/tempest/scenario/manager.py +++ b/tempest/scenario/manager.py @@ -309,8 +309,13 @@ class ScenarioTest(tempest.test.BaseTestCase): if isinstance(server_or_ip, six.string_types): ip = server_or_ip else: - addr = server_or_ip['addresses'][CONF.compute.network_for_ssh][0] - ip = addr['addr'] + addrs = server_or_ip['addresses'][CONF.compute.network_for_ssh] + try: + ip = (addr['addr'] for addr in addrs if + netaddr.valid_ipv4(addr['addr'])).next() + except StopIteration: + raise lib_exc.NotFound("No IPv4 addresses to use for SSH to " + "remote server.") if username is None: username = CONF.scenario.ssh_user diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py index 835304813..2c4522dfb 100644 --- a/tempest/scenario/test_network_basic_ops.py +++ b/tempest/scenario/test_network_basic_ops.py @@ -318,11 +318,15 @@ class TestNetworkBasicOps(manager.NetworkScenarioTest): LOG.info(msg) return - subnet = self._list_subnets( - network_id=CONF.network.public_network_id) - self.assertEqual(1, len(subnet), "Found %d subnets" % len(subnet)) - - external_ips = [subnet[0]['gateway_ip']] + # We ping the external IP from the instance using its floating IP + # which is always IPv4, so we must only test connectivity to + # external IPv4 IPs if the external network is dualstack. + v4_subnets = [s for s in self._list_subnets( + network_id=CONF.network.public_network_id) if s['ip_version'] == 4] + self.assertEqual(1, len(v4_subnets), + "Found %d IPv4 subnets" % len(v4_subnets)) + + external_ips = [v4_subnets[0]['gateway_ip']] self._check_server_connectivity(self.floating_ip_tuple.floating_ip, external_ips) diff --git a/tempest/tests/common/utils/linux/test_remote_client.py b/tempest/tests/common/utils/linux/test_remote_client.py index 40b7b32aa..d6377e620 100644 --- a/tempest/tests/common/utils/linux/test_remote_client.py +++ b/tempest/tests/common/utils/linux/test_remote_client.py @@ -100,15 +100,17 @@ class TestRemoteClient(base.TestCase): self._assert_exec_called_with('cut -f1 -d. /proc/uptime') def test_ping_host(self): - ping_response = """PING localhost (127.0.0.1) 56(84) bytes of data. -64 bytes from localhost (127.0.0.1): icmp_req=1 ttl=64 time=0.048 ms + ping_response = """PING localhost (127.0.0.1) 70(98) bytes of data. +78 bytes from localhost (127.0.0.1): icmp_req=1 ttl=64 time=0.048 ms +78 bytes from localhost (127.0.0.1): icmp_req=2 ttl=64 time=0.048 ms --- localhost ping statistics --- -1 packets transmitted, 1 received, 0% packet loss, time 0ms +2 packets transmitted, 2 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 0.048/0.048/0.048/0.000 ms""" self.ssh_mock.mock.exec_command.return_value = ping_response - self.assertEqual(self.conn.ping_host('127.0.0.1'), ping_response) - self._assert_exec_called_with('ping -c1 -w1 127.0.0.1') + self.assertEqual(self.conn.ping_host('127.0.0.1', count=2, size=70), + ping_response) + self._assert_exec_called_with('ping -c2 -w2 -s70 127.0.0.1') def test_get_mac_address(self): macs = """0a:0b:0c:0d:0e:0f |