Merge "set all build_intervals to 1s"
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 82438be..ed77b98 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -211,13 +211,14 @@
# admin credentials are known. (boolean value)
#allow_tenant_isolation=false
-# Valid primary image reference to be used in tests. (string
-# value)
-#image_ref={$IMAGE_ID}
+# Valid primary image reference to be used in tests. This is a
+# required option (string value)
+#image_ref=<None>
-# Valid secondary image reference to be used in tests. (string
-# value)
-#image_ref_alt={$IMAGE_ID_ALT}
+# Valid secondary image reference to be used in tests. This is
+# a required option, but if only one image is available
+# duplicate the value of image_ref above (string value)
+#image_ref_alt=<None>
# Valid primary flavor to use in tests. (string value)
#flavor_ref=1
diff --git a/tempest/api/orchestration/stacks/test_server_cfn_init.py b/tempest/api/orchestration/stacks/test_server_cfn_init.py
deleted file mode 100644
index 4b845b1..0000000
--- a/tempest/api/orchestration/stacks/test_server_cfn_init.py
+++ /dev/null
@@ -1,121 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import json
-import testtools
-
-from tempest.api.orchestration import base
-from tempest.common.utils import data_utils
-from tempest.common.utils.linux import remote_client
-from tempest import config
-from tempest import exceptions
-from tempest.openstack.common import log as logging
-from tempest import test
-
-CONF = config.CONF
-LOG = logging.getLogger(__name__)
-
-
-class ServerCfnInitTestJSON(base.BaseOrchestrationTest):
- existing_keypair = CONF.orchestration.keypair_name is not None
-
- @classmethod
- @test.safe_setup
- def setUpClass(cls):
- super(ServerCfnInitTestJSON, cls).setUpClass()
- if not CONF.orchestration.image_ref:
- raise cls.skipException("No image available to test")
- template = cls.load_template('cfn_init_signal')
- stack_name = data_utils.rand_name('heat')
- if CONF.orchestration.keypair_name:
- keypair_name = CONF.orchestration.keypair_name
- else:
- cls.keypair = cls._create_keypair()
- keypair_name = cls.keypair['name']
-
- # create the stack
- cls.stack_identifier = cls.create_stack(
- stack_name,
- template,
- parameters={
- 'key_name': keypair_name,
- 'flavor': CONF.orchestration.instance_type,
- 'image': CONF.orchestration.image_ref,
- 'network': cls._get_default_network()['id'],
- 'timeout': CONF.orchestration.build_timeout
- })
-
- @test.attr(type='slow')
- @testtools.skipIf(existing_keypair, 'Server ssh tests are disabled.')
- def test_can_log_into_created_server(self):
-
- sid = self.stack_identifier
- rid = 'SmokeServer'
-
- # wait for create to complete.
- self.client.wait_for_stack_status(sid, 'CREATE_COMPLETE')
-
- resp, body = self.client.get_resource(sid, rid)
- self.assertEqual('CREATE_COMPLETE', body['resource_status'])
-
- # fetch the IP address from servers client, since we can't get it
- # from the stack until stack create is complete
- resp, server = self.servers_client.get_server(
- body['physical_resource_id'])
-
- # Check that the user can authenticate with the generated password
- linux_client = remote_client.RemoteClient(server, 'ec2-user',
- pkey=self.keypair[
- 'private_key'])
- linux_client.validate_authentication()
-
- @test.attr(type='slow')
- def test_all_resources_created(self):
- sid = self.stack_identifier
- self.client.wait_for_resource_status(
- sid, 'WaitHandle', 'CREATE_COMPLETE')
- self.client.wait_for_resource_status(
- sid, 'SmokeSecurityGroup', 'CREATE_COMPLETE')
- self.client.wait_for_resource_status(
- sid, 'SmokeKeys', 'CREATE_COMPLETE')
- self.client.wait_for_resource_status(
- sid, 'CfnUser', 'CREATE_COMPLETE')
- self.client.wait_for_resource_status(
- sid, 'SmokeServer', 'CREATE_COMPLETE')
- try:
- self.client.wait_for_resource_status(
- sid, 'WaitCondition', 'CREATE_COMPLETE')
- except (exceptions.StackResourceBuildErrorException,
- exceptions.TimeoutException) as e:
- # attempt to log the server console to help with debugging
- # the cause of the server not signalling the waitcondition
- # to heat.
- resp, body = self.client.get_resource(sid, 'SmokeServer')
- server_id = body['physical_resource_id']
- LOG.debug('Console output for %s', server_id)
- resp, output = self.servers_client.get_console_output(
- server_id, None)
- LOG.debug(output)
- raise e
-
- # wait for create to complete.
- self.client.wait_for_stack_status(sid, 'CREATE_COMPLETE')
-
- # This is an assert of great significance, as it means the following
- # has happened:
- # - cfn-init read the provided metadata and wrote out a file
- # - a user was created and credentials written to the server
- # - a cfn-signal was built which was signed with provided credentials
- # - the wait condition was fulfilled and the stack has changed state
- wait_status = json.loads(
- self.get_stack_output(sid, 'WaitConditionStatus'))
- self.assertEqual('smoke test complete', wait_status['00000'])
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 4d11d24..67d0203 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -109,6 +109,7 @@
cls.backups_client = cls.os.backups_client
cls.volume_services_client = cls.os.volume_services_client
cls.volumes_extension_client = cls.os.volumes_extension_client
+ cls.availability_zone_client = cls.os.volume_availability_zone_client
@classmethod
def create_volume(cls, size=1, **kwargs):
diff --git a/tempest/api/volume/test_availability_zone.py b/tempest/api/volume/test_availability_zone.py
new file mode 100644
index 0000000..1db7b7b
--- /dev/null
+++ b/tempest/api/volume/test_availability_zone.py
@@ -0,0 +1,40 @@
+# Copyright 2014 NEC Corporation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.volume import base
+from tempest import test
+
+
+class AvailabilityZoneTestJSON(base.BaseVolumeV1Test):
+
+ """
+ Tests Availability Zone API List
+ """
+
+ @classmethod
+ def setUpClass(cls):
+ super(AvailabilityZoneTestJSON, cls).setUpClass()
+ cls.client = cls.availability_zone_client
+
+ @test.attr(type='gate')
+ def test_get_availability_zone_list(self):
+ # List of availability zone
+ resp, availability_zone = self.client.get_availability_zone_list()
+ self.assertEqual(200, resp.status)
+ self.assertTrue(len(availability_zone) > 0)
+
+
+class AvailabilityZoneTestXML(AvailabilityZoneTestJSON):
+ _interface = 'xml'
diff --git a/tempest/api_schema/compute/v2/security_groups.py b/tempest/api_schema/compute/v2/security_groups.py
index 8b4bead..9a852e5 100644
--- a/tempest/api_schema/compute/v2/security_groups.py
+++ b/tempest/api_schema/compute/v2/security_groups.py
@@ -80,6 +80,10 @@
}
}
+delete_security_group = {
+ 'status_code': [202]
+}
+
create_security_group_rule = {
'status_code': [200],
'response_body': {
diff --git a/tempest/clients.py b/tempest/clients.py
index 37049d6..7745d54 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -168,6 +168,8 @@
VolumesServicesClientJSON
from tempest.services.volume.json.admin.volume_types_client import \
VolumeTypesClientJSON
+from tempest.services.volume.json.availability_zone_client import \
+ VolumeAvailabilityZoneClientJSON
from tempest.services.volume.json.backups_client import BackupsClientJSON
from tempest.services.volume.json.extensions_client import \
ExtensionsClientJSON as VolumeExtensionClientJSON
@@ -183,6 +185,8 @@
VolumesServicesClientXML
from tempest.services.volume.xml.admin.volume_types_client import \
VolumeTypesClientXML
+from tempest.services.volume.xml.availability_zone_client import \
+ VolumeAvailabilityZoneClientXML
from tempest.services.volume.xml.backups_client import BackupsClientXML
from tempest.services.volume.xml.extensions_client import \
ExtensionsClientXML as VolumeExtensionClientXML
@@ -262,6 +266,8 @@
self.auth_provider)
self.token_client = TokenClientXML()
self.token_v3_client = V3TokenClientXML()
+ self.volume_availability_zone_client = \
+ VolumeAvailabilityZoneClientXML(self.auth_provider)
elif self.interface == 'json':
self.certificates_client = CertificatesClientJSON(
@@ -358,6 +364,8 @@
self.negative_client = rest_client.NegativeRestClient(
self.auth_provider)
self.negative_client.service = service
+ self.volume_availability_zone_client = \
+ VolumeAvailabilityZoneClientJSON(self.auth_provider)
else:
msg = "Unsupported interface type `%s'" % interface
diff --git a/tempest/config.py b/tempest/config.py
index 5f340e3..9546f6c 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -137,11 +137,12 @@
"better parallel execution, but also requires that "
"OpenStack Identity API admin credentials are known."),
cfg.StrOpt('image_ref',
- default="{$IMAGE_ID}",
- help="Valid primary image reference to be used in tests."),
+ help="Valid primary image reference to be used in tests. "
+ "This is a required option"),
cfg.StrOpt('image_ref_alt',
- default="{$IMAGE_ID_ALT}",
- help="Valid secondary image reference to be used in tests."),
+ help="Valid secondary image reference to be used in tests. "
+ "This is a required option, but if only one image is "
+ "available duplicate the value of image_ref above"),
cfg.StrOpt('flavor_ref',
default="1",
help="Valid primary flavor to use in tests."),
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index e057c74..f83a4b8 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -16,9 +16,12 @@
import logging
import os
+import re
import six
import subprocess
+import time
+from heatclient import exc as heat_exceptions
import netaddr
from neutronclient.common import exceptions as exc
from novaclient import exceptions as nova_exceptions
@@ -32,6 +35,7 @@
from tempest import config
from tempest import exceptions
from tempest.openstack.common import log
+from tempest.openstack.common import timeutils
import tempest.test
CONF = config.CONF
@@ -1069,3 +1073,98 @@
for net in networks['networks']:
if net['name'] == CONF.compute.fixed_network_name:
return net
+
+ @staticmethod
+ def _stack_output(stack, output_key):
+ """Return a stack output value for a given key."""
+ return next((o['output_value'] for o in stack.outputs
+ if o['output_key'] == output_key), None)
+
+ def _ping_ip_address(self, ip_address, should_succeed=True):
+ cmd = ['ping', '-c1', '-w1', ip_address]
+
+ def ping():
+ proc = subprocess.Popen(cmd,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ proc.wait()
+ return (proc.returncode == 0) == should_succeed
+
+ return tempest.test.call_until_true(
+ ping, CONF.orchestration.build_timeout, 1)
+
+ def _wait_for_resource_status(self, stack_identifier, resource_name,
+ status, failure_pattern='^.*_FAILED$'):
+ """Waits for a Resource to reach a given status."""
+ fail_regexp = re.compile(failure_pattern)
+ build_timeout = CONF.orchestration.build_timeout
+ build_interval = CONF.orchestration.build_interval
+
+ start = timeutils.utcnow()
+ while timeutils.delta_seconds(start,
+ timeutils.utcnow()) < build_timeout:
+ try:
+ res = self.client.resources.get(
+ stack_identifier, resource_name)
+ except heat_exceptions.HTTPNotFound:
+ # ignore this, as the resource may not have
+ # been created yet
+ pass
+ else:
+ if res.resource_status == status:
+ return
+ if fail_regexp.search(res.resource_status):
+ raise exceptions.StackResourceBuildErrorException(
+ resource_name=res.resource_name,
+ stack_identifier=stack_identifier,
+ resource_status=res.resource_status,
+ resource_status_reason=res.resource_status_reason)
+ time.sleep(build_interval)
+
+ message = ('Resource %s failed to reach %s status within '
+ 'the required time (%s s).' %
+ (res.resource_name, status, build_timeout))
+ raise exceptions.TimeoutException(message)
+
+ def _wait_for_stack_status(self, stack_identifier, status,
+ failure_pattern='^.*_FAILED$'):
+ """
+ Waits for a Stack to reach a given status.
+
+ Note this compares the full $action_$status, e.g
+ CREATE_COMPLETE, not just COMPLETE which is exposed
+ via the status property of Stack in heatclient
+ """
+ fail_regexp = re.compile(failure_pattern)
+ build_timeout = CONF.orchestration.build_timeout
+ build_interval = CONF.orchestration.build_interval
+
+ start = timeutils.utcnow()
+ while timeutils.delta_seconds(start,
+ timeutils.utcnow()) < build_timeout:
+ try:
+ stack = self.client.stacks.get(stack_identifier)
+ except heat_exceptions.HTTPNotFound:
+ # ignore this, as the stackource may not have
+ # been created yet
+ pass
+ else:
+ if stack.stack_status == status:
+ return
+ if fail_regexp.search(stack.stack_status):
+ raise exceptions.StackBuildErrorException(
+ stack_identifier=stack_identifier,
+ stack_status=stack.stack_status,
+ stack_status_reason=stack.stack_status_reason)
+ time.sleep(build_interval)
+
+ message = ('Stack %s failed to reach %s status within '
+ 'the required time (%s s).' %
+ (stack.stack_name, status, build_timeout))
+ raise exceptions.TimeoutException(message)
+
+ def _stack_delete(self, stack_identifier):
+ try:
+ self.client.stacks.delete(stack_identifier)
+ except heat_exceptions.HTTPNotFound:
+ pass
diff --git a/tempest/api/orchestration/stacks/templates/cfn_init_signal.yaml b/tempest/scenario/orchestration/cfn_init_signal.yaml
similarity index 97%
rename from tempest/api/orchestration/stacks/templates/cfn_init_signal.yaml
rename to tempest/scenario/orchestration/cfn_init_signal.yaml
index fa5345e..c95aabf 100644
--- a/tempest/api/orchestration/stacks/templates/cfn_init_signal.yaml
+++ b/tempest/scenario/orchestration/cfn_init_signal.yaml
@@ -62,7 +62,7 @@
#!/bin/bash -v
/opt/aws/bin/cfn-init
/opt/aws/bin/cfn-signal -e 0 --data "`cat /tmp/smoke-status`" \
- "WaitHandle"
+ --id smoke_status "WaitHandle"
WaitHandle:
Type: AWS::CloudFormation::WaitConditionHandle
WaitCondition:
diff --git a/tempest/scenario/orchestration/test_server_cfn_init.py b/tempest/scenario/orchestration/test_server_cfn_init.py
new file mode 100644
index 0000000..36e6126
--- /dev/null
+++ b/tempest/scenario/orchestration/test_server_cfn_init.py
@@ -0,0 +1,130 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import json
+
+from tempest import config
+from tempest import exceptions
+from tempest.openstack.common import log as logging
+from tempest.scenario import manager
+from tempest import test
+
+CONF = config.CONF
+LOG = logging.getLogger(__name__)
+
+
+class CfnInitScenarioTest(manager.OrchestrationScenarioTest):
+
+ def setUp(self):
+ super(CfnInitScenarioTest, self).setUp()
+ if not CONF.orchestration.image_ref:
+ raise self.skipException("No image available to test")
+ self.client = self.orchestration_client
+ self.template_name = 'cfn_init_signal.yaml'
+
+ def assign_keypair(self):
+ self.stack_name = self._stack_rand_name()
+ if CONF.orchestration.keypair_name:
+ self.keypair = None
+ self.keypair_name = CONF.orchestration.keypair_name
+ else:
+ self.keypair = self.create_keypair()
+ self.keypair_name = self.keypair.id
+
+ def launch_stack(self):
+ net = self._get_default_network()
+ self.parameters = {
+ 'key_name': self.keypair_name,
+ 'flavor': CONF.orchestration.instance_type,
+ 'image': CONF.orchestration.image_ref,
+ 'timeout': CONF.orchestration.build_timeout,
+ 'network': net['id'],
+ }
+
+ # create the stack
+ self.template = self._load_template(__file__, self.template_name)
+ self.client.stacks.create(
+ stack_name=self.stack_name,
+ template=self.template,
+ parameters=self.parameters)
+
+ self.stack = self.client.stacks.get(self.stack_name)
+ self.stack_identifier = '%s/%s' % (self.stack_name, self.stack.id)
+ self.addCleanup(self._stack_delete, self.stack_identifier)
+
+ def check_stack(self):
+ sid = self.stack_identifier
+ self._wait_for_resource_status(
+ sid, 'WaitHandle', 'CREATE_COMPLETE')
+ self._wait_for_resource_status(
+ sid, 'SmokeSecurityGroup', 'CREATE_COMPLETE')
+ self._wait_for_resource_status(
+ sid, 'SmokeKeys', 'CREATE_COMPLETE')
+ self._wait_for_resource_status(
+ sid, 'CfnUser', 'CREATE_COMPLETE')
+ self._wait_for_resource_status(
+ sid, 'SmokeServer', 'CREATE_COMPLETE')
+
+ server_resource = self.client.resources.get(sid, 'SmokeServer')
+ server_id = server_resource.physical_resource_id
+ server = self.compute_client.servers.get(server_id)
+ server_ip = server.networks[CONF.compute.network_for_ssh][0]
+
+ if not self._ping_ip_address(server_ip):
+ self._log_console_output(servers=[server])
+ self.fail(
+ "Timed out waiting for %s to become reachable" % server_ip)
+
+ try:
+ self._wait_for_resource_status(
+ sid, 'WaitCondition', 'CREATE_COMPLETE')
+ except (exceptions.StackResourceBuildErrorException,
+ exceptions.TimeoutException) as e:
+ raise e
+ finally:
+ # attempt to log the server console regardless of WaitCondition
+ # going to complete. This allows successful and failed cloud-init
+ # logs to be compared
+ self._log_console_output(servers=[server])
+
+ self._wait_for_stack_status(sid, 'CREATE_COMPLETE')
+
+ stack = self.client.stacks.get(sid)
+
+ # This is an assert of great significance, as it means the following
+ # has happened:
+ # - cfn-init read the provided metadata and wrote out a file
+ # - a user was created and credentials written to the server
+ # - a cfn-signal was built which was signed with provided credentials
+ # - the wait condition was fulfilled and the stack has changed state
+ wait_status = json.loads(
+ self._stack_output(stack, 'WaitConditionStatus'))
+ self.assertEqual('smoke test complete', wait_status['smoke_status'])
+
+ if self.keypair:
+ # Check that the user can authenticate with the generated
+ # keypair
+ try:
+ linux_client = self.get_remote_client(
+ server_ip, username='ec2-user')
+ linux_client.validate_authentication()
+ except (exceptions.ServerUnreachable,
+ exceptions.SSHTimeout) as e:
+ self._log_console_output(servers=[server])
+ raise e
+
+ @test.attr(type='slow')
+ @test.services('orchestration', 'compute')
+ def test_server_cfn_init(self):
+ self.assign_keypair()
+ self.launch_stack()
+ self.check_stack()
diff --git a/tempest/services/compute/json/security_groups_client.py b/tempest/services/compute/json/security_groups_client.py
index c19baf3..a86f3df 100644
--- a/tempest/services/compute/json/security_groups_client.py
+++ b/tempest/services/compute/json/security_groups_client.py
@@ -88,7 +88,10 @@
def delete_security_group(self, security_group_id):
"""Deletes the provided Security Group."""
- return self.delete('os-security-groups/%s' % str(security_group_id))
+ resp, body = self.delete(
+ 'os-security-groups/%s' % str(security_group_id))
+ self.validate_response(schema.delete_security_group, resp, body)
+ return resp, body
def create_security_group_rule(self, parent_group_id, ip_proto, from_port,
to_port, **kwargs):
diff --git a/tempest/services/volume/json/availability_zone_client.py b/tempest/services/volume/json/availability_zone_client.py
new file mode 100644
index 0000000..6839d3a
--- /dev/null
+++ b/tempest/services/volume/json/availability_zone_client.py
@@ -0,0 +1,34 @@
+# Copyright 2014 NEC Corporation.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import json
+
+from tempest.common import rest_client
+from tempest import config
+
+CONF = config.CONF
+
+
+class VolumeAvailabilityZoneClientJSON(rest_client.RestClient):
+
+ def __init__(self, auth_provider):
+ super(VolumeAvailabilityZoneClientJSON, self).__init__(
+ auth_provider)
+ self.service = CONF.volume.catalog_type
+
+ def get_availability_zone_list(self):
+ resp, body = self.get('os-availability-zone')
+ body = json.loads(body)
+ return resp, body['availabilityZoneInfo']
diff --git a/tempest/services/volume/xml/availability_zone_client.py b/tempest/services/volume/xml/availability_zone_client.py
new file mode 100644
index 0000000..e4a004a
--- /dev/null
+++ b/tempest/services/volume/xml/availability_zone_client.py
@@ -0,0 +1,39 @@
+# Copyright 2014 NEC Corporation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from lxml import etree
+
+from tempest.common import rest_client
+from tempest.common import xml_utils
+from tempest import config
+
+CONF = config.CONF
+
+
+class VolumeAvailabilityZoneClientXML(rest_client.RestClient):
+ TYPE = "xml"
+
+ def __init__(self, auth_provider):
+ super(VolumeAvailabilityZoneClientXML, self).__init__(
+ auth_provider)
+ self.service = CONF.volume.catalog_type
+
+ def _parse_array(self, node):
+ return [xml_utils.xml_to_json(x) for x in node]
+
+ def get_availability_zone_list(self):
+ resp, body = self.get('os-availability-zone')
+ availability_zone = self._parse_array(etree.fromstring(body))
+ return resp, availability_zone
diff --git a/tempest/thirdparty/boto/test_ec2_instance_run.py b/tempest/thirdparty/boto/test_ec2_instance_run.py
index 33b8d6e..b2eb18d 100644
--- a/tempest/thirdparty/boto/test_ec2_instance_run.py
+++ b/tempest/thirdparty/boto/test_ec2_instance_run.py
@@ -193,7 +193,6 @@
instance.terminate()
self.cancelResourceCleanUp(rcuk)
- @test.skip_because(bug="1098891")
@test.attr(type='smoke')
def test_run_terminate_instance(self):
# EC2 run, terminate immediately
@@ -211,7 +210,7 @@
pass
except exception.EC2ResponseError as exc:
if self.ec2_error_code.\
- client.InvalidInstanceID.NotFound.match(exc):
+ client.InvalidInstanceID.NotFound.match(exc) is None:
pass
else:
raise