Improve upgrade process to include upgrade flag

Previously, the upgrade process for the undercloud has been to just
rerun the install process. The problem is that this does not include
anwyay to indicate that it's already been run if we need to trigger some
different actions on subsequent runs.  As part of the Newton to Ocata
upgrade, we need to be able to run the cell v2 setup in a different
order than on a traditional install. We need a way for the puppet
scripts that ultimately get run to know if it's an upgrade or install
action. This change adds the creation of an undercloud_upgrade fact that
will be used when the upgrade process is run. Additionally a new script
called instack-upgrade-undercloud has been created to be used when
upgrade. The use of instack-install-undercloud will cause the install
flow to be execuated, while the instack-upgrade-undercloud will set the
fact to true to allow for the puppet scripts to use the upgrade flow.

Change-Id: Ie3cb21e30334fe8ffc0a9d6e707b42269b64c9ec
Related-Bug: #1649341
Related-Blueprint: undercloud-upgrade
This commit is contained in:
Alex Schultz 2017-01-11 12:43:01 -07:00
parent c5f80ddfbf
commit a661b6d5e1
4 changed files with 127 additions and 2 deletions

View File

@ -17,6 +17,7 @@ import io
import json
import os
import subprocess
import tempfile
import fixtures
import mock
@ -41,6 +42,7 @@ class BaseTestCase(base.BaseTestCase):
class TestUndercloud(BaseTestCase):
@mock.patch('instack_undercloud.undercloud._handle_upgrade_fact')
@mock.patch('instack_undercloud.undercloud._configure_logging')
@mock.patch('instack_undercloud.undercloud._validate_configuration')
@mock.patch('instack_undercloud.undercloud._run_command')
@ -53,7 +55,8 @@ class TestUndercloud(BaseTestCase):
def test_install(self, mock_load_config, mock_generate_environment,
mock_run_yum_update, mock_run_instack, mock_run_orc,
mock_post_config, mock_run_command,
mock_validate_configuration, mock_configure_logging):
mock_validate_configuration, mock_configure_logging,
mock_upgrade_fact):
fake_env = mock.MagicMock()
mock_generate_environment.return_value = fake_env
undercloud.install('.')
@ -63,6 +66,33 @@ class TestUndercloud(BaseTestCase):
mock_run_orc.assert_called_with(fake_env)
mock_run_command.assert_called_with(
['sudo', 'rm', '-f', '/tmp/svc-map-services'], None, 'rm')
mock_upgrade_fact.assert_called_with(False)
@mock.patch('instack_undercloud.undercloud._handle_upgrade_fact')
@mock.patch('instack_undercloud.undercloud._configure_logging')
@mock.patch('instack_undercloud.undercloud._validate_configuration')
@mock.patch('instack_undercloud.undercloud._run_command')
@mock.patch('instack_undercloud.undercloud._post_config')
@mock.patch('instack_undercloud.undercloud._run_orc')
@mock.patch('instack_undercloud.undercloud._run_yum_update')
@mock.patch('instack_undercloud.undercloud._run_instack')
@mock.patch('instack_undercloud.undercloud._generate_environment')
@mock.patch('instack_undercloud.undercloud._load_config')
def test_install_upgrade(self, mock_load_config, mock_generate_environment,
mock_run_yum_update, mock_run_instack,
mock_run_orc, mock_post_config, mock_run_command,
mock_validate_configuration,
mock_configure_logging, mock_upgrade_fact):
fake_env = mock.MagicMock()
mock_generate_environment.return_value = fake_env
undercloud.install('.', upgrade=True)
self.assertTrue(mock_validate_configuration.called)
mock_generate_environment.assert_called_with('.')
mock_run_instack.assert_called_with(fake_env)
mock_run_orc.assert_called_with(fake_env)
mock_run_command.assert_called_with(
['sudo', 'rm', '-f', '/tmp/svc-map-services'], None, 'rm')
mock_upgrade_fact.assert_called_with(True)
@mock.patch('instack_undercloud.undercloud._configure_logging')
def test_install_exception(self, mock_configure_logging):
@ -744,3 +774,64 @@ class TestPostConfig(base.BaseTestCase):
mock_instance.flavors.list.return_value = mock_flavors
undercloud._delete_default_flavors(mock_instance)
mock_instance.flavors.delete.assert_called_once_with('8ar')
class TestUpgradeFact(base.BaseTestCase):
@mock.patch('instack_undercloud.undercloud._run_command')
@mock.patch('os.path.dirname')
@mock.patch('os.path.exists')
@mock.patch.object(tempfile, 'mkstemp', return_value=(1, '/tmp/file'))
def test_upgrade_fact(self, mock_mkstemp, mock_exists, mock_dirname,
mock_run):
fact_path = '/etc/facter/facts.d/undercloud_upgrade.txt'
mock_dirname.return_value = '/etc/facter/facts.d'
mock_exists.side_effect = [False, True]
with mock.patch('instack_undercloud.undercloud.open') as mock_open:
undercloud._handle_upgrade_fact(True)
mock_open.assert_called_with('/tmp/file', 'w')
run_calls = [
mock.call(['sudo', 'mkdir', '-p', '/etc/facter/facts.d']),
mock.call(['sudo', 'mv', '/tmp/file', fact_path]),
mock.call(['sudo', 'chmod', '0644', fact_path])
]
mock_run.assert_has_calls(run_calls)
self.assertEqual(mock_run.call_count, 3)
@mock.patch('instack_undercloud.undercloud._run_command')
@mock.patch('os.path.dirname')
@mock.patch('os.path.exists')
@mock.patch.object(tempfile, 'mkstemp', return_value=(1, '/tmp/file'))
def test_upgrade_fact_install(self, mock_mkstemp, mock_exists,
mock_dirname, mock_run):
mock_dirname.return_value = '/etc/facter/facts.d'
mock_exists.return_value = False
with mock.patch('instack_undercloud.undercloud.open') as mock_open:
undercloud._handle_upgrade_fact(False)
mock_open.assert_not_called()
mock_run.assert_not_called()
@mock.patch('instack_undercloud.undercloud._run_command')
@mock.patch('os.path.dirname')
@mock.patch('os.path.exists')
@mock.patch.object(tempfile, 'mkstemp', return_value=(1, '/tmp/file'))
def test_upgrade_fact_upgrade_after_install(self, mock_mkstemp,
mock_exists, mock_dirname,
mock_run):
fact_path = '/etc/facter/facts.d/undercloud_upgrade.txt'
mock_dirname.return_value = '/etc/facter/facts.d'
mock_exists.return_value = True
with mock.patch('instack_undercloud.undercloud.open') as open_m:
undercloud._handle_upgrade_fact(True)
open_m.assert_called_with('/tmp/file', 'w')
run_calls = [
mock.call(['sudo', 'mv', '/tmp/file', fact_path]),
mock.call(['sudo', 'chmod', '0644', fact_path])
]
mock_run.assert_has_calls(run_calls)
self.assertEqual(mock_run.call_count, 2)

View File

@ -1232,7 +1232,36 @@ def _post_config(instack_env):
_post_config_mistral(instack_env, mistral)
def install(instack_root):
def _handle_upgrade_fact(upgrade=False):
"""Create an upgrade fact for use in puppet
Since we don't run different puppets depending on if it's an upgrade or
not, we need to be able to pass a flag into puppet to let it know if
we're doing an upgrade. This is helpful when trying to handle state
transitions from an already installed undercloud. This function creates
a static fact named undercloud_upgrade only after the install has occured.
When invoked with upgrade=True, the $::undercloud_upgrade fact should
be set to true.
:param upgrade: Boolean indicating if this is an upgrade action or not
"""
fact_string = 'undercloud_upgrade={}'.format(upgrade)
fact_path = '/etc/facter/facts.d/undercloud_upgrade.txt'
if not os.path.exists(os.path.dirname(fact_path)) and upgrade:
_run_command(['sudo', 'mkdir', '-p', os.path.dirname(fact_path)])
# We only need to ensure the fact is correct when we've already installed
# the undercloud.
if os.path.exists(os.path.dirname(fact_path)):
tmp_fact = tempfile.mkstemp()[1]
with open(tmp_fact, 'w') as f:
f.write(fact_string.lower())
_run_command(['sudo', 'mv', tmp_fact, fact_path])
_run_command(['sudo', 'chmod', '0644', fact_path])
def install(instack_root, upgrade=False):
"""Install the undercloud
:param instack_root: The path containing the instack-undercloud elements
@ -1248,6 +1277,7 @@ def install(instack_root):
_generate_init_data(instack_env)
if CONF.undercloud_update_packages:
_run_yum_update(instack_env)
_handle_upgrade_fact(upgrade)
_run_instack(instack_env)
_run_orc(instack_env)
_post_config(instack_env)

View File

@ -0,0 +1,3 @@
#!/bin/bash
python -c "from instack_undercloud import undercloud; undercloud.install('$(dirname $0)/..', upgrade=True)"

View File

@ -24,6 +24,7 @@ packages =
scripts =
scripts/instack-install-undercloud
scripts/instack-upgrade-undercloud
scripts/instack-virt-setup
scripts/instack-haproxy-cert-update