create history for the overcloud deploy command

Add a history directory in user $HOME for tracking
all the calls of the openstack overcloud deploy command
passed by the users.
Each deploy command run by a user is now track in an history file under
$HOME/.tripleo directory.
It allow to retrieve all the previous calls from
the openstack overcloud deploy command, the environment files used and the
templates directories used, for analysis, debugging or tracking. See
https://bugs.launchpad.net/tripleo/+bug/1673700

It will be very usefull for post deployment user experience and
for customer debuging.

This review aim to be backport for old releases support.
I will implement a  mistral workflow in a follow up review for
Pike, as suggested by D0ugal (D Matthews)

Change-Id: Ibf7dd4a7b0036b6f2a0809d0448a05665c809167
Closes-Bug: #1673700
This commit is contained in:
Mathieu Bultel
2017-03-16 16:29:51 +01:00
parent be2a20637a
commit fa1683ca9d
4 changed files with 83 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
---
fixes:
- Each deploy command run by a user is now track in an history file under
$HOME/.tripleo directory. It allow to retrieve all the previous calls from
the openstack overcloud deploy command, the environment files used and the
templates directories used, for analysis, debugging or tracking. See
https://bugs.launchpad.net/tripleo/+bug/1673700

View File

@@ -15,9 +15,12 @@
from uuid import uuid4
import argparse
import mock
from mock import call
import os.path
import tempfile
from unittest import TestCase
import yaml
@@ -712,3 +715,45 @@ class TestBracketIPV6(TestCase):
def test_already_bracketed(self):
result = utils.bracket_ipv6('[::1]')
self.assertEqual('[::1]', result)
class TestStoreCliParam(TestCase):
def setUp(self):
self.args = argparse.ArgumentParser()
@mock.patch('os.mkdir')
@mock.patch('os.path.exists')
def test_fail_to_create_file(self, mock_exists, mock_mkdir):
mock_exists.return_value = False
mock_mkdir.side_effect = OSError()
self.assertRaises(OSError, utils.store_cli_param, self.args)
@mock.patch('os.path.isdir')
@mock.patch('os.path.exists')
def test_exists_but_not_dir(self, mock_exists, mock_isdir):
mock_exists.return_value = True
mock_isdir.return_value = False
self.assertRaises(exceptions.InvalidConfiguration,
utils.store_cli_param,
self.args)
@mock.patch('six.moves.builtins.open')
@mock.patch('os.path.isdir')
@mock.patch('os.path.exists')
def test_write_cli_param(self, mock_exists, mock_isdir, mock_open):
history_path = os.path.join(os.path.expanduser("~"), '.tripleo')
mock_exists.return_value = True
mock_isdir.return_value = True
utils.store_cli_param(self.args)
expected_call = [call("%s/history" % history_path, 'a')]
mock_open.assert_has_calls(expected_call)
@mock.patch('six.moves.builtins.open')
@mock.patch('os.path.isdir')
@mock.patch('os.path.exists')
def test_fail_to_write_data(self, mock_exists, mock_isdir, mock_open):
mock_exists.return_value = True
mock_isdir.return_value = True
mock_open.side_effect = IOError()
self.assertRaises(IOError, utils.store_cli_param, self.args)

View File

@@ -15,6 +15,7 @@
from __future__ import print_function
import csv
import datetime
import hashlib
import json
import logging
@@ -77,6 +78,35 @@ def write_overcloudrc(stack_name, overcloudrcs, config_directory='.'):
os.chmod(rcv3path, 0o600)
def store_cli_param(parsed_args):
"""write the cli parameters into an history file"""
history_path = os.path.join(os.path.expanduser("~"), '.tripleo')
if not os.path.exists(history_path):
try:
os.mkdir(history_path)
except OSError as e:
messages = "Unable to create TripleO history directory: "
"{0}, {1}".format(history_path, e)
raise OSError(messages)
if os.path.isdir(history_path):
try:
with open(os.path.join(history_path,
'history'), 'a') as history:
args = parsed_args.__dict__.copy()
used_args = ', '.join('%s=%s' % (key, value)
for key, value in args.items())
history.write(' '.join([str(datetime.datetime.now()),
used_args]))
except IOError as e:
messages = "Unable to write into TripleO history file: "
"{0}, {1}".format(history_path, e)
raise IOError(messages)
else:
raise exceptions.InvalidConfiguration("Target path %s is not a "
"directory" % history_path)
def create_tempest_deployer_input(config_name='tempest-deployer-input.conf'):
config = configparser.ConfigParser()

View File

@@ -812,6 +812,7 @@ class DeployOvercloud(command.Command):
sc_logger.setLevel(logging.CRITICAL)
self._validate_args(parsed_args)
utils.store_cli_param(parsed_args)
stack = utils.get_stack(self.orchestration_client, parsed_args.stack)