Merge "Log all resource events during overcloud deploy"

This commit is contained in:
Jenkins 2015-11-16 21:13:13 +00:00 committed by Gerrit Code Review
commit 0925b0300e
5 changed files with 127 additions and 41 deletions

View File

@ -9,7 +9,7 @@ ipaddress
passlib>=1.6 passlib>=1.6
python-ironic-inspector-client>=1.0.1 python-ironic-inspector-client>=1.0.1
os-cloud-config os-cloud-config
python-heatclient>=0.3.0 python-heatclient>=0.6.0
python-ironicclient>=0.8.0 python-ironicclient>=0.8.0
python-openstackclient>=1.5.0 python-openstackclient>=1.5.0
six>=1.9.0 six>=1.9.0

View File

@ -115,19 +115,36 @@ class TestCheckHypervisorUtil(TestCase):
class TestWaitForStackUtil(TestCase): class TestWaitForStackUtil(TestCase):
def setUp(self): def setUp(self):
self.mock_orchestration = mock.Mock() self.mock_orchestration = mock.Mock()
self.mock_stacks = mock.MagicMock()
self.stack_status = mock.PropertyMock()
type(self.mock_stacks).stack_status = self.stack_status
self.mock_orchestration.stacks.get.return_value = self.mock_stacks
def mock_event(self, resource_name, id, resource_status_reason,
resource_status, event_time):
e = mock.Mock()
e.resource_name = resource_name
e.id = id
e.resource_status_reason = resource_status_reason
e.resource_status = resource_status
e.event_time = event_time
return e
@mock.patch("heatclient.common.event_utils.get_events")
@mock.patch('time.sleep', return_value=None) @mock.patch('time.sleep', return_value=None)
def test_wait_for_stack_ready(self, sleep_mock): def test_wait_for_stack_ready(self, sleep_mock, mock_el):
self.mock_orchestration.reset_mock() stack = mock.Mock()
self.mock_stacks.reset_mock() stack.stack_name = 'stack'
self.mock_orchestration.stacks.get.return_value = stack
return_values = ['CREATE_IN_PROGRESS', 'CREATE_COMPLETE'] mock_el.side_effect = [[
self.mock_event('stack', 'aaa', 'Stack CREATE started',
self.stack_status.side_effect = return_values 'CREATE_IN_PROGRESS', '2015-10-14T02:25:21Z'),
self.mock_event('thing', 'bbb', 'state changed',
'CREATE_IN_PROGRESS', '2015-10-14T02:25:21Z'),
], [
self.mock_event('thing', 'ccc', 'state changed',
'CREATE_COMPLETE', '2015-10-14T02:25:43Z'),
self.mock_event('stack', 'ddd',
'Stack CREATE completed successfully',
'CREATE_COMPLETE', '2015-10-14T02:25:43Z'),
]]
complete = utils.wait_for_stack_ready(self.mock_orchestration, 'stack') complete = utils.wait_for_stack_ready(self.mock_orchestration, 'stack')
@ -136,28 +153,36 @@ class TestWaitForStackUtil(TestCase):
sleep_mock.assert_called_once_with(mock.ANY) sleep_mock.assert_called_once_with(mock.ANY)
def test_wait_for_stack_ready_no_stack(self): def test_wait_for_stack_ready_no_stack(self):
self.mock_orchestration.reset_mock()
self.mock_orchestration.stacks.get.return_value = None self.mock_orchestration.stacks.get.return_value = None
complete = utils.wait_for_stack_ready(self.mock_orchestration, 'stack') complete = utils.wait_for_stack_ready(self.mock_orchestration, 'stack')
self.mock_orchestration.stacks.get.return_value = self.mock_stacks
self.assertFalse(complete) self.assertFalse(complete)
def test_wait_for_stack_ready_failed(self): @mock.patch("heatclient.common.event_utils.get_events")
self.mock_orchestration.reset_mock() @mock.patch('time.sleep', return_value=None)
self.mock_stacks.reset_mock() def test_wait_for_stack_ready_failed(self, sleep_mock, mock_el):
stack = mock.Mock()
return_values = ['CREATE_FAILED'] stack.stack_name = 'stack'
self.mock_orchestration.stacks.get.return_value = stack
self.stack_status.side_effect = return_values mock_el.side_effect = [[
self.mock_event('stack', 'aaa', 'Stack CREATE started',
'CREATE_IN_PROGRESS', '2015-10-14T02:25:21Z'),
self.mock_event('thing', 'bbb', 'state changed',
'CREATE_IN_PROGRESS', '2015-10-14T02:25:21Z'),
], [
self.mock_event('thing', 'ccc', 'ouch',
'CREATE_FAILED', '2015-10-14T02:25:43Z'),
self.mock_event('stack', 'ddd', 'ouch',
'CREATE_FAILED', '2015-10-14T02:25:43Z'),
]]
complete = utils.wait_for_stack_ready(self.mock_orchestration, 'stack') complete = utils.wait_for_stack_ready(self.mock_orchestration, 'stack')
self.assertFalse(complete) self.assertFalse(complete)
sleep_mock.assert_called_once_with(mock.ANY)
class TestWaitForIntrospection(TestCase): class TestWaitForIntrospection(TestCase):

View File

@ -35,7 +35,9 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
super(TestDeployOvercloud, self).setUp() super(TestDeployOvercloud, self).setUp()
# Get the command object to test # Get the command object to test
self.cmd = overcloud_deploy.DeployOvercloud(self.app, None) app_args = mock.Mock()
app_args.verbose_level = 1
self.cmd = overcloud_deploy.DeployOvercloud(self.app, app_args)
# mock validations for all deploy tests # mock validations for all deploy tests
# for validator tests, see test_overcloud_deploy_validators.py # for validator tests, see test_overcloud_deploy_validators.py
@ -51,6 +53,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
super(TestDeployOvercloud, self).tearDown() super(TestDeployOvercloud, self).tearDown()
os.unlink(self.parameter_defaults_env_file) os.unlink(self.parameter_defaults_env_file)
@mock.patch("heatclient.common.event_utils.get_events")
@mock.patch('tripleo_common.update.add_breakpoints_cleanup_into_env') @mock.patch('tripleo_common.update.add_breakpoints_cleanup_into_env')
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.' @mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'_create_parameters_env') '_create_parameters_env')
@ -93,7 +96,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
mock_create_tempest_deployer_input, mock_create_tempest_deployer_input,
mock_deploy_postconfig, mock_deploy_postconfig,
mock_create_parameters_env, mock_create_parameters_env,
mock_breakpoints_cleanup): mock_breakpoints_cleanupm,
mock_events):
arglist = ['--templates', '--ceph-storage-scale', '3'] arglist = ['--templates', '--ceph-storage-scale', '3']
verifylist = [ verifylist = [
@ -109,6 +113,9 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
clients = self.app.client_manager clients = self.app.client_manager
orchestration_client = clients.tripleoclient.orchestration orchestration_client = clients.tripleoclient.orchestration
orchestration_client.stacks.get.return_value = fakes.create_tht_stack() orchestration_client.stacks.get.return_value = fakes.create_tht_stack()
mock_event = mock.Mock()
mock_event.id = '1234'
mock_events.return_value = [mock_events]
mock_check_hypervisor_stats.return_value = { mock_check_hypervisor_stats.return_value = {
'count': 4, 'count': 4,
@ -372,6 +379,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
mock_validate_args.assert_called_once_with(parsed_args) mock_validate_args.assert_called_once_with(parsed_args)
@mock.patch("heatclient.common.event_utils.get_events")
@mock.patch('tripleo_common.update.add_breakpoints_cleanup_into_env') @mock.patch('tripleo_common.update.add_breakpoints_cleanup_into_env')
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.' @mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'_deploy_postconfig') '_deploy_postconfig')
@ -409,7 +417,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
mock_generate_overcloud_passwords, mock_generate_overcloud_passwords,
mock_create_tempest_deployer_input, mock_create_tempest_deployer_input,
mock_deploy_postconfig, mock_deploy_postconfig,
mock_breakpoints_cleanup): mock_breakpoints_cleanup,
mock_events):
arglist = ['--templates', '/home/stack/tripleo-heat-templates'] arglist = ['--templates', '/home/stack/tripleo-heat-templates']
verifylist = [ verifylist = [
@ -421,6 +430,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
clients = self.app.client_manager clients = self.app.client_manager
orchestration_client = clients.tripleoclient.orchestration orchestration_client = clients.tripleoclient.orchestration
orchestration_client.stacks.get.return_value = fakes.create_tht_stack() orchestration_client.stacks.get.return_value = fakes.create_tht_stack()
orchestration_client.stacks.get.return_value = fakes.create_tht_stack()
mock_events.return_value = []
mock_check_hypervisor_stats.return_value = { mock_check_hypervisor_stats.return_value = {
'count': 4, 'count': 4,

View File

@ -19,13 +19,13 @@ import json
import logging import logging
import os import os
import passlib.utils as passutils import passlib.utils as passutils
import re
import six import six
import struct import struct
import subprocess import subprocess
import sys import sys
import time import time
from heatclient.common import event_utils
from heatclient.exc import HTTPNotFound from heatclient.exc import HTTPNotFound
from six.moves import configparser from six.moves import configparser
from six.moves import urllib from six.moves import urllib
@ -176,7 +176,8 @@ def check_hypervisor_stats(compute_client, nodes=1, memory=0, vcpu=0):
return None return None
def wait_for_stack_ready(orchestration_client, stack_name): def wait_for_stack_ready(orchestration_client, stack_name, marker=None,
action='CREATE', verbose=False):
"""Check the status of an orchestration stack """Check the status of an orchestration stack
Get the status of an orchestration stack and check whether it is complete Get the status of an orchestration stack and check whether it is complete
@ -187,26 +188,63 @@ def wait_for_stack_ready(orchestration_client, stack_name):
:param stack_name: Name or UUID of stack to retrieve :param stack_name: Name or UUID of stack to retrieve
:type stack_name: string :type stack_name: string
:param marker: UUID of the last stack event before the current action
:type marker: string
:param action: Current action to check the stack for COMPLETE
:type action: string
:param verbose: Whether to print events
:type verbose: boolean
""" """
SUCCESSFUL_MATCH_OUTPUT = "(CREATE|UPDATE)_COMPLETE" stack = get_stack(orchestration_client, stack_name)
FAIL_MATCH_OUTPUT = "(CREATE|UPDATE)_FAILED" if not stack:
return False
stack_name = stack.stack_name
while True: while True:
stack = orchestration_client.stacks.get(stack_name) events = event_utils.get_events(orchestration_client,
stack_id=stack_name, nested_depth=2,
event_args={'sort_dir': 'asc',
'marker': marker})
if not stack: if len(events) >= 1:
return False # set marker to last event that was received.
marker = getattr(events[-1], 'id', None)
status = stack.stack_status if verbose:
events_log = event_log_formatter(events)
print(events_log)
for event in events:
# check if stack event was also received
if getattr(event, 'resource_name', '') == stack_name:
stack_status = getattr(event, 'resource_status', '')
print("Stack %(name)s %(status)s" % dict(
name=stack_name, status=stack_status))
if stack_status == '%s_COMPLETE' % action:
return True
elif stack_status == '%s_FAILED' % action:
return False
time.sleep(5)
if re.match(SUCCESSFUL_MATCH_OUTPUT, status):
return True
if re.match(FAIL_MATCH_OUTPUT, status):
print("Stack failed with status: {}".format(
stack.stack_status_reason, file=sys.stderr))
return False
time.sleep(10) def event_log_formatter(events):
"""Return the events in log format."""
event_log = []
log_format = ("%(event_time)s "
"[%(rsrc_name)s]: %(rsrc_status)s %(rsrc_status_reason)s")
for event in events:
event_time = getattr(event, 'event_time', '')
log = log_format % {
'event_time': event_time.replace('T', ' '),
'rsrc_name': getattr(event, 'resource_name', ''),
'rsrc_status': getattr(event, 'resource_status', ''),
'rsrc_status_reason': getattr(event, 'resource_status_reason', '')
}
event_log.append(log)
return "\n".join(event_log)
def wait_for_provision_state(baremetal_client, node_uuid, provision_state, def wait_for_provision_state(baremetal_client, node_uuid, provision_state,

View File

@ -27,6 +27,7 @@ import time
import uuid import uuid
from cliff import command from cliff import command
from heatclient.common import event_utils
from heatclient.common import template_utils from heatclient.common import template_utils
from keystoneclient import exceptions as kscexc from keystoneclient import exceptions as kscexc
from openstackclient.common import exceptions as oscexc from openstackclient.common import exceptions as oscexc
@ -254,15 +255,26 @@ class DeployOvercloud(command.Command):
if stack is None: if stack is None:
self.log.info("Performing Heat stack create") self.log.info("Performing Heat stack create")
action = 'CREATE'
marker = None
orchestration_client.stacks.create(**stack_args) orchestration_client.stacks.create(**stack_args)
else: else:
self.log.info("Performing Heat stack update") self.log.info("Performing Heat stack update")
# Make sure existing parameters for stack are reused # Make sure existing parameters for stack are reused
stack_args['existing'] = 'true' stack_args['existing'] = 'true'
# Find the last top-level event to use for the first marker
events = event_utils.get_events(orchestration_client,
stack_id=stack_name,
event_args={'sort_dir': 'desc',
'limit': 1})
marker = events[0].id if events else None
action = 'UPDATE'
orchestration_client.stacks.update(stack.id, **stack_args) orchestration_client.stacks.update(stack.id, **stack_args)
verbose_events = self.app_args.verbose_level > 0
create_result = utils.wait_for_stack_ready( create_result = utils.wait_for_stack_ready(
orchestration_client, stack_name) orchestration_client, stack_name, marker, action, verbose_events)
if not create_result: if not create_result:
if stack is None: if stack is None:
raise exceptions.DeploymentError("Heat Stack create failed.") raise exceptions.DeploymentError("Heat Stack create failed.")