Add wait for delete method
Add wait for delete method to resource and add two proxy methods to wait for delete for servers and stacks. Change-Id: Iaff65457e94ffd5f64f5951f0c2ba211f4f06bdd
This commit is contained in:
@@ -14,7 +14,6 @@ from openstack import exceptions
|
||||
from openstack.orchestration.v1 import resource as stack_resource
|
||||
from openstack.orchestration.v1 import stack
|
||||
from openstack import proxy
|
||||
from openstack import resource
|
||||
|
||||
|
||||
class Proxy(proxy.BaseProxy):
|
||||
@@ -84,11 +83,6 @@ class Proxy(proxy.BaseProxy):
|
||||
"""
|
||||
self._delete(stack.Stack, value, ignore_missing=ignore_missing)
|
||||
|
||||
def wait_for_stack(self, value, status='CREATE_COMPLETE',
|
||||
failures=['CREATE_FAILED'], interval=2, wait=120):
|
||||
return resource.wait_for_status(self.session, value, status,
|
||||
failures, interval, wait)
|
||||
|
||||
def resources(self, value, **query):
|
||||
"""Return a generator of resources
|
||||
|
||||
|
||||
@@ -204,3 +204,41 @@ class BaseProxy(object):
|
||||
res = self._get_resource(resource_type, value, path_args)
|
||||
|
||||
return res.head(self.session)
|
||||
|
||||
def wait_for_status(self, value, status, failures=[], interval=2,
|
||||
wait=120):
|
||||
"""Wait for a resource to be in a particular status.
|
||||
|
||||
:param value: The resource to wait on to reach the status. The
|
||||
resource must have a status attribute.
|
||||
:type value: :class:`~openstack.resource.Resource`
|
||||
:param status: Desired status of the resource.
|
||||
:param list failures: Statuses that would indicate the transition
|
||||
failed such as 'ERROR'.
|
||||
:param interval: Number of seconds to wait between checks.
|
||||
:param wait: Maximum number of seconds to wait for the change.
|
||||
|
||||
:return: Method returns resource on success.
|
||||
:raises: :class:`~openstack.exceptions.ResourceTimeout` transition
|
||||
to status failed to occur in wait seconds.
|
||||
:raises: :class:`~openstack.exceptions.ResourceFailure` resource
|
||||
transitioned to one of the failure states.
|
||||
:raises: :class:`~AttributeError` if the resource does not have a
|
||||
status attribute
|
||||
"""
|
||||
return resource.wait_for_status(self.session, value, status,
|
||||
failures, interval, wait)
|
||||
|
||||
def wait_for_delete(self, value, interval=2, wait=120):
|
||||
"""Wait for the resource to be deleted.
|
||||
|
||||
:param value: The resource to wait on to be deleted.
|
||||
:type value: :class:`~openstack.resource.Resource`
|
||||
:param interval: Number of seconds to wait between checks.
|
||||
:param wait: Maximum number of seconds to wait for the delete.
|
||||
|
||||
:return: Method returns resource on success.
|
||||
:raises: :class:`~openstack.exceptions.ResourceTimeout` transition
|
||||
to delete failed to occur in wait seconds.
|
||||
"""
|
||||
return resource.wait_for_delete(self.session, value, interval, wait)
|
||||
|
||||
@@ -936,15 +936,14 @@ class Resource(collections.MutableMapping):
|
||||
"No %s found for %s" % (cls.__name__, name_or_id))
|
||||
|
||||
|
||||
def wait_for_status(session, resource, status=None, failures=None,
|
||||
interval=5, wait=120):
|
||||
def wait_for_status(session, resource, status, failures, interval, wait):
|
||||
"""Wait for the resource to be in a particular status.
|
||||
|
||||
:param session: The session to use for making this request.
|
||||
:type session: :class:`~openstack.session.Session`
|
||||
:param resource: The resource to wait on to reach the status. The resource
|
||||
must have a status attribute.
|
||||
:type resource: :class:`~openstack.resource.Resource`
|
||||
:param session: The session to use for making this request.
|
||||
:type session: :class:`~openstack.session.Session`
|
||||
:param status: Desired status of the resource.
|
||||
:param list failures: Statuses that would indicate the transition
|
||||
failed such as 'ERROR'.
|
||||
@@ -978,3 +977,29 @@ def wait_for_status(session, resource, status=None, failures=None,
|
||||
total_sleep += interval
|
||||
msg = "Timeout waiting for %s to transition to %s" % (resource.id, status)
|
||||
raise exceptions.ResourceTimeout(msg)
|
||||
|
||||
|
||||
def wait_for_delete(session, resource, interval, wait):
|
||||
"""Wait for the resource to be deleted.
|
||||
|
||||
:param session: The session to use for making this request.
|
||||
:type session: :class:`~openstack.session.Session`
|
||||
:param resource: The resource to wait on to be deleted.
|
||||
:type resource: :class:`~openstack.resource.Resource`
|
||||
:param interval: Number of seconds to wait between checks.
|
||||
:param wait: Maximum number of seconds to wait for the delete.
|
||||
|
||||
:return: Method returns self on success.
|
||||
:raises: :class:`~openstack.exceptions.ResourceTimeout` transition
|
||||
to status failed to occur in wait seconds.
|
||||
"""
|
||||
total_sleep = 0
|
||||
while total_sleep < wait:
|
||||
try:
|
||||
resource.get(session)
|
||||
except exceptions.NotFoundException:
|
||||
return resource
|
||||
time.sleep(interval)
|
||||
total_sleep += interval
|
||||
msg = "Timeout waiting for %s delete" % (resource.id)
|
||||
raise exceptions.ResourceTimeout(msg)
|
||||
|
||||
@@ -81,19 +81,6 @@ class BaseFunctionalTest(unittest.TestCase):
|
||||
if expected != actual:
|
||||
raise Exception(expected + ' != ' + actual)
|
||||
|
||||
# TODO(thowe): This should probably be logic moved info the proxy or
|
||||
# or the resource, but for now I just want to get the functional tests
|
||||
# working again.
|
||||
@classmethod
|
||||
def wait_for_delete(cls, proxy_find, name_or_id, interval=2, linger=15,
|
||||
wait=60):
|
||||
total_sleep = 0
|
||||
while total_sleep < wait:
|
||||
if proxy_find(name_or_id) is None:
|
||||
# Wait a little longer for the vestiges of the deleted object
|
||||
time.sleep(linger)
|
||||
return True
|
||||
print('waiting for delete ' + name_or_id)
|
||||
time.sleep(interval)
|
||||
total_sleep += interval
|
||||
return False
|
||||
def linger_for_delete(cls):
|
||||
time.sleep(40)
|
||||
|
||||
@@ -20,7 +20,7 @@ from openstack.tests.functional.network.v2 import test_network
|
||||
class TestServer(base.BaseFunctionalTest):
|
||||
|
||||
NAME = uuid.uuid4().hex
|
||||
ID = None
|
||||
server = None
|
||||
network = None
|
||||
subnet = None
|
||||
|
||||
@@ -40,24 +40,25 @@ class TestServer(base.BaseFunctionalTest):
|
||||
name=cls.NAME, flavor=flavor, image=image.id, **args)
|
||||
assert isinstance(sot, server.Server)
|
||||
cls.assertIs(cls.NAME, sot.name)
|
||||
cls.ID = sot.id
|
||||
cls.server = sot
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
sot = cls.conn.compute.delete_server(cls.ID)
|
||||
sot = cls.conn.compute.delete_server(cls.server.id)
|
||||
cls.assertIs(None, sot)
|
||||
# Need to wait for the stack to go away before network delete
|
||||
cls.wait_for_delete(cls.conn.compute.find_server, cls.ID)
|
||||
cls.conn.compute.wait_for_delete(cls.server)
|
||||
cls.linger_for_delete()
|
||||
test_network.delete_network(cls.conn, cls.network, cls.subnet)
|
||||
|
||||
def test_find(self):
|
||||
sot = self.conn.compute.find_server(self.NAME)
|
||||
self.assertEqual(self.ID, sot.id)
|
||||
self.assertEqual(self.server.id, sot.id)
|
||||
|
||||
def test_get(self):
|
||||
sot = self.conn.compute.get_server(self.ID)
|
||||
sot = self.conn.compute.get_server(self.server.id)
|
||||
self.assertEqual(self.NAME, sot.name)
|
||||
self.assertEqual(self.ID, sot.id)
|
||||
self.assertEqual(self.server.id, sot.id)
|
||||
|
||||
def test_list(self):
|
||||
names = [o.name for o in self.conn.compute.servers()]
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from openstack import exceptions
|
||||
from openstack.orchestration.v1 import stack
|
||||
from openstack.tests.functional import base
|
||||
from openstack.tests.functional.network.v2 import test_network
|
||||
@@ -18,7 +19,7 @@ from openstack.tests.functional.network.v2 import test_network
|
||||
class TestStack(base.BaseFunctionalTest):
|
||||
|
||||
NAME = 'test_stack'
|
||||
ID = None
|
||||
stack = None
|
||||
network = None
|
||||
subnet = None
|
||||
|
||||
@@ -45,17 +46,23 @@ class TestStack(base.BaseFunctionalTest):
|
||||
)
|
||||
assert isinstance(sot, stack.Stack)
|
||||
cls.assertIs(True, (sot.id is not None))
|
||||
cls.ID = sot.id
|
||||
cls.stack = sot
|
||||
cls.assertIs(cls.NAME, sot.name)
|
||||
cls.conn.orchestration.wait_for_stack(sot)
|
||||
cls.conn.orchestration.wait_for_status(
|
||||
sot, status='CREATE_COMPLETE', failures=['CREATE_FAILED'])
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
super(TestStack, cls).tearDownClass()
|
||||
cls.conn.orchestration.delete_stack(cls.ID, ignore_missing=False)
|
||||
cls.conn.orchestration.delete_stack(cls.stack, ignore_missing=False)
|
||||
cls.conn.compute.delete_keypair(cls.NAME)
|
||||
# Need to wait for the stack to go away before network delete
|
||||
cls.wait_for_delete(cls.conn.orchestration.find_stack, cls.ID)
|
||||
try:
|
||||
cls.conn.orchestration.wait_for_status(
|
||||
cls.stack, 'DELETE_COMPLETE')
|
||||
except exceptions.NotFoundException:
|
||||
pass
|
||||
cls.linger_for_delete()
|
||||
test_network.delete_network(cls.conn, cls.network, cls.subnet)
|
||||
|
||||
def test_list(self):
|
||||
|
||||
@@ -188,10 +188,3 @@ class TestComputeProxy(test_proxy_base.TestProxyBase):
|
||||
|
||||
def test_server_update(self):
|
||||
self.verify_update(self.proxy.update_server, server.Server)
|
||||
|
||||
def test_server_wait_for(self):
|
||||
value = server.Server(attrs={'id': '1234'})
|
||||
self.verify_wait_for_status(
|
||||
self.proxy.wait_for_server,
|
||||
method_args=[value],
|
||||
expected_args=[value, 'ACTIVE', ['ERROR'], 2, 120])
|
||||
|
||||
@@ -44,14 +44,6 @@ class TestOrchestrationProxy(test_proxy_base.TestProxyBase):
|
||||
def test_stack_delete_ignore(self):
|
||||
self.verify_delete(self.proxy.delete_stack, stack.Stack, True)
|
||||
|
||||
def test_stack_wait_for(self):
|
||||
value = stack.Stack(attrs={'id': '1234'})
|
||||
self.verify_wait_for_status(
|
||||
self.proxy.wait_for_stack,
|
||||
method_args=[value],
|
||||
expected_args=[value, 'CREATE_COMPLETE', ['CREATE_FAILED'],
|
||||
2, 120])
|
||||
|
||||
@mock.patch.object(stack.Stack, 'from_id')
|
||||
@mock.patch.object(stack.Stack, 'find')
|
||||
def test_resources_with_stack_object(self, mock_find, mock_from):
|
||||
|
||||
@@ -313,3 +313,35 @@ class TestProxyHead(testtools.TestCase):
|
||||
|
||||
MockHeadResource.assert_called_with()
|
||||
instance.head.assert_called_with(self.session)
|
||||
|
||||
@mock.patch("openstack.resource.wait_for_status")
|
||||
def test_wait_for(self, mock_wait):
|
||||
mock_resource = mock.Mock()
|
||||
mock_wait.return_value = mock_resource
|
||||
self.sot.wait_for_status(mock_resource, 'ACTIVE')
|
||||
mock_wait.assert_called_once_with(
|
||||
self.session, mock_resource, 'ACTIVE', [], 2, 120)
|
||||
|
||||
@mock.patch("openstack.resource.wait_for_status")
|
||||
def test_wait_for_params(self, mock_wait):
|
||||
mock_resource = mock.Mock()
|
||||
mock_wait.return_value = mock_resource
|
||||
self.sot.wait_for_status(mock_resource, 'ACTIVE', ['ERROR'], 1, 2)
|
||||
mock_wait.assert_called_once_with(
|
||||
self.session, mock_resource, 'ACTIVE', ['ERROR'], 1, 2)
|
||||
|
||||
@mock.patch("openstack.resource.wait_for_delete")
|
||||
def test_wait_for_delete(self, mock_wait):
|
||||
mock_resource = mock.Mock()
|
||||
mock_wait.return_value = mock_resource
|
||||
self.sot.wait_for_delete(mock_resource)
|
||||
mock_wait.assert_called_once_with(
|
||||
self.session, mock_resource, 2, 120)
|
||||
|
||||
@mock.patch("openstack.resource.wait_for_delete")
|
||||
def test_wait_for_delete_params(self, mock_wait):
|
||||
mock_resource = mock.Mock()
|
||||
mock_wait.return_value = mock_resource
|
||||
self.sot.wait_for_delete(mock_resource, 1, 2)
|
||||
mock_wait.assert_called_once_with(
|
||||
self.session, mock_resource, 1, 2)
|
||||
|
||||
@@ -1316,3 +1316,22 @@ class TestWaitForStatus(base.TestCase):
|
||||
|
||||
self.assertRaises(AttributeError, resource.wait_for_status,
|
||||
self.sess, sot, 'ACTIVE', ['ERROR'], 1, 2)
|
||||
|
||||
|
||||
class TestWaitForDelete(base.TestCase):
|
||||
|
||||
def test_wait_for_delete(self):
|
||||
sess = mock.Mock()
|
||||
sot = FakeResource.new(**fake_data)
|
||||
sot.get = mock.MagicMock()
|
||||
sot.get.side_effect = [sot, exceptions.NotFoundException(mock.Mock())]
|
||||
|
||||
self.assertEqual(sot, resource.wait_for_delete(sess, sot, 1, 2))
|
||||
|
||||
def test_wait_for_delete_fail(self):
|
||||
sess = mock.Mock()
|
||||
sot = FakeResource.new(**fake_data)
|
||||
sot.get = mock.MagicMock(return_value=sot)
|
||||
|
||||
self.assertRaises(exceptions.ResourceTimeout, resource.wait_for_delete,
|
||||
sess, sot, 1, 2)
|
||||
|
||||
Reference in New Issue
Block a user