Merge "Wait for all instance ports to become ACTIVE"
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index e249f35..9e97f47 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -672,6 +672,28 @@
raise lib_exc.TimeoutException
+def wait_for_server_ports_active(client, server_id, is_active, **kwargs):
+ """Wait for all server ports to reach active status
+ :param client: The network client to use when querying the port's status
+ :param server_id: The uuid of the server's ports we need to verify.
+ :param is_active: A function to call to the check port active status.
+ :param kwargs: Additional arguments, if any, to pass to list_ports()
+ """
+ start_time = time.time()
+ while (time.time() - start_time <= client.build_timeout):
+ ports = client.list_ports(device_id=server_id, **kwargs)['ports']
+ if all(is_active(port) for port in ports):
+ LOG.debug("Server ID %s ports are all ACTIVE %s: ",
+ server_id, ports)
+ return ports
+ LOG.warning("Server ID %s has ports that are not ACTIVE, waiting "
+ "for state to change on all: %s", server_id, ports)
+ time.sleep(client.build_interval)
+ LOG.error("Server ID %s ports have failed to transition to ACTIVE, "
+ "timing out: %s", server_id, ports)
+ raise lib_exc.TimeoutException
+
+
def wait_for_ssh(ssh_client, timeout=30):
"""Waits for SSH connection to become usable"""
start_time = int(time.time())
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 714a7c7..2b2ee99 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -1098,8 +1098,6 @@
if ip_addr and not kwargs.get('fixed_ips'):
kwargs['fixed_ips'] = 'ip_address=%s' % ip_addr
- ports = self.os_admin.ports_client.list_ports(
- device_id=server['id'], **kwargs)['ports']
# A port can have more than one IP address in some cases.
# If the network is dual-stack (IPv4 + IPv6), this port is associated
@@ -1114,6 +1112,18 @@
return (port['status'] == 'ACTIVE' or
port.get('binding:vnic_type') == 'baremetal')
+ # Wait for all compute ports to be ACTIVE.
+ # This will raise a TimeoutException if that does not happen.
+ client = self.os_admin.ports_client
+ try:
+ ports = waiters.wait_for_server_ports_active(
+ client=client, server_id=server['id'],
+ is_active=_is_active, **kwargs)
+ except lib_exc.TimeoutException:
+ LOG.error("Server ports failed transitioning to ACTIVE for "
+ "server: %s", server)
+ raise
+
port_map = [(p["id"], fxip["ip_address"])
for p in ports
for fxip in p["fixed_ips"]
@@ -1121,7 +1131,8 @@
_is_active(p))]
inactive = [p for p in ports if p['status'] != 'ACTIVE']
if inactive:
- LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
+ # This should just be Ironic ports, see _is_active() above
+ LOG.debug("Instance has ports that are not ACTIVE: %s", inactive)
self.assertNotEmpty(port_map,
"No IPv4 addresses found in: %s" % ports)
diff --git a/tempest/tests/common/test_waiters.py b/tempest/tests/common/test_waiters.py
index f194173..f7f2dc7 100755
--- a/tempest/tests/common/test_waiters.py
+++ b/tempest/tests/common/test_waiters.py
@@ -884,6 +884,58 @@
waiters.wait_for_port_status, mock_client,
fake_port_id, fake_status)
+ def test_wait_for_server_ports_active(self):
+ """Test that the waiter replies with the ports before the timeout"""
+
+ def is_active(port):
+ return port['status'] == 'ACTIVE'
+
+ def client_response(device_id):
+ """Mock client response, replies with partial status after one
+ call and final status after 2 calls
+ """
+ if mock_client.call_count >= 2:
+ return mock_ports_active
+ else:
+ mock_client.call_count += 1
+ if mock_client.call_count > 1:
+ return mock_ports_half_active
+ return mock_ports
+
+ mock_ports = {'ports': [{'id': '1234', 'status': 'DOWN'},
+ {'id': '5678', 'status': 'DOWN'}]}
+ mock_ports_half_active = {'ports': [{'id': '1234', 'status': 'ACTIVE'},
+ {'id': '5678', 'status': 'DOWN'}]}
+ mock_ports_active = {'ports': [{'id': '1234', 'status': 'ACTIVE'},
+ {'id': '5678', 'status': 'ACTIVE'}]}
+ mock_client = mock.Mock(
+ spec=ports_client.PortsClient,
+ build_timeout=30, build_interval=1,
+ list_ports=client_response)
+ fake_server_id = "9876"
+ self.assertEqual(mock_ports_active['ports'],
+ waiters.wait_for_server_ports_active(
+ mock_client, fake_server_id, is_active))
+
+ def test_wait_for_server_ports_active_timeout(self):
+ """Negative test - checking that a timeout
+ presented by a small 'fake_timeout' and a static status of
+ 'DOWN' in the mock will raise a timeout exception
+ """
+
+ def is_active(port):
+ return port['status'] == 'ACTIVE'
+
+ mock_ports = {'ports': [{'id': '1234', 'status': "DOWN"}]}
+ mock_client = mock.Mock(
+ spec=ports_client.PortsClient,
+ build_timeout=2, build_interval=1,
+ list_ports=lambda device_id: mock_ports)
+ fake_server_id = "9876"
+ self.assertRaises(lib_exc.TimeoutException,
+ waiters.wait_for_server_ports_active,
+ mock_client, fake_server_id, is_active)
+
class TestServerFloatingIPWaiters(base.TestCase):