Don't regenerate the overcloud passwords if the Heat stack exists

If the user is in the incorrect directory (one different from
where they originally deployed), the function to generate
passwords will create a new password file with random passwords.
This will then be sent to Heat and it will attempt to reconfigure
the passwords for all services (which currently isn't fully
supported and can leave users with a non-functioning overcloud).
The issue can be replicated with:

    openstack overcloud deploy --templates
    cd /tmp (or any other different directory)
    openstack overcloud deploy --templates

This changes the behaviour to display an error if the password
file can't be found, but the Heat stack already exists.

Closes-Bug: #1541342
Change-Id: I2ce63c254c10d6382d626b2f5436019971a26952
This commit is contained in:
Dougal Matthews 2016-02-03 11:35:43 +00:00
parent cff4ff4e38
commit 8f9b01e516
4 changed files with 35 additions and 9 deletions

View File

@ -60,3 +60,7 @@ class StateTransitionFailed(Exception):
class ProfileMatchingError(Exception):
"""Failed to validate or assign node profiles"""
class PasswordFileNotFound(Exception):
"""Password file for the Heat stack not found in the current working dir"""

View File

@ -18,12 +18,12 @@ from uuid import uuid4
import mock
import os.path
import tempfile
from unittest import TestCase
from tripleoclient import exceptions
from tripleoclient.tests.v1.utils import (
generate_overcloud_passwords_mock)
from tripleoclient import utils
from unittest import TestCase
class TestPasswordsUtil(TestCase):
@ -36,7 +36,8 @@ class TestPasswordsUtil(TestCase):
mock_open = mock.mock_open()
with mock.patch('six.moves.builtins.open', mock_open):
passwords = utils.generate_overcloud_passwords()
passwords = utils.generate_overcloud_passwords(
create_password_file=True)
self.assertEqual(sorted(mock_open().write.mock_calls), [
mock.call('NEUTRON_METADATA_PROXY_SHARED_SECRET=PASSWORD\n'),
@ -59,6 +60,14 @@ class TestPasswordsUtil(TestCase):
self.assertEqual(len(passwords), 15)
def test_generate_passwords_update(self):
mock_open = mock.mock_open()
with mock.patch('six.moves.builtins.open', mock_open):
with self.assertRaises(exceptions.PasswordFileNotFound):
utils.generate_overcloud_passwords()
@mock.patch("os.path.isfile", return_value=True)
@mock.patch("passlib.utils.generate_password",
return_value="PASSWORD")

View File

@ -19,6 +19,7 @@ import hashlib
import json
import logging
import os
import os.path
import passlib.utils as passutils
import six
import struct
@ -53,7 +54,8 @@ _PASSWORD_NAMES = (
)
def generate_overcloud_passwords(output_file="tripleo-overcloud-passwords"):
def generate_overcloud_passwords(output_file="tripleo-overcloud-passwords",
create_password_file=False):
"""Create the passwords needed for the overcloud
This will create the set of passwords required by the overcloud, store
@ -61,10 +63,17 @@ def generate_overcloud_passwords(output_file="tripleo-overcloud-passwords"):
file already exists the existing passwords will be returned instead,
"""
log = logging.getLogger(__name__ + ".generate_overcloud_passwords")
log.debug("Using password file: {0}".format(os.path.abspath(output_file)))
passwords = {}
if os.path.isfile(output_file):
with open(output_file) as f:
passwords = dict(line.split('=') for line in f.read().splitlines())
elif not create_password_file:
raise exceptions.PasswordFileNotFound(
"The password file could not be found!")
for name in _PASSWORD_NAMES:
if not passwords.get(name):

View File

@ -51,7 +51,7 @@ class DeployOvercloud(command.Command):
predeploy_errors = 0
predeploy_warnings = 0
def set_overcloud_passwords(self, parameters, parsed_args):
def set_overcloud_passwords(self, stack_is_new, parameters):
"""Add passwords to the parameters dictionary
:param parameters: A dictionary for the passwords to be added to
@ -61,7 +61,9 @@ class DeployOvercloud(command.Command):
undercloud_ceilometer_snmpd_password = utils.get_config_value(
"auth", "undercloud_ceilometer_snmpd_password")
passwords = utils.generate_overcloud_passwords()
passwords = utils.generate_overcloud_passwords(
create_password_file=stack_is_new)
ceilometer_pass = passwords['OVERCLOUD_CEILOMETER_PASSWORD']
ceilometer_secret = passwords['OVERCLOUD_CEILOMETER_SECRET']
parameters['AdminPassword'] = passwords['OVERCLOUD_ADMIN_PASSWORD']
@ -90,11 +92,13 @@ class DeployOvercloud(command.Command):
def _update_parameters(self, args, network_client, stack):
parameters = constants.PARAMETERS.copy()
if stack is None:
stack_is_new = stack is None
if stack_is_new:
parameters.update(constants.NEW_STACK_PARAMETERS)
self.log.debug("Generating overcloud passwords")
self.set_overcloud_passwords(parameters, args)
self.set_overcloud_passwords(stack_is_new, parameters)
timestamp = int(time.time())
parameters['DeployIdentifier'] = timestamp
@ -131,7 +135,7 @@ class DeployOvercloud(command.Command):
('NeutronMechanismDrivers', 'neutron_mechanism_drivers')
)
if stack is None:
if stack_is_new:
new_stack_args = (
('NeutronNetworkType', 'neutron_network_type'),
('NeutronTunnelIdRanges', 'neutron_tunnel_id_ranges'),
@ -185,7 +189,7 @@ class DeployOvercloud(command.Command):
if int(parameters.get('CephStorageCount', 0)) > 0:
if stack is None:
if stack_is_new:
parameters.update({
'CephClusterFSID': six.text_type(uuid.uuid1()),
'CephMonKey': utils.create_cephx_key(),