diff --git a/heat_integrationtests/common/test_resources/test_resource.py b/heat_integrationtests/common/test_resources/test_resource.py index 55255a6e14..92b09f1f46 100644 --- a/heat_integrationtests/common/test_resources/test_resource.py +++ b/heat_integrationtests/common/test_resources/test_resource.py @@ -11,7 +11,10 @@ # License for the specific language governing permissions and limitations # under the License. +import datetime import eventlet +from oslo_utils import timeutils +import six from heat.common.i18n import _ from heat.engine import attributes @@ -34,10 +37,19 @@ class TestResource(resource.Resource): support_status = support.SupportStatus(version='2014.1') - PROPERTIES = ( - VALUE, UPDATE_REPLACE, FAIL, WAIT_SECS + ACTION_TIMES = ( + CREATE_WAIT_SECS, UPDATE_WAIT_SECS, DELETE_WAIT_SECS ) = ( - 'value', 'update_replace', 'fail', 'wait_secs' + 'create', 'update', 'delete') + + PROPERTIES = ( + VALUE, UPDATE_REPLACE, FAIL, + CLIENT_NAME, ENTITY_NAME, + WAIT_SECS, ACTION_WAIT_SECS + ) = ( + 'value', 'update_replace', 'fail', + 'client_name', 'entity_name', + 'wait_secs', 'action_wait_secs' ) ATTRIBUTES = ( @@ -69,11 +81,47 @@ class TestResource(resource.Resource): ), WAIT_SECS: properties.Schema( properties.Schema.NUMBER, - _('Value which can be set for resource to wait after an action ' - 'is performed.'), + _('Seconds to wait after an action (-1 is infinite)'), update_allowed=True, default=0, ), + ACTION_WAIT_SECS: properties.Schema( + properties.Schema.MAP, + _('Options for simulating waiting.'), + update_allowed=True, + schema={ + CREATE_WAIT_SECS: properties.Schema( + properties.Schema.NUMBER, + _('Seconds to wait after a create. ' + 'Defaults to the global wait_secs'), + update_allowed=True, + ), + UPDATE_WAIT_SECS: properties.Schema( + properties.Schema.NUMBER, + _('Seconds to wait after an update. ' + 'Defaults to the global wait_secs'), + update_allowed=True, + ), + DELETE_WAIT_SECS: properties.Schema( + properties.Schema.NUMBER, + _('Seconds to wait after a delete. ' + 'Defaults to the global wait_secs'), + update_allowed=True, + ), + } + ), + CLIENT_NAME: properties.Schema( + properties.Schema.STRING, + _('Client to poll.'), + default='', + update_allowed=True + ), + ENTITY_NAME: properties.Schema( + properties.Schema.STRING, + _('Client entity to poll.'), + default='', + update_allowed=True + ), } attributes_schema = { @@ -84,44 +132,88 @@ class TestResource(resource.Resource): ), } + def _wait_secs(self): + secs = None + if self.properties[self.ACTION_WAIT_SECS]: + secs = self.properties[self.ACTION_WAIT_SECS][self.action.lower()] + if secs is None: + secs = self.properties[self.WAIT_SECS] + LOG.info('%s wait_secs:%s, action:%s' % (self.name, secs, + self.action.lower())) + return secs + def handle_create(self): - value = self.properties.get(self.VALUE) fail_prop = self.properties.get(self.FAIL) - sleep_secs = self.properties.get(self.WAIT_SECS) + if not fail_prop: + value = self.properties.get(self.VALUE) + self.data_set('value', value, redact=False) + self.resource_id_set(self.physical_resource_name()) - self.data_set('value', value, redact=False) - self.resource_id_set(self.physical_resource_name()) - - # sleep for specified time - if sleep_secs: - LOG.debug("Resource %s sleeping for %s seconds", - self.name, sleep_secs) - eventlet.sleep(sleep_secs) - - # emulate failure - if fail_prop: - raise ValueError("Test Resource failed %s" % self.name) + return timeutils.utcnow(), self._wait_secs() def handle_update(self, json_snippet=None, tmpl_diff=None, prop_diff=None): + self.properties = json_snippet.properties(self.properties_schema, + self.context) value = prop_diff.get(self.VALUE) - new_prop = json_snippet._properties if value: - update_replace = new_prop.get(self.UPDATE_REPLACE, False) + update_replace = self.properties[self.UPDATE_REPLACE] if update_replace: raise resource.UpdateReplace(self.name) else: - fail_prop = new_prop.get(self.FAIL, False) - sleep_secs = new_prop.get(self.WAIT_SECS, 0) # emulate failure - if fail_prop: - raise Exception("Test Resource failed %s", self.name) - # update in place - self.data_set('value', value, redact=False) + fail_prop = self.properties[self.FAIL] + if not fail_prop: + # update in place + self.data_set('value', value, redact=False) + return timeutils.utcnow(), self._wait_secs() + return timeutils.utcnow(), 0 - if sleep_secs: - LOG.debug("Update of Resource %s sleeping for %s seconds", - self.name, sleep_secs) - eventlet.sleep(sleep_secs) + def handle_delete(self): + return timeutils.utcnow(), self._wait_secs() + + def check_create_complete(self, cookie): + return self._check_status_complete(*cookie) + + def check_update_complete(self, cookie): + return self._check_status_complete(*cookie) + + def check_delete_complete(self, cookie): + return self._check_status_complete(*cookie) + + def _check_status_complete(self, started_at, wait_secs): + def simulated_effort(): + client_name = self.properties[self.CLIENT_NAME] + self.entity = self.properties[self.ENTITY_NAME] + if client_name and self.entity: + # Allow the user to set the value to a real resource id. + entity_id = self.data().get('value') or self.resource_id + try: + obj = getattr(self.client(name=client_name), self.entity) + obj.get(entity_id) + except Exception as exc: + LOG.debug('%s.%s(%s) %s' % (client_name, self.entity, + entity_id, six.text_type(exc))) + else: + # just sleep some more + eventlet.sleep(1) + + if isinstance(started_at, six.string_types): + started_at = timeutils.parse_isotime(started_at) + + started_at = timeutils.normalize_time(started_at) + waited = timeutils.utcnow() - started_at + LOG.info("Resource %s waited %s/%s seconds", + self.name, waited, wait_secs) + + # wait_secs < 0 is an infinite wait time. + if wait_secs >= 0 and waited > datetime.timedelta(seconds=wait_secs): + fail_prop = self.properties[self.FAIL] + if fail_prop and self.action != self.DELETE: + raise ValueError("Test Resource failed %s" % self.name) + return True + + simulated_effort() + return False def _resolve_attribute(self, name): if name == self.OUTPUT: diff --git a/heat_integrationtests/functional/test_create_update.py b/heat_integrationtests/functional/test_create_update.py index 87d7a23613..b2aad34ef8 100644 --- a/heat_integrationtests/functional/test_create_update.py +++ b/heat_integrationtests/functional/test_create_update.py @@ -26,7 +26,10 @@ test_template_one_resource = { 'value': 'Test1', 'fail': False, 'update_replace': False, - 'wait_secs': 0 + 'wait_secs': 0, + 'action_wait_secs': {'create': 1}, + 'client_name': 'nova', + 'entity_name': 'servers', } } } @@ -42,7 +45,8 @@ test_template_two_resource = { 'value': 'Test1', 'fail': False, 'update_replace': False, - 'wait_secs': 0 + 'wait_secs': 0, + 'action_wait_secs': {'update': 1} } }, 'test2': {