Inherit UpdateCommand class from overcloud DeployCommand class

Instead of dealing with some decidated functions in Update/upgrade
classes for manipulating the plan and the env files,
inherit the update class from the DeployCommand class will allow the
update && upgrade to take benefit from the plan management code
and all the tips and tricks that the deploy class does for handling
the corner cases of the plan (access rights, path of user files)
Also it avoid to rewrite/duplicate some code to solve the issue where
the --templates option is ignored by the update workflow
(and upgrade for Queens btw).
Related tripleo-common review:
https://review.openstack.org/540816

Depends-On: Ica4bb404cd3a38a97300b8af36af41c35f09825c
Change-Id: Ib96f4f5078ec1c45981d051e546bf931d2885ae2
This commit is contained in:
Mathieu Bultel 2018-02-05 10:54:12 +01:00 committed by Jiri Stransky
parent 933d694e51
commit 6ca0ee146d
7 changed files with 92 additions and 53 deletions

View File

@ -23,6 +23,8 @@ RHEL_REGISTRATION_EXTRACONFIG_NAME = (
# The name of the file which holds the plan environment contents
PLAN_ENVIRONMENT = 'plan-environment.yaml'
USER_ENVIRONMENT = 'user-environment.yaml'
USER_PARAMETERS = 'user-environments/tripleoclient-parameters.yaml'
# This directory may contain additional environments to use during deploy
DEFAULT_ENV_DIRECTORY = "~/.tripleo/environments"

View File

@ -695,13 +695,18 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
env, update_plan_only, run_validations,
skip_deploy_identifier, plan_env_file):
assertEqual(
{'parameter_defaults': {},
{'parameter_defaults': {'NovaComputeLibvirtType': 'qemu'},
'resource_registry': {
'Test': 'OS::Heat::None',
'resources': {'*': {'*': {
'UpdateDeployment': {'hooks': []}}}}}}, env)
mock_deploy_heat.side_effect = _fake_heat_deploy
object_client = clients.tripleoclient.object_store
object_client.get_object = mock.Mock()
mock_env = yaml.safe_dump({'parameter_defaults':
{'NovaComputeLibvirtType': 'qemu'}})
object_client.get_object.return_value = ({}, mock_env)
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
@ -816,6 +821,12 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
fixtures.EnvironmentVariable('TRIPLEO_ENVIRONMENT_DIRECTORY',
self.tmp_dir.join('env')))
object_client = clients.tripleoclient.object_store
object_client.get_object = mock.Mock()
mock_env = yaml.safe_dump({'parameter_defaults':
{'NovaComputeLibvirtType': 'qemu'}})
object_client.get_object.return_value = ({}, mock_env)
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
error = self.assertRaises(hc_exc.CommandError, self.cmd.take_action,
parsed_args)
@ -993,6 +1004,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
self.cmd.take_action(parsed_args)
user_env = {
'environments': [],
'resource_registry': {'Test': 'OS::Heat::None',
'Cleanup': 'OS::Heat::None'}}
parameters_env = {
@ -1204,6 +1216,12 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
('disable_password_generation', True)]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
object_client = clients.tripleoclient.object_store
object_client.get_object = mock.Mock()
mock_env = yaml.safe_dump({'parameter_defaults':
{'NovaComputeLibvirtType': 'qemu'}})
object_client.get_object.return_value = ({}, mock_env)
self.cmd.take_action(parsed_args)
self.assertTrue(mock_heat_deploy.called)

View File

@ -45,6 +45,7 @@ class TestOvercloudUpdate(utils.TestCommand):
super(TestOvercloudUpdate, self).setUp()
self.app.client_manager.auth_ref = mock.Mock(auth_token="TOKEN")
self.app.client_manager.baremetal = mock.Mock()
self.app.client_manager.orchestration = mock.Mock()
self.app.client_manager.tripleoclient = FakeClientWrapper()
self.app.client_manager.workflow_engine = mock.Mock()

View File

@ -41,20 +41,26 @@ class TestOvercloudUpdate(fakes.TestOvercloudUpdate):
autospec=True)
@mock.patch('tripleoclient.workflows.package_update.update',
autospec=True)
@mock.patch('six.moves.builtins.open')
@mock.patch('os.path.abspath')
@mock.patch('yaml.load')
def test_update_out(self, mock_yaml, mock_abspath, mock_open, mock_update,
mock_logger, mock_get_stack):
@mock.patch('shutil.copytree', autospec=True)
@mock.patch('six.moves.builtins.open')
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'_deploy_tripleo_heat_templates', autospec=True)
def test_update_out(self, mock_deploy, mock_open, mock_copy, mock_yaml,
mock_abspath, mock_update, mock_logger,
mock_get_stack):
mock_stack = mock.Mock()
mock_stack.stack_name = 'mystack'
mock_get_stack.return_value = mock_stack
mock_abspath.return_value = '/home/fake/my-fake-registry.yaml'
mock_yaml.return_value = {'fake_container': 'fake_value'}
argslist = ['--stack', 'overcloud', '--init-update',
argslist = ['--stack', 'overcloud', '--init-update', '--templates',
'--container-registry-file', 'my-fake-registry.yaml']
verifylist = [
('stack', 'overcloud'),
('templates', constants.TRIPLEO_HEAT_TEMPLATES),
('init_update', True),
('container_registry_file', 'my-fake-registry.yaml')
]
@ -66,8 +72,7 @@ class TestOvercloudUpdate(fakes.TestOvercloudUpdate):
container='mystack',
container_registry={'fake_container': 'fake_value'},
ceph_ansible_playbook='/usr/share/ceph-ansible'
'/site-docker.yml.sample',
environments={}
'/site-docker.yml.sample'
)
@mock.patch('tripleoclient.workflows.package_update.update',
@ -75,15 +80,19 @@ class TestOvercloudUpdate(fakes.TestOvercloudUpdate):
@mock.patch('six.moves.builtins.open')
@mock.patch('os.path.abspath')
@mock.patch('yaml.load')
def test_update_failed(self, mock_yaml, mock_abspath, mock_open,
mock_update):
@mock.patch('shutil.copytree', autospec=True)
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'_deploy_tripleo_heat_templates', autospec=True)
def test_update_failed(self, mock_deploy, mock_copy, mock_yaml,
mock_abspath, mock_open, mock_update):
mock_update.side_effect = exceptions.DeploymentError()
mock_abspath.return_value = '/home/fake/my-fake-registry.yaml'
mock_yaml.return_value = {'fake_container': 'fake_value'}
argslist = ['--stack', 'overcloud', '--init-update',
argslist = ['--stack', 'overcloud', '--init-update', '--templates',
'--container-registry-file', 'my-fake-registry.yaml']
verifylist = [
('stack', 'overcloud'),
('templates', constants.TRIPLEO_HEAT_TEMPLATES),
('init_update', True),
('container_registry_file', 'my-fake-registry.yaml')
]

View File

@ -423,7 +423,6 @@ class DeployOvercloud(command.Command):
def _deploy_tripleo_heat_templates(self, stack, parsed_args,
tht_root, user_tht_root):
"""Deploy the fixed templates in TripleO Heat Templates"""
parameters = self._update_parameters(parsed_args, stack)
plans = plan_management.list_deployment_plans(self.workflow_client)
generate_passwords = not parsed_args.disable_password_generation
@ -459,10 +458,18 @@ class DeployOvercloud(command.Command):
if parsed_args.environment_directories:
created_env_files.extend(utils.load_environment_directories(
parsed_args.environment_directories))
env.update(self._create_parameters_env(parameters,
tht_root,
parsed_args.stack))
parameters = {}
if stack:
try:
# If user environment already exist then keep it
user_env = yaml.safe_load(self.object_client.get_object(
parsed_args.stack, constants.USER_ENVIRONMENT)[1])
template_utils.deep_update(env, user_env)
except ClientException:
pass
parameters.update(self._update_parameters(parsed_args, stack))
template_utils.deep_update(env, self._create_parameters_env(
parameters, tht_root, parsed_args.stack))
if parsed_args.rhel_reg:
reg_env_files, reg_env = self._create_registration_env(

View File

@ -17,37 +17,23 @@ import logging
import os
import yaml
from heatclient.common import template_utils
from osc_lib.i18n import _
from oslo_concurrency import processutils
from tripleoclient import command
from tripleoclient import constants
from tripleoclient import exceptions
from tripleoclient import utils as oooutils
from tripleoclient.v1.overcloud_deploy import DeployOvercloud
from tripleoclient.workflows import package_update
class UpdateOvercloud(command.Command):
class UpdateOvercloud(DeployOvercloud):
"""Updates packages on overcloud nodes"""
log = logging.getLogger(__name__ + ".UpdateOvercloud")
def get_parser(self, prog_name):
parser = super(UpdateOvercloud, self).get_parser(prog_name)
parser.add_argument('--stack',
nargs='?',
dest='stack',
help=_('Name or ID of heat stack to scale '
'(default=Env: OVERCLOUD_STACK_NAME)'),
default='overcloud'
)
parser.add_argument('--templates',
nargs='?',
default=constants.TRIPLEO_HEAT_TEMPLATES,
help=_("The directory containing the Heat"
"templates to deploy. "),
)
parser.add_argument('--init-update',
dest='init_update',
action='store_true',
@ -69,13 +55,6 @@ class UpdateOvercloud(command.Command):
'used for update. This value should be set '
'during the init-minor-update step.')
)
parser.add_argument('--extra-environment', '-e',
action='append',
dest='environment_files',
default=[],
help=_("Extra environment required for the "
"major update"),
)
parser.add_argument('--nodes',
action="store",
default=None,
@ -123,19 +102,17 @@ class UpdateOvercloud(command.Command):
"to re-run this command and provide the registry file "
"with: --container-registry-file option.")
registry = None
# Extra env file
environment_files = parsed_args.environment_files
env = {}
if environment_files:
env_files, env = (
template_utils.process_multiple_environments_and_files(
env_paths=environment_files))
# Run update
ceph_ansible_playbook = parsed_args.ceph_ansible_playbook
# Run Overcloud deploy (stack update)
# In case of update and upgrade we need to force the
# update_plan_only. The heat stack update is done by the
# packag_update mistral action
parsed_args.update_plan_only = True
super(UpdateOvercloud, self).take_action(parsed_args)
package_update.update(clients, container=stack_name,
container_registry=registry,
ceph_ansible_playbook=ceph_ansible_playbook,
environments=env)
ceph_ansible_playbook=ceph_ansible_playbook)
package_update.get_config(clients, container=stack_name)
print("Update init on stack {0} complete.".format(
parsed_args.stack))
@ -197,7 +174,6 @@ class UpgradeOvercloud(UpdateOvercloud):
stack_name = stack.stack_name
converge = parsed_args.converge
if converge:
converge_file = parsed_args.upgrade_converge_file
# Add the converge file to the user environment:

View File

@ -10,6 +10,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import logging
import os
import tempfile
import yaml
@ -157,13 +158,24 @@ def update_plan_from_templates(clients, name, tht_root, roles_file=None,
# 'passwords' if they exist as they can't be recreated from the
# templates content.
passwords = []
user_env = {}
# If the user provides a plan-environment files, then used it
if plan_env_file:
with open(os.path.abspath(plan_env_file)) as content:
env = yaml.load(content.read())
else:
try:
env = yaml.safe_load(swift_client.get_object(
name, constants.PLAN_ENVIRONMENT)[1])
except swift_exc.ClientException:
pass
try:
env = yaml.safe_load(swift_client.get_object(
name, constants.PLAN_ENVIRONMENT)[1])
passwords = env.get('passwords', [])
# Get user environment
user_env = yaml.safe_load(swift_client.get_object(
name, constants.USER_ENVIRONMENT)[1])
except swift_exc.ClientException:
pass
passwords = env.get('passwords', [])
# TODO(dmatthews): Removing the existing plan files should probably be
# a Mistral action.
print("Removing the current plan files")
@ -183,10 +195,24 @@ def update_plan_from_templates(clients, name, tht_root, roles_file=None,
print("Uploading new plan files")
_upload_templates(swift_client, name, tht_root, roles_file, plan_env_file,
networks_file)
# Update password and user parameters into swift
_update_passwords(swift_client, name, passwords)
_update_user_environment(swift_client, name, user_env,
constants.USER_ENVIRONMENT)
update_deployment_plan(clients, container=name,
generate_passwords=generate_passwords,
source_url=None)
source_url=None, plan_environment=env)
def _update_user_environment(swift_client, name, user_params, filename):
if user_params:
try:
swift_client.put_object(name,
filename,
yaml.safe_dump(user_params,
default_flow_style=False))
except swift_exc.ClientException:
LOG.debug("Unable to put %s in %s", filename, name)
def _update_passwords(swift_client, name, passwords):