TRAIN-ONLY Fix overcloud support report for T

overcloud support report collect relies on
os-collect-config running on the overcloud nodes.
Since this isn't the case in Train, we need to
change how we manage this process. This change will
backport pieces from Ussuri and use Ansible rather
than Mistral.

Closes-Bug: #1939461
Depends-On: https://review.opendev.org/c/openstack/tripleo-ansible/+/804181
Change-Id: I7adcf5ed310903ed8f5064fbbb6af6b42f280936
This commit is contained in:
Brendan Shephard 2021-08-11 00:35:51 +00:00
parent 31dd5e7cfa
commit 58caa75f02
4 changed files with 193 additions and 141 deletions

View File

@ -23,6 +23,11 @@ WS_URL = "ws://0.0.0.0"
WSS_URL = "wss://0.0.0.0"
class FakeOptions(object):
def __init__(self):
self.debug = True
class FakeApp(object):
def __init__(self):
_stdout = None

View File

@ -1,4 +1,4 @@
# Copyright 2017 Red Hat, Inc.
# Copyright 2020 Red Hat, Inc.
#
# 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
@ -15,119 +15,134 @@
import mock
from osc_lib.tests import utils
from tripleoclient.tests.v1.overcloud_deploy import fakes
from tripleoclient.tests import fakes
from tripleoclient.v1 import overcloud_support
class TestOvercloudSupportReport(fakes.TestDeployOvercloud):
class TestOvercloudSupportReport(utils.TestCommand):
def setUp(self):
super(TestOvercloudSupportReport, self).setUp()
self.cmd = overcloud_support.ReportExecute(self.app, None)
# Get the command object to test
app_args = mock.Mock()
app_args.verbose_level = 1
self.app.client_manager.workflow_engine = mock.Mock()
self.app.client_manager.tripleoclient = mock.Mock()
self.app.client_manager.object_store = mock.Mock()
self.workflow = self.app.client_manager.workflow_engine
self.swift = self.app.client_manager.object_store
self.app.options = fakes.FakeOptions()
self.cmd = overcloud_support.ReportExecute(self.app, app_args)
@mock.patch('tripleoclient.workflows.support.download_files')
@mock.patch('tripleoclient.workflows.support.delete_container')
@mock.patch('tripleoclient.workflows.support.fetch_logs')
def test_action(self, fetch_logs_mock, delete_container_mock,
download_files_mock):
arglist = ['-c', 'mycontainer', '-t', '60', 'control']
verifylist = [
('server_name', 'control'),
('container', 'mycontainer'),
('timeout', 60)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@mock.patch('os.chmod')
@mock.patch('tripleoclient.workflows.package_update.get_key')
@mock.patch('tripleoclient.utils.get_tripleo_ansible_inventory')
@mock.patch('tripleoclient.utils.run_ansible_playbook',
autospec=True)
def test_overcloud_support_noargs(self, mock_playbook, mock_inventory,
mock_key, mock_chmod):
parsed_args = self.check_parser(self.cmd, ['all'], [])
self.key = mock_key
mock_inventory.return_value = '/home/stack/tripleo-ansible-inventory'
playbook = ('/usr/share/ansible/tripleo-playbooks'
'/cli-support-collect-logs.yaml')
self.cmd.take_action(parsed_args)
with mock.patch('builtins.open', mock.mock_open()):
with open('/home/stack/.ssh/id_rsa_tripleo', 'w') as key_file:
key_file.write(self.key)
self.cmd.take_action(parsed_args)
fetch_logs_mock.assert_called_once_with(self.app.client_manager,
parsed_args.container,
parsed_args.server_name,
timeout=60,
concurrency=None)
mock_playbook.assert_called_once_with(
logger=mock.ANY,
ansible_config='/etc/ansible/ansible.cfg',
workdir=mock.ANY,
python_interpreter='/usr/bin/python3',
playbook=playbook,
inventory=mock_inventory(),
verbosity=1,
timeout=None,
forks=None,
extra_vars={
'server_name': 'all',
'sos_destination': '/var/lib/tripleo/support'
}
)
download_files_mock.assert_called_once_with(
self.app.client_manager, parsed_args.container,
parsed_args.destination)
@mock.patch('os.chmod')
@mock.patch('tripleoclient.workflows.package_update.get_key')
@mock.patch('tripleoclient.utils.get_tripleo_ansible_inventory')
@mock.patch('tripleoclient.utils.run_ansible_playbook',
autospec=True)
def test_overcloud_support_args(self, mock_playbook, mock_inventory,
mock_key, mock_chmod):
arglist = ['server1', '--output', 'test']
parsed_args = self.check_parser(self.cmd, arglist, [])
self.key = mock_key
mock_inventory.return_value = '/home/stack/tripleo-ansible-inventory'
playbook = ('/usr/share/ansible/tripleo-playbooks'
'/cli-support-collect-logs.yaml')
delete_container_mock.assert_called_once_with(self.app.client_manager,
parsed_args.container,
timeout=60,
concurrency=None)
with mock.patch('builtins.open', mock.mock_open()):
with open('/home/stack/.ssh/id_rsa_tripleo', 'w') as key_file:
key_file.write(self.key)
self.cmd.take_action(parsed_args)
@mock.patch('tripleoclient.workflows.support.download_files')
@mock.patch('tripleoclient.workflows.support.delete_container')
@mock.patch('tripleoclient.workflows.support.fetch_logs')
def test_action_skip_container_delete(self, fetch_logs_mock,
delete_container_mock,
download_files_mock):
arglist = ['-c', 'mycontainer', '--skip-container-delete', 'control']
verifylist = [
('server_name', 'control'),
('container', 'mycontainer')
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
mock_playbook.assert_called_once_with(
logger=mock.ANY,
ansible_config='/etc/ansible/ansible.cfg',
workdir=mock.ANY,
python_interpreter='/usr/bin/python3',
playbook=playbook,
inventory=mock_inventory(),
verbosity=1,
timeout=None,
forks=None,
extra_vars={
'server_name': 'server1',
'sos_destination': 'test'
}
)
self.cmd.take_action(parsed_args)
@mock.patch('os.chmod')
@mock.patch('tripleoclient.workflows.package_update.get_key')
@mock.patch('tripleoclient.utils.get_tripleo_ansible_inventory')
@mock.patch('tripleoclient.utils.run_ansible_playbook',
autospec=True)
def test_overcloud_support_args_stack(self, mock_playbook, mock_inventory,
mock_key, mock_chmod):
arglist = ['server1', '--output', 'test', '--stack', 'notovercloud']
parsed_args = self.check_parser(self.cmd, arglist, [])
self.key = mock_key
fetch_logs_mock.assert_called_once_with(self.app.client_manager,
parsed_args.container,
parsed_args.server_name,
timeout=None,
concurrency=None)
with mock.patch('builtins.open', mock.mock_open()):
with open('/home/stack/.ssh/id_rsa_tripleo', 'w') as key_file:
key_file.write(self.key)
self.cmd.take_action(parsed_args)
download_files_mock.assert_called_once_with(
self.app.client_manager, parsed_args.container,
parsed_args.destination)
playbook = ('/usr/share/ansible/tripleo-playbooks'
'/cli-support-collect-logs.yaml')
delete_container_mock.assert_not_called()
mock_inventory.assert_called_once_with(
inventory_file='/home/stack/'
'tripleo-ansible-inventory.yaml',
ssh_user='tripleo-admin',
stack='notovercloud',
return_inventory_file_path=True)
@mock.patch('tripleoclient.workflows.support.delete_container')
@mock.patch('tripleoclient.workflows.support.fetch_logs')
def test_action_collect_logs_only(self, fetch_logs_mock,
delete_container_mock):
arglist = ['--collect-only', '-t', '60', '-n', '10', 'control']
verifylist = [
('server_name', 'control'),
('collect_only', True),
('timeout', 60),
('concurrency', 10)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
fetch_logs_mock.assert_called_once_with(self.app.client_manager,
parsed_args.container,
parsed_args.server_name,
timeout=60,
concurrency=10)
delete_container_mock.assert_not_called()
@mock.patch('tripleoclient.workflows.support.download_files')
@mock.patch('tripleoclient.workflows.support.delete_container')
@mock.patch('tripleoclient.workflows.support.fetch_logs')
def test_action_download_logs_only(self, fetch_logs_mock,
delete_container_mock,
download_files_mock):
arglist = ['--download-only', 'control']
verifylist = [
('server_name', 'control'),
('download_only', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
fetch_logs_mock.assert_not_called()
delete_container_mock.assert_not_called()
download_files_mock.assert_called_once_with(
self.app.client_manager, parsed_args.container,
parsed_args.destination)
mock_playbook.assert_called_once_with(
logger=mock.ANY,
ansible_config='/etc/ansible/ansible.cfg',
workdir=mock.ANY,
python_interpreter='/usr/bin/python3',
playbook=playbook,
inventory=mock_inventory(),
verbosity=1,
timeout=None,
forks=None,
extra_vars={
'server_name': 'server1',
'sos_destination': 'test'
}
)

View File

@ -95,7 +95,7 @@ class TempDirs(object):
def __init__(self, dir_path=None, dir_prefix='tripleo', cleanup=True,
chdir=True):
"""This context manager will create, push, and cleanup temp directories.
"""This context manager will create, push, and cleanup temp directories
>>> with TempDirs() as t:
... with open('file', 'w') as f:
@ -197,7 +197,8 @@ def run_ansible_playbook(logger,
extra_vars=None,
plan='overcloud',
gathering_policy=None,
forks=None):
forks=None,
timeout=None):
"""Simple wrapper for ansible-playbook
:param logger: logger instance
@ -266,6 +267,10 @@ def run_ansible_playbook(logger,
When not specified, the policy will be the default Ansible one, ie.
'implicit'.
:type gathering_facts: String
:param timeout: This sets the timeout for the ansible-playbook
command.
:type timeout: Integer
"""
env = os.environ.copy()
@ -388,6 +393,11 @@ def run_ansible_playbook(logger,
cmd.extend(['-c', connection, play])
if timeout:
cmd.extend([
'--timeout %s' % timeout
])
if run_command_and_log(logger, cmd, env=env) != 0:
logger.warning(
"{} did not complete successfully.".format(play)

View File

@ -14,11 +14,14 @@
#
import logging
import os
from osc_lib.i18n import _
from tripleoclient import command
from tripleoclient.workflows import support
from tripleoclient import constants
from tripleoclient import utils
from tripleoclient.workflows import package_update
class ReportExecute(command.Command):
@ -29,21 +32,29 @@ class ReportExecute(command.Command):
def get_parser(self, prog_name):
parser = super(ReportExecute, self).get_parser(prog_name)
parser.add_argument('server_name',
help=_('Nova server_name or partial name to match.'
' For example "controller" will match all '
'controllers for an environment.'))
help=_('Server name, group name, or partial name'
' to match. For example "Controller" will'
' match all controllers for an'
' environment.'))
parser.add_argument('-c', '--container', dest='container',
default='overcloud_support',
help=_('Swift Container to store logs to'))
parser.add_argument('-o', '--output', dest='destination',
default='support_logs',
action='store_true',
help=_('DEPRECATED: Swift Container to store'
' logs to'))
parser.add_argument('-o',
'--output',
dest='destination',
default='/var/lib/tripleo/support',
help=_('Output directory for the report'))
parser.add_argument('--stack',
help=_("Stack name to use for log collection."),
default='overcloud')
parser.add_argument('--skip-container-delete', dest='skip_delete',
default=False,
help=_('Do not delete the container after the '
'files have been downloaded. Ignored '
'if --collect-only or --download-only '
'is provided.'),
help=_('DEPRECATED: Do not delete the container '
'after the files have been downloaded. '
'Ignored if --collect-only or '
'--download-only is provided.'),
action='store_true')
parser.add_argument('-t', '--timeout', dest='timeout', type=int,
default=None,
@ -56,52 +67,63 @@ class ReportExecute(command.Command):
'object deletion tasks to run.'))
group = parser.add_mutually_exclusive_group(required=False)
group.add_argument('--collect-only', dest='collect_only',
help=_('Skip log downloads, only collect logs and '
'put in the container'),
help=_('DEPRECATED: Skip log downloads, only '
'collect logs and put in the container'),
default=False,
action='store_true')
group.add_argument('--download-only', dest='download_only',
help=_('Skip generation, only download from '
'the provided container'),
help=_('DEPRECATED: Skip generation, only '
'download from the provided container'),
default=False,
action='store_true')
parser.add_argument('-v',
'--verbose',
dest='verbosity',
type=int,
default=1)
return parser
def take_action(self, parsed_args):
self.log.debug('take_action({})'.format(parsed_args))
playbook = os.path.join(
constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
'cli-support-collect-logs.yaml')
inventory = utils.get_tripleo_ansible_inventory(
inventory_file='/home/stack/'
'tripleo-ansible-inventory.yaml',
ssh_user='tripleo-admin',
stack=parsed_args.stack,
return_inventory_file_path=True)
clients = self.app.client_manager
container = parsed_args.container
server_name = parsed_args.server_name
destination = parsed_args.destination
timeout = parsed_args.timeout
concurrency = parsed_args.concurrency
key = package_update.get_key(clients)
if not server_name:
raise Exception(_('Please specify the server_name option.'))
# The playbook we're using for this relies on this file
# existing in this location. So we need to write it and
# set the permissions appropriately. Not required after
# Train.
with open('/home/stack/.ssh/id_rsa_tripleo', 'w') as key_file:
key_file.write(key)
if not parsed_args.download_only:
print(_('Starting log collection... (This may take a while)'))
try:
support.fetch_logs(clients, container, server_name,
timeout=timeout, concurrency=concurrency)
except Exception as err:
self.log.error('Unable to fetch logs, {}'.format(err))
raise err
os.chmod('/home/stack/.ssh/id_rsa_tripleo', 0o600)
if not parsed_args.collect_only:
try:
support.download_files(clients, container, destination)
except Exception as err:
self.log.error('Unable to download files, {}'.format(err))
raise err
extra_vars = {
'server_name': parsed_args.server_name,
'sos_destination': parsed_args.destination,
}
if not parsed_args.collect_only and not parsed_args.download_only and \
not parsed_args.skip_delete:
print(_('Deleting container') + ' {}...'.format(container))
try:
support.delete_container(clients, container, timeout=timeout,
concurrency=concurrency)
except Exception as err:
self.log.error('Unable to delete container, {}'.format(err))
raise err
with utils.TempDirs() as tmp:
utils.run_ansible_playbook(
logger=self.log,
ansible_config='/etc/ansible/ansible.cfg',
playbook=playbook,
inventory=inventory,
python_interpreter='/usr/bin/python3',
workdir=tmp,
verbosity=parsed_args.verbosity,
forks=parsed_args.concurrency,
timeout=parsed_args.timeout,
extra_vars=extra_vars
)