Merge "Test for the agent management extension API"
diff --git a/.testr.conf b/.testr.conf
index 05b12c4..c25ebec 100644
--- a/.testr.conf
+++ b/.testr.conf
@@ -2,7 +2,8 @@
test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-500} \
- ${PYTHON:-python} -m subunit.run discover -t ./ ./tempest $LISTOPT $IDOPTION
+ OS_TEST_PATH=${OS_TEST_PATH:-./tempest} \
+ ${PYTHON:-python} -m subunit.run discover -t ./ $OS_TEST_PATH $LISTOPT $IDOPTION
test_id_option=--load-list $IDFILE
test_list_option=--list
group_regex=([^\.]*\.)*
diff --git a/tempest/api/compute/images/test_images_oneserver_negative.py b/tempest/api/compute/images/test_images_oneserver_negative.py
index 864f445..2d27b81 100644
--- a/tempest/api/compute/images/test_images_oneserver_negative.py
+++ b/tempest/api/compute/images/test_images_oneserver_negative.py
@@ -49,7 +49,10 @@
LOG.exception(exc)
# Rebuild server if cannot reach the ACTIVE state
# Usually it means the server had a serius accident
- self.__class__.server_id = self.rebuild_server(self.server_id)
+ self._reset_server()
+
+ def _reset_server(self):
+ self.__class__.server_id = self.rebuild_server(self.server_id)
@classmethod
def setUpClass(cls):
@@ -116,6 +119,7 @@
self.assertEqual(202, resp.status)
image_id = data_utils.parse_image_id(resp['location'])
self.image_ids.append(image_id)
+ self.addCleanup(self._reset_server)
# Create second snapshot
alt_snapshot_name = data_utils.rand_name('test-snap-')
@@ -139,6 +143,7 @@
self.assertEqual(202, resp.status)
image_id = data_utils.parse_image_id(resp['location'])
self.image_ids.append(image_id)
+ self.addCleanup(self._reset_server)
# Do not wait, attempt to delete the image, ensure it's successful
resp, body = self.client.delete_image(image_id)
diff --git a/tempest/api/network/common.py b/tempest/api/network/common.py
index ab19fa8..528a204 100644
--- a/tempest/api/network/common.py
+++ b/tempest/api/network/common.py
@@ -47,6 +47,9 @@
def delete(self):
raise NotImplemented()
+ def __hash__(self):
+ return id(self)
+
class DeletableNetwork(DeletableResource):
@@ -86,6 +89,23 @@
class DeletableFloatingIp(DeletableResource):
+ def update(self, *args, **kwargs):
+ result = self.client.update_floatingip(floatingip=self.id,
+ body=dict(
+ floatingip=dict(*args,
+ **kwargs)
+ ))
+ super(DeletableFloatingIp, self).update(**result['floatingip'])
+
+ def __repr__(self):
+ return '<%s addr="%s">' % (self.__class__.__name__,
+ self.floating_ip_address)
+
+ def __str__(self):
+ return '<"FloatingIP" addr="%s" id="%s">' % (self.__class__.__name__,
+ self.floating_ip_address,
+ self.id)
+
def delete(self):
self.client.delete_floatingip(self.id)
diff --git a/tempest/cli/simple_read_only/test_neutron.py b/tempest/cli/simple_read_only/test_neutron.py
index 047b17d..80376ab 100644
--- a/tempest/cli/simple_read_only/test_neutron.py
+++ b/tempest/cli/simple_read_only/test_neutron.py
@@ -44,35 +44,43 @@
raise cls.skipException(msg)
super(SimpleReadOnlyNeutronClientTest, cls).setUpClass()
+ @test.attr(type='smoke')
def test_neutron_fake_action(self):
self.assertRaises(subprocess.CalledProcessError,
self.neutron,
'this-does-not-exist')
+ @test.attr(type='smoke')
def test_neutron_net_list(self):
self.neutron('net-list')
+ @test.attr(type='smoke')
def test_neutron_ext_list(self):
ext = self.parser.listing(self.neutron('ext-list'))
self.assertTableStruct(ext, ['alias', 'name'])
+ @test.attr(type='smoke')
def test_neutron_dhcp_agent_list_hosting_net(self):
self.neutron('dhcp-agent-list-hosting-net',
params=CONF.compute.fixed_network_name)
+ @test.attr(type='smoke')
def test_neutron_agent_list(self):
agents = self.parser.listing(self.neutron('agent-list'))
field_names = ['id', 'agent_type', 'host', 'alive', 'admin_state_up']
self.assertTableStruct(agents, field_names)
+ @test.attr(type='smoke')
def test_neutron_floatingip_list(self):
self.neutron('floatingip-list')
@test.skip_because(bug="1240694")
+ @test.attr(type='smoke')
def test_neutron_meter_label_list(self):
self.neutron('meter-label-list')
@test.skip_because(bug="1240694")
+ @test.attr(type='smoke')
def test_neutron_meter_label_rule_list(self):
self.neutron('meter-label-rule-list')
@@ -83,40 +91,52 @@
if '404 Not Found' not in e.stderr:
self.fail('%s: Unexpected failure.' % command)
+ @test.attr(type='smoke')
def test_neutron_lb_healthmonitor_list(self):
self._test_neutron_lbaas_command('lb-healthmonitor-list')
+ @test.attr(type='smoke')
def test_neutron_lb_member_list(self):
self._test_neutron_lbaas_command('lb-member-list')
+ @test.attr(type='smoke')
def test_neutron_lb_pool_list(self):
self._test_neutron_lbaas_command('lb-pool-list')
+ @test.attr(type='smoke')
def test_neutron_lb_vip_list(self):
self._test_neutron_lbaas_command('lb-vip-list')
+ @test.attr(type='smoke')
def test_neutron_net_external_list(self):
self.neutron('net-external-list')
+ @test.attr(type='smoke')
def test_neutron_port_list(self):
self.neutron('port-list')
+ @test.attr(type='smoke')
def test_neutron_quota_list(self):
self.neutron('quota-list')
+ @test.attr(type='smoke')
def test_neutron_router_list(self):
self.neutron('router-list')
+ @test.attr(type='smoke')
def test_neutron_security_group_list(self):
security_grp = self.parser.listing(self.neutron('security-group-list'))
self.assertTableStruct(security_grp, ['id', 'name', 'description'])
+ @test.attr(type='smoke')
def test_neutron_security_group_rule_list(self):
self.neutron('security-group-rule-list')
+ @test.attr(type='smoke')
def test_neutron_subnet_list(self):
self.neutron('subnet-list')
+ @test.attr(type='smoke')
def test_neutron_help(self):
help_text = self.neutron('help')
lines = help_text.split('\n')
@@ -136,11 +156,14 @@
# Optional arguments:
+ @test.attr(type='smoke')
def test_neutron_version(self):
self.neutron('', flags='--version')
+ @test.attr(type='smoke')
def test_neutron_debug_net_list(self):
self.neutron('net-list', flags='--debug')
+ @test.attr(type='smoke')
def test_neutron_quiet_net_list(self):
self.neutron('net-list', flags='--quiet')
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index fa46cd7..e839d20 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -628,7 +628,15 @@
self.set_resource(data_utils.rand_name('floatingip-'), floating_ip)
return floating_ip
- def _ping_ip_address(self, ip_address):
+ def _disassociate_floating_ip(self, floating_ip):
+ """
+ :param floating_ip: type DeletableFloatingIp
+ """
+ floating_ip.update(port_id=None)
+ self.assertEqual(None, floating_ip.port_id)
+ return floating_ip
+
+ def _ping_ip_address(self, ip_address, should_succeed=True):
cmd = ['ping', '-c1', '-w1', ip_address]
def ping():
@@ -636,8 +644,7 @@
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
proc.wait()
- if proc.returncode == 0:
- return True
+ return (proc.returncode == 0) == should_succeed
return tempest.test.call_until_true(
ping, self.config.compute.ping_timeout, 1)
@@ -649,17 +656,37 @@
timeout=timeout)
return ssh_client.test_connection_auth()
- def _check_vm_connectivity(self, ip_address, username, private_key):
- self.assertTrue(self._ping_ip_address(ip_address),
- "Timed out waiting for %s to become "
- "reachable" % ip_address)
- self.assertTrue(self._is_reachable_via_ssh(
- ip_address,
- username,
- private_key,
- timeout=self.config.compute.ssh_timeout),
- 'Auth failure in connecting to %s@%s via ssh' %
- (username, ip_address))
+ def _check_vm_connectivity(self, ip_address,
+ username=None,
+ private_key=None,
+ should_connect=True):
+ """
+ :param ip_address: server to test against
+ :param username: server's ssh username
+ :param private_key: server's ssh private key to be used
+ :param should_connect: True/False indicates positive/negative test
+ positive - attempt ping and ssh
+ negative - attempt ping and fail if succeed
+
+ :raises: AssertError if the result of the connectivity check does
+ not match the value of the should_connect param
+ """
+ if should_connect:
+ msg = "Timed out waiting for %s to become reachable" % ip_address
+ else:
+ msg = "ip address %s is reachable" % ip_address
+ self.assertTrue(self._ping_ip_address(ip_address,
+ should_succeed=should_connect),
+ msg=msg)
+ if should_connect:
+ # no need to check ssh for negative connectivity
+ self.assertTrue(self._is_reachable_via_ssh(
+ ip_address,
+ username,
+ private_key,
+ timeout=self.config.compute.ssh_timeout),
+ 'Auth failure in connecting to %s@%s via ssh' %
+ (username, ip_address))
def _create_security_group_nova(self, client=None,
namestart='secgroup-smoke-',
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 1418b75..d605dff 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -41,10 +41,7 @@
def __init__(self, compute_client, floating_ip_map):
self.compute_client = compute_client
- self.unchecked = {}
- for k in floating_ip_map.keys():
- self.unchecked[k] = [f.floating_ip_address
- for f in floating_ip_map[k]]
+ self.unchecked = floating_ip_map.copy()
def run_checks(self):
"""Check for any remaining unverified floating IPs
@@ -56,16 +53,14 @@
"""
to_delete = []
loggable_map = {}
- for k, check_addrs in self.unchecked.iteritems():
- serverdata = self.compute_client.servers.get(k.id)
- for net_name, ip_addr in serverdata.networks.iteritems():
- for addr in ip_addr:
- if addr in check_addrs:
- check_addrs.remove(addr)
- if len(check_addrs) == 0:
- to_delete.append(k)
+ for check_addr, server in self.unchecked.iteritems():
+ serverdata = self.compute_client.servers.get(server.id)
+ ip_addr = [addr for sublist in serverdata.networks.values() for
+ addr in sublist]
+ if check_addr.floating_ip_address in ip_addr:
+ to_delete.append(check_addr)
else:
- loggable_map[k.id] = check_addrs
+ loggable_map[server.id] = check_addr
for to_del in to_delete:
del self.unchecked[to_del]
@@ -93,6 +88,9 @@
ssh server hosted at the IP address. This check guarantees
that the IP address is associated with the target VM.
+ - detach the floating-ip from the VM and verify that it becomes
+ unreachable
+
# TODO(mnewby) - Need to implement the following:
- the Tempest host can ssh into the VM via the IP address and
successfully execute the following:
@@ -297,30 +295,34 @@
"Timed out while waiting for the floating IP assignments "
"to propagate")
- def _assign_floating_ips(self):
+ def _create_and_associate_floating_ips(self):
public_network_id = self.config.network.public_network_id
for server in self.servers:
floating_ip = self._create_floating_ip(server, public_network_id)
- self.floating_ips.setdefault(server, [])
- self.floating_ips[server].append(floating_ip)
+ self.floating_ips[floating_ip] = server
- def _check_public_network_connectivity(self):
+ def _check_public_network_connectivity(self, should_connect=True):
# The target login is assumed to have been configured for
# key-based authentication by cloud-init.
ssh_login = self.config.compute.image_ssh_user
private_key = self.keypairs[self.tenant_id].private_key
try:
- for server, floating_ips in self.floating_ips.iteritems():
- for floating_ip in floating_ips:
- ip_address = floating_ip.floating_ip_address
- self._check_vm_connectivity(ip_address,
- ssh_login,
- private_key)
+ for floating_ip, server in self.floating_ips.iteritems():
+ ip_address = floating_ip.floating_ip_address
+ self._check_vm_connectivity(ip_address,
+ ssh_login,
+ private_key,
+ should_connect=should_connect)
except Exception as exc:
LOG.exception(exc)
debug.log_ip_ns()
raise exc
+ def _disassociate_floating_ips(self):
+ for floating_ip, server in self.floating_ips.iteritems():
+ self._disassociate_floating_ip(floating_ip)
+ self.floating_ips[floating_ip] = None
+
@attr(type='smoke')
@services('compute', 'network')
def test_network_basic_ops(self):
@@ -329,7 +331,9 @@
self._create_networks()
self._check_networks()
self._create_servers()
- self._assign_floating_ips()
+ self._create_and_associate_floating_ips()
self._wait_for_floating_ip_association()
self._check_tenant_network_connectivity()
- self._check_public_network_connectivity()
+ self._check_public_network_connectivity(should_connect=True)
+ self._disassociate_floating_ips()
+ self._check_public_network_connectivity(should_connect=False)
diff --git a/tox.ini b/tox.ini
index 1d7e1b7..c7f92ae 100644
--- a/tox.ini
+++ b/tox.ini
@@ -12,12 +12,15 @@
install_command = pip install -U {opts} {packages}
[testenv:py26]
+setenv = OS_TEST_PATH=./tempest/tests
commands = python setup.py test --slowest --testr-arg='tempest\.tests {posargs}'
[testenv:py33]
+setenv = OS_TEST_PATH=./tempest/tests
commands = python setup.py test --slowest --testr-arg='tempest\.tests {posargs}'
[testenv:py27]
+setenv = OS_TEST_PATH=./tempest/tests
commands = python setup.py test --slowest --testr-arg='tempest\.tests {posargs}'
[testenv:all]