Heat test ssh to the server.

This is run when the resource creation is complete, but before
the stack creation is complete.

Change-Id: I05c5624a6f3c4817391de87224d647e260607f3e
This commit is contained in:
Steve Baker 2013-06-12 12:47:16 +12:00
parent 0985ca79e1
commit 38d8f09279
3 changed files with 101 additions and 12 deletions

View File

@ -36,6 +36,7 @@ class BaseOrchestrationTest(tempest.test.BaseTestCase):
cls.os = os
cls.orchestration_client = os.orchestration_client
cls.servers_client = os.servers_client
cls.keypairs_client = os.keypairs_client
cls.stacks = []
@ -108,3 +109,9 @@ class BaseOrchestrationTest(tempest.test.BaseTestCase):
condition()
return
time.sleep(self.build_interval)
@staticmethod
def stack_output(stack, output_key):
"""Return a stack output value for a give key."""
return next((o['output_value'] for o in stack['outputs']
if o['output_key'] == output_key), None)

View File

@ -14,9 +14,12 @@
import json
import logging
import testtools
from tempest.api.orchestration import base
from tempest.common.utils.data_utils import rand_name
from tempest.common.utils.linux.remote_client import RemoteClient
import tempest.config
from tempest.test import attr
@ -25,6 +28,8 @@ LOG = logging.getLogger(__name__)
class InstanceCfnInitTestJSON(base.BaseOrchestrationTest):
_interface = 'json'
existing_keypair = (tempest.config.TempestConfig().
orchestration.keypair_name is not None)
template = """
HeatTemplateFormatVersion: '2012-12-12'
@ -101,6 +106,10 @@ Outputs:
Description: Contents of /tmp/smoke-status on SmokeServer
Value:
Fn::GetAtt: [WaitCondition, Data]
SmokeServerIp:
Description: IP address of server
Value:
Fn::GetAtt: [SmokeServer, PublicIp]
"""
@classmethod
@ -113,8 +122,11 @@ Outputs:
def setUp(self):
super(InstanceCfnInitTestJSON, self).setUp()
stack_name = rand_name('heat')
keypair_name = (self.orchestration_cfg.keypair_name or
self._create_keypair()['name'])
if self.orchestration_cfg.keypair_name:
keypair_name = self.orchestration_cfg.keypair_name
else:
self.keypair = self._create_keypair()
keypair_name = self.keypair['name']
# create the stack
self.stack_identifier = self.create_stack(
@ -126,6 +138,29 @@ Outputs:
'ImageId': self.orchestration_cfg.image_ref
})
@attr(type='gate')
@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 server resource create to complete.
self.client.wait_for_resource_status(sid, rid, '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 = RemoteClient(
server, 'ec2-user', pkey=self.keypair['private_key'])
self.assertTrue(linux_client.can_authenticate())
@attr(type='gate')
def test_stack_wait_condition_data(self):
@ -148,5 +183,6 @@ Outputs:
# - a user was created and credentials written to the instance
# - 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(body['outputs'][0]['output_value'])
wait_status = json.loads(
self.stack_output(body, 'WaitConditionStatus'))
self.assertEqual('smoke test complete', wait_status['00000'])

View File

@ -16,6 +16,7 @@
# under the License.
import json
import re
import time
import urllib
@ -68,24 +69,69 @@ class OrchestrationClient(rest_client.RestClient):
body = json.loads(body)
return resp, body['stack']
def list_resources(self, stack_identifier):
"""Returns the details of a single resource."""
url = "stacks/%s/resources" % stack_identifier
resp, body = self.get(url)
body = json.loads(body)
return resp, body['resources']
def get_resource(self, stack_identifier, resource_name):
"""Returns the details of a single resource."""
url = "stacks/%s/resources/%s" % (stack_identifier, resource_name)
resp, body = self.get(url)
body = json.loads(body)
return resp, body['resource']
def delete_stack(self, stack_identifier):
"""Deletes the specified Stack."""
return self.delete("stacks/%s" % str(stack_identifier))
def wait_for_stack_status(self, stack_identifier, status, failure_status=(
'CREATE_FAILED',
'DELETE_FAILED',
'UPDATE_FAILED',
'ROLLBACK_FAILED')):
"""Waits for a Volume to reach a given status."""
stack_status = None
def wait_for_resource_status(self, stack_identifier, resource_name,
status, failure_pattern='^.*_FAILED$'):
"""Waits for a Resource to reach a given status."""
start = int(time.time())
fail_regexp = re.compile(failure_pattern)
while stack_status != status:
while True:
try:
resp, body = self.get_resource(
stack_identifier, resource_name)
except exceptions.NotFound:
# ignore this, as the resource may not have
# been created yet
pass
else:
resource_name = body['logical_resource_id']
resource_status = body['resource_status']
if resource_status == status:
return
if fail_regexp.search(resource_status):
raise exceptions.StackBuildErrorException(
stack_identifier=stack_identifier,
resource_status=resource_status,
resource_status_reason=body['resource_status_reason'])
if int(time.time()) - start >= self.build_timeout:
message = ('Resource %s failed to reach %s status within '
'the required time (%s s).' %
(resource_name, status, self.build_timeout))
raise exceptions.TimeoutException(message)
time.sleep(self.build_interval)
def wait_for_stack_status(self, stack_identifier, status,
failure_pattern='^.*_FAILED$'):
"""Waits for a Stack to reach a given status."""
start = int(time.time())
fail_regexp = re.compile(failure_pattern)
while True:
resp, body = self.get_stack(stack_identifier)
stack_name = body['stack_name']
stack_status = body['stack_status']
if stack_status in failure_status:
if stack_status == status:
return
if fail_regexp.search(stack_status):
raise exceptions.StackBuildErrorException(
stack_identifier=stack_identifier,
stack_status=stack_status,