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:
parent
31dd5e7cfa
commit
58caa75f02
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
}
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue