Drop minion

This is no longer valid with the switch to ephemeral heat for
deployments.

Depends-On: https://review.rdoproject.org/r/c/openstack/tripleoclient-distgit/+/35977
Change-Id: Id3e21879c0fb831583ec41804e99da8cffcb9d86
(cherry picked from commit 4dbb45315b)
This commit is contained in:
Alex Schultz 2021-09-30 10:09:38 -06:00 committed by Sandeep Yadav
parent d0a9648d81
commit c22f23a075
10 changed files with 0 additions and 1239 deletions

View File

@ -100,8 +100,6 @@ openstack.tripleoclient.v2 =
tripleo_container_image_prepare_default = tripleoclient.v1.container_image:TripleOImagePrepareDefault
undercloud_install = tripleoclient.v1.undercloud:InstallUndercloud
undercloud_upgrade = tripleoclient.v1.undercloud:UpgradeUndercloud
undercloud_minion_install = tripleoclient.v1.undercloud_minion:InstallUndercloudMinion
undercloud_minion_upgrade = tripleoclient.v1.undercloud_minion:UpgradeUndercloudMinion
undercloud_backup = tripleoclient.v1.undercloud_backup:BackupUndercloud
tripleo_validator_group_info = tripleoclient.v1.tripleo_validator:TripleOValidatorGroupInfo
tripleo_validator_list = tripleoclient.v1.tripleo_validator:TripleOValidatorList
@ -114,7 +112,6 @@ openstack.tripleoclient.v2 =
oslo.config.opts =
undercloud_config = tripleoclient.config.undercloud:list_opts
standalone_config = tripleoclient.config.standalone:list_opts
minion_config = tripleoclient.config.minion:list_opts
[flake8]
show-source = True

View File

@ -85,7 +85,6 @@ setenv =
commands =
oslo-config-generator --config-file config-generator/undercloud.conf
oslo-config-generator --config-file config-generator/standalone.conf
oslo-config-generator --config-file config-generator/minion.conf
[testenv:releasenotes]
deps =

View File

@ -1,158 +0,0 @@
# Copyright 2019 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
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
import copy
from osc_lib.i18n import _
from oslo_config import cfg
from tripleoclient import constants
from tripleoclient.config.standalone import StandaloneConfig
CONF = cfg.CONF
class MinionConfig(StandaloneConfig):
def get_minion_service_opts(self, heat_engine=True,
ironic_conductor=False):
_opts = [
cfg.BoolOpt('enable_heat_engine',
default=heat_engine,
help=_(
'Whether to install the Heat Engine service.')),
cfg.BoolOpt('enable_ironic_conductor',
default=ironic_conductor,
help=_(
'Whether to install the Ironic Conductor service. '
'This is currently disabled by default.')),
]
return self.sort_opts(_opts)
def get_base_opts(self):
_base_opts = super(MinionConfig, self).get_base_opts()
_opts = [
cfg.StrOpt('minion_log_file',
default=constants.MINION_LOG_FILE,
help=_(
'The path to a log file to store the '
'install/upgrade logs.'),
),
cfg.StrOpt('minion_hostname',
help=_(
'Fully qualified hostname (including domain) to '
'set on the Undercloud Minion. If left unset, the '
'current hostname will be used, but the user is '
'responsible for configuring all system hostname '
'settings appropriately. If set, the Undercloud '
'Minion install will configure all system hostname '
'settings.'),
),
cfg.StrOpt('minion_local_ip',
default='192.168.24.50/24',
help=_(
'IP information for the interface on the '
'Undercloud Minion. The IP portion '
'of the value will be assigned to the network '
'interface defined by local_interface, with the '
'netmask defined by the prefix portion of the '
'value.')
),
cfg.ListOpt('minion_nameservers',
default=[],
help=_(
'DNS nameserver(s) to configure on the Undercloud '
'Minion.')
),
cfg.ListOpt('minion_ntp_servers',
default=['0.pool.ntp.org', '1.pool.ntp.org',
'2.pool.ntp.org', '3.pool.ntp.org'],
help=_('List of ntp servers to use.')),
cfg.StrOpt('minion_timezone', default=None,
help=_('Host timezone to be used. If no timezone is '
'specified, the existing timezone configuration '
'is used.')),
cfg.StrOpt('minion_service_certificate',
default='',
help=_(
'TODO: '
'Certificate file to use for OpenStack service SSL '
'connections. Setting this enables SSL for the '
'OpenStack API endpoints, leaving it unset '
'disables SSL.')
),
cfg.StrOpt('minion_password_file',
default='tripleo-undercloud-passwords.yaml',
help=_(
'The name of the file to look for the passwords '
'used to connect to the Undercloud. We assume '
'this file is in output_dir if a fully qualified '
'path is not provided.')
),
cfg.StrOpt('minion_undercloud_output_file',
default='tripleo-undercloud-outputs.yaml',
help=_(
'The name of the file to look for the Undercloud '
'output file that contains configuration '
'information. We assume this file is in the folder '
'where the command is executed if a fully '
'qualified path is not provided.')
),
cfg.StrOpt('minion_local_interface',
default='eth1',
help=_('Network interface on the Undercloud Minion '
'that will be used for the services.')
),
cfg.IntOpt('minion_local_mtu',
default=1500,
help=_('MTU to use for the local_interface.')
),
cfg.BoolOpt('minion_debug',
default=True,
help=_(
'Whether to enable the debug log level for '
'OpenStack services and Container Image Prepare '
'step.')
),
cfg.BoolOpt('minion_enable_selinux',
default=True,
help=_('Enable or disable SELinux during the '
'deployment.')),
cfg.BoolOpt('minion_enable_validations',
default=True,
help=_(
'Run pre-flight checks when installing or '
'upgrading.')
),
]
return self.sort_opts(_base_opts + _opts)
def get_opts(self):
_base_opts = self.get_base_opts()
_service_opts = self.get_minion_service_opts()
return self.sort_opts(_base_opts + _service_opts)
def list_opts():
"""List config opts for oslo config generator"""
return [(None, copy.deepcopy(MinionConfig().get_opts()))]
def load_global_config():
"""Register MinionConfig options into global config"""
_opts = MinionConfig().get_opts()
CONF.register_opts(_opts)

View File

@ -1,103 +0,0 @@
# Copyright 2018 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
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
from tripleoclient.config.minion import MinionConfig
from tripleoclient.tests import base
class TestMinionConfig(base.TestCase):
def setUp(self):
super(TestMinionConfig, self).setUp()
# Get the class object to test
self.config = MinionConfig()
def test_get_base_opts(self):
ret = self.config.get_base_opts()
expected = ['cleanup',
'container_cli',
'container_healthcheck_disabled',
'container_images_file',
'container_insecure_registries',
'container_registry_mirror',
'custom_env_files',
'deployment_user',
'heat_container_image',
'heat_native',
'hieradata_override',
'minion_debug',
'minion_enable_selinux',
'minion_enable_validations',
'minion_hostname',
'minion_local_interface',
'minion_local_ip',
'minion_local_mtu',
'minion_log_file',
'minion_nameservers',
'minion_ntp_servers',
'minion_password_file',
'minion_service_certificate',
'minion_timezone',
'minion_undercloud_output_file',
'net_config_override',
'networks_file',
'output_dir',
'roles_file',
'templates']
self.assertEqual(expected, [x.name for x in ret])
def test_get_opts(self):
ret = self.config.get_opts()
expected = ['cleanup',
'container_cli',
'container_healthcheck_disabled',
'container_images_file',
'container_insecure_registries',
'container_registry_mirror',
'custom_env_files',
'deployment_user',
'enable_heat_engine',
'enable_ironic_conductor',
'heat_container_image',
'heat_native',
'hieradata_override',
'minion_debug',
'minion_enable_selinux',
'minion_enable_validations',
'minion_hostname',
'minion_local_interface',
'minion_local_ip',
'minion_local_mtu',
'minion_log_file',
'minion_nameservers',
'minion_ntp_servers',
'minion_password_file',
'minion_service_certificate',
'minion_timezone',
'minion_undercloud_output_file',
'net_config_override',
'networks_file',
'output_dir',
'roles_file',
'templates']
self.assertEqual(expected, [x.name for x in ret])
def test_get_minion_service_opts(self):
ret = self.config.get_minion_service_opts()
expected = {'enable_heat_engine': True,
'enable_ironic_conductor': False}
self.assertEqual(sorted(expected.keys()), [x.name for x in ret])
for x in ret:
self.assertEqual(expected[x.name], x.default, "%s config not %s" %
(x.name, expected[x.name]))

View File

@ -1,248 +0,0 @@
# Copyright 2019 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
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
import mock
import sys
import tempfile
import yaml
from oslo_config import cfg
from oslo_config import fixture as oslo_fixture
from tripleo_common.image import kolla_builder
from tripleoclient.tests import base
from tripleoclient.v1 import minion_config
class TestMinionDeploy(base.TestCase):
def setUp(self):
super(TestMinionDeploy, self).setUp()
self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF))
self.conf.set_default('output_dir', '/home/stack')
# set timezone so we don't have to mock it everywhere
self.conf.set_default('minion_timezone', 'UTC')
@mock.patch('tripleoclient.v1.minion_config._process_undercloud_passwords')
@mock.patch('tripleoclient.v1.undercloud_preflight.minion_check')
@mock.patch('os.makedirs', return_value=None)
@mock.patch('tripleoclient.v1.minion_config._process_undercloud_output',
return_value='output.yaml')
@mock.patch('tripleoclient.v1.minion_config._container_images_config')
@mock.patch('tripleoclient.utils.write_env_file')
@mock.patch('tripleoclient.utils.get_deployment_user')
@mock.patch('tripleoclient.utils.load_config')
def test_basic_deploy(self, mock_load_config, mock_get_user,
mock_write_env, mock_undercloud_output,
mock_images_config, mock_isdir,
mock_check, mock_pass):
mock_get_user.return_value = 'foo'
cmd = minion_config.prepare_minion_deploy()
expected_cmd = ['sudo', '--preserve-env',
'openstack', 'tripleo', 'deploy',
'--standalone', '--standalone-role',
'UndercloudMinion', '--stack', 'minion',
'-r',
'/usr/share/openstack-tripleo-heat-templates/roles/'
'UndercloudMinion.yaml',
'--local-ip=192.168.24.50/24',
'--templates='
'/usr/share/openstack-tripleo-heat-templates/',
'--networks-file='
'/usr/share/openstack-tripleo-heat-templates/'
'network_data_undercloud.yaml',
'-e', 'output.yaml',
'--heat-native',
'-e', '/usr/share/openstack-tripleo-heat-templates/'
'environments/undercloud/undercloud-minion.yaml',
'-e', '/usr/share/openstack-tripleo-heat-templates/'
'environments/use-dns-for-vips.yaml',
'-e', '/usr/share/openstack-tripleo-heat-templates/'
'environments/services/heat-engine.yaml',
'--deployment-user', 'foo',
'--output-dir=/home/stack',
'-e', '/home/stack/tripleo-config-generated-env-files/'
'minion_parameters.yaml',
'--log-file=install-minion.log',
'-e', '/usr/share/openstack-tripleo-heat-templates/'
'minion-stack-vstate-dropin.yaml']
self.assertEqual(expected_cmd, cmd)
env_data = {
'PythonInterpreter': sys.executable,
'ContainerImagePrepareDebug': True,
'Debug': True,
'UndercloudMinionLocalMtu': 1500,
'ContainerHealthcheckDisabled': False,
'NeutronPublicInterface': 'eth1',
'SELinuxMode': 'enforcing',
'NtpServer': ['0.pool.ntp.org',
'1.pool.ntp.org',
'2.pool.ntp.org',
'3.pool.ntp.org'],
'TimeZone': 'UTC',
'DockerInsecureRegistryAddress': ['192.168.24.50:8787'],
'ContainerCli': 'podman',
'LocalContainerRegistry': '192.168.24.50',
'DeploymentUser': 'foo'}
mock_write_env.assert_called_once_with(
env_data, '/home/stack/tripleo-config-generated-env-files/'
'minion_parameters.yaml', {})
@mock.patch('tripleoclient.v1.minion_config._process_undercloud_passwords')
@mock.patch('tripleoclient.v1.undercloud_preflight.minion_check')
@mock.patch('os.path.exists', return_value=True)
@mock.patch('os.makedirs', return_value=None)
@mock.patch('tripleoclient.v1.minion_config._process_undercloud_output',
return_value='output.yaml')
@mock.patch('tripleoclient.v1.minion_config._container_images_config')
@mock.patch('tripleoclient.utils.write_env_file')
@mock.patch('tripleoclient.utils.load_config')
def test_configured_deploy(self, mock_load_config,
mock_write_env, mock_undercloud_output,
mock_images_config, mock_isdir, mock_exists,
mock_check, mock_pass):
self.conf.set_default('deployment_user', 'bar')
self.conf.set_default('enable_heat_engine', False)
self.conf.set_default('enable_ironic_conductor', True)
self.conf.set_default('hieradata_override', '/data.yaml')
self.conf.set_default('minion_debug', False)
self.conf.set_default('minion_enable_selinux', False)
self.conf.set_default('minion_local_interface', 'enp0s4')
self.conf.set_default('minion_local_ip', '1.1.1.1/24')
self.conf.set_default('minion_local_mtu', '1350')
self.conf.set_default('minion_ntp_servers', ['pool.ntp.org'])
self.conf.set_default('networks_file', 'network.yaml')
self.conf.set_default('output_dir', '/bar')
self.conf.set_default('templates', '/foo')
cmd = minion_config.prepare_minion_deploy()
expected_cmd = ['sudo', '--preserve-env',
'openstack', 'tripleo', 'deploy',
'--standalone', '--standalone-role',
'UndercloudMinion', '--stack', 'minion',
'-r', '/foo/roles/UndercloudMinion.yaml',
'--local-ip=1.1.1.1/24',
'--templates=/foo',
'--networks-file=network.yaml',
'-e', 'output.yaml',
'--heat-native',
'-e', '/foo/environments/undercloud/'
'undercloud-minion.yaml',
'-e', '/foo/environments/use-dns-for-vips.yaml',
'-e', '/foo/environments/services/'
'ironic-conductor.yaml',
'--deployment-user', 'bar',
'--output-dir=/bar',
'-e', '/bar/tripleo-config-generated-env-files/'
'minion_parameters.yaml',
'--hieradata-override=/data.yaml',
'--log-file=install-minion.log',
'-e', '/foo/minion-stack-vstate-dropin.yaml']
self.assertEqual(expected_cmd, cmd)
env_data = {
'PythonInterpreter': sys.executable,
'ContainerImagePrepareDebug': False,
'Debug': False,
'UndercloudMinionLocalMtu': 1350,
'ContainerHealthcheckDisabled': False,
'NeutronPublicInterface': 'enp0s4',
'SELinuxMode': 'permissive',
'NtpServer': ['pool.ntp.org'],
'TimeZone': 'UTC',
'DockerInsecureRegistryAddress': ['1.1.1.1:8787'],
'ContainerCli': 'podman',
'LocalContainerRegistry': '1.1.1.1',
'DeploymentUser': 'bar'}
mock_write_env.assert_called_once_with(
env_data, '/bar/tripleo-config-generated-env-files/'
'minion_parameters.yaml', {})
class TestMinionContainerImageConfig(base.TestCase):
def setUp(self):
super(TestMinionContainerImageConfig, self).setUp()
conf_keys = (
'container_images_file',
)
self.conf = mock.Mock(**{key: getattr(minion_config.CONF, key)
for key in conf_keys})
@mock.patch('shutil.copy')
def test_defaults(self, mock_copy):
env = {}
deploy_args = []
cip_default = getattr(kolla_builder,
'CONTAINER_IMAGE_PREPARE_PARAM', None)
self.addCleanup(setattr, kolla_builder,
'CONTAINER_IMAGE_PREPARE_PARAM', cip_default)
setattr(kolla_builder, 'CONTAINER_IMAGE_PREPARE_PARAM', [{
'set': {
'namespace': 'one',
'name_prefix': 'two',
'name_suffix': 'three',
'tag': 'four',
},
'tag_from_label': 'five',
}])
minion_config._container_images_config(self.conf, deploy_args,
env, None)
self.assertEqual([], deploy_args)
cip = env['ContainerImagePrepare'][0]
set = cip['set']
self.assertEqual(
'one', set['namespace'])
self.assertEqual(
'two', set['name_prefix'])
self.assertEqual(
'three', set['name_suffix'])
self.assertEqual(
'four', set['tag'])
self.assertEqual(
'five', cip['tag_from_label'])
@mock.patch('shutil.copy')
def test_container_images_file(self, mock_copy):
env = {}
deploy_args = []
self.conf.container_images_file = '/tmp/container_images_file.yaml'
minion_config._container_images_config(self.conf, deploy_args,
env, None)
self.assertEqual(['-e', '/tmp/container_images_file.yaml'],
deploy_args)
self.assertEqual({}, env)
@mock.patch('shutil.copy')
def test_custom(self, mock_copy):
env = {}
deploy_args = []
with tempfile.NamedTemporaryFile(mode='w') as f:
yaml.dump({
'parameter_defaults': {'ContainerImagePrepare': [{
'set': {
'namespace': 'one',
'name_prefix': 'two',
'name_suffix': 'three',
'tag': 'four',
},
'tag_from_label': 'five',
}]}
}, f)
self.conf.container_images_file = f.name
cif_name = f.name
minion_config._container_images_config(
self.conf, deploy_args, env, None)
self.assertEqual(['-e', cif_name], deploy_args)

View File

@ -1,130 +0,0 @@
# Copyright 2015 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
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
import fixtures
import mock
from oslo_config import cfg
from oslo_config import fixture as oslo_fixture
from tripleoclient.tests.v1.test_plugin import TestPluginV1
# Load the plugin init module for the plugin list and show commands
from tripleoclient.v1 import undercloud_minion
class FakePluginV1Client(object):
def __init__(self, **kwargs):
self.auth_token = kwargs['token']
self.management_url = kwargs['endpoint']
class TestMinionInstall(TestPluginV1):
def setUp(self):
super(TestMinionInstall, self).setUp()
self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF))
# don't actually load config from ~/minion.conf
self.mock_config_load = self.useFixture(
fixtures.MockPatch('tripleoclient.utils.load_config'))
# Get the command object to test
app_args = mock.Mock()
app_args.verbose_level = 1
self.cmd = undercloud_minion.InstallUndercloudMinion(self.app,
app_args)
@mock.patch('tripleoclient.v1.minion_config.prepare_minion_deploy')
@mock.patch('tripleoclient.utils.ensure_run_as_normal_user')
@mock.patch('tripleoclient.utils.configure_logging')
@mock.patch('subprocess.check_call', autospec=True)
def test_take_action(self, mock_subprocess, mock_logging, mock_usercheck,
mock_prepare_deploy):
arglist = []
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
mock_prepare_deploy.return_value = ['foo']
self.cmd.take_action(parsed_args)
mock_prepare_deploy.assert_called_once_with(
dry_run=False, force_stack_update=False, no_validations=False,
verbose_level=1)
mock_usercheck.assert_called_once()
mock_subprocess.assert_called_with(['foo'])
@mock.patch('tripleoclient.v1.minion_config.prepare_minion_deploy')
@mock.patch('tripleoclient.utils.ensure_run_as_normal_user')
@mock.patch('tripleoclient.utils.configure_logging')
@mock.patch('subprocess.check_call', autospec=True)
def test_take_action_dry_run(self, mock_subprocess, mock_logging,
mock_usercheck, mock_prepare_deploy):
arglist = ['--dry-run']
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
mock_prepare_deploy.assert_called_once_with(
dry_run=True, force_stack_update=False, no_validations=True,
verbose_level=1)
mock_usercheck.assert_called_once()
self.assertCountEqual(mock_subprocess.call_args_list, [])
class TestMinionUpgrade(TestPluginV1):
def setUp(self):
super(TestMinionUpgrade, self).setUp()
self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF))
# don't actually load config from ~/minion.conf
self.mock_config_load = self.useFixture(
fixtures.MockPatch('tripleoclient.utils.load_config'))
# Get the command object to test
app_args = mock.Mock()
app_args.verbose_level = 1
self.cmd = undercloud_minion.UpgradeUndercloudMinion(self.app,
app_args)
@mock.patch('tripleoclient.v1.minion_config.prepare_minion_deploy')
@mock.patch('tripleoclient.utils.ensure_run_as_normal_user')
@mock.patch('tripleoclient.utils.configure_logging')
@mock.patch('subprocess.check_call', autospec=True)
def test_take_action(self, mock_subprocess, mock_logging, mock_usercheck,
mock_prepare_deploy):
arglist = []
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
mock_prepare_deploy.return_value = ['foo']
self.cmd.take_action(parsed_args)
mock_prepare_deploy.assert_called_once_with(
force_stack_update=False, no_validations=False, upgrade=True,
verbose_level=1, yes=False)
mock_usercheck.assert_called_once()
mock_subprocess.assert_called_with(['foo'])
@mock.patch('tripleoclient.v1.minion_config.prepare_minion_deploy')
@mock.patch('tripleoclient.utils.ensure_run_as_normal_user')
@mock.patch('tripleoclient.utils.configure_logging')
@mock.patch('subprocess.check_call', autospec=True)
def test_take_action_yes(self, mock_subprocess, mock_logging,
mock_usercheck, mock_prepare_deploy):
arglist = ['--yes']
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
mock_prepare_deploy.return_value = ['foo']
self.cmd.take_action(parsed_args)
mock_prepare_deploy.assert_called_once_with(
force_stack_update=False, no_validations=False, upgrade=True,
verbose_level=1, yes=True)
mock_usercheck.assert_called_once()
mock_subprocess.assert_called_with(['foo'])

View File

@ -1,421 +0,0 @@
# Copyright 2019 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
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
"""Plugin action implementation"""
import json
import logging
import os
import shutil
import sys
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from jinja2 import Environment
from jinja2 import FileSystemLoader
from osc_lib.i18n import _
from oslo_config import cfg
from tripleo_common.image import kolla_builder
from tripleoclient.config.minion import load_global_config
from tripleoclient.config.minion import MinionConfig
from tripleoclient import constants
from tripleoclient import exceptions
from tripleoclient import utils
from tripleoclient.v1 import undercloud_preflight
# Provides mappings for some of the instack_env tags to minion heat
# params or minion.conf opts known here (as a fallback), needed to maintain
# feature parity with instack net config override templates.
# TODO(bogdando): all of the needed mappings should be wired-in, eventually
# NOTE(aschultz): this is used by the custom netconfig still, even though
# the minion config is new.
INSTACK_NETCONF_MAPPING = {
'LOCAL_INTERFACE': 'minion_local_interface',
'LOCAL_IP': 'minion_local_ip',
'LOCAL_MTU': 'UndercloudMinionLocalMtu',
'PUBLIC_INTERFACE_IP': 'minion_local_ip', # can't be 'CloudName'
'UNDERCLOUD_NAMESERVERS': 'minion_nameservers',
'SUBNETS_STATIC_ROUTES': 'ControlPlaneStaticRoutes',
}
PARAMETER_MAPPING = {
'minion_debug': 'Debug',
'minion_local_mtu': 'UndercloudMinionLocalMtu',
'container_healthcheck_disabled': 'ContainerHealthcheckDisabled',
'minion_local_interface': 'NeutronPublicInterface',
}
SUBNET_PARAMETER_MAPPING = {
'cidr': 'NetworkCidr',
'gateway': 'NetworkGateway',
'host_routes': 'HostRoutes'
}
THT_HOME = os.environ.get('THT_HOME',
"/usr/share/openstack-tripleo-heat-templates/")
USER_HOME = os.environ.get('HOME', '')
CONF = cfg.CONF
# When adding new options to the lists below, make sure to regenerate the
# sample config by running "tox -e genconfig" in the project root.
ci_defaults = kolla_builder.container_images_prepare_defaults()
config = MinionConfig()
# Routed subnets
_opts = config.get_opts()
load_global_config()
LOG = logging.getLogger(__name__ + ".minion_config")
def _get_jinja_env_source(f):
path, filename = os.path.split(f)
env = Environment(loader=FileSystemLoader(path))
src = env.loader.get_source(env, filename)[0]
return (env, src)
def _process_undercloud_output(templates_dir, output_file_path):
"""copy the undercloud output file to our work dir"""
output_file = os.path.join(constants.MINION_OUTPUT_DIR, output_file_path)
env_file = os.path.join(templates_dir, 'tripleo-undercloud-base.yaml')
if os.path.exists(output_file):
src_file = output_file
elif os.path.exists(output_file_path):
src_file = output_file_path
else:
raise exceptions.DeploymentError('Cannot locate undercloud output '
'file {}'.format(output_file_path))
try:
shutil.copy(os.path.abspath(src_file), env_file)
except Exception:
msg = _('Cannot copy undercloud output file %s into a '
'tempdir!') % src_file
LOG.error(msg)
raise exceptions.DeploymentError(msg)
return env_file
def _process_undercloud_passwords(src_file, dest_file):
try:
shutil.copy(os.path.abspath(src_file), dest_file)
except Exception:
msg = _('Cannot copy undercloud password file %(src)s to '
'%(dest)s') % {'src': src_file, 'dest': dest_file}
LOG.error(msg)
raise exceptions.DeploymentError(msg)
def prepare_minion_deploy(upgrade=False, no_validations=False,
verbose_level=1, yes=False,
force_stack_update=False, dry_run=False):
"""Prepare Minion deploy command based on minion.conf"""
env_data = {}
registry_overwrites = {}
deploy_args = []
# Fetch configuration and use its log file param to add logging to a file
utils.load_config(CONF, constants.MINION_CONF_PATH)
utils.configure_logging(LOG, verbose_level, CONF['minion_log_file'])
# NOTE(bogdando): the generated env files are stored another path then
# picked up later.
# NOTE(aschultz): We copy this into the tht root that we save because
# we move any user provided environment files into this root later.
if not CONF.get('output_dir'):
output_dir = os.path.join(constants.MINION_OUTPUT_DIR,
'tripleo-deploy',
'minion')
else:
output_dir = CONF['output_dir']
tempdir = os.path.join(os.path.abspath(output_dir),
'tripleo-config-generated-env-files')
utils.makedirs(tempdir)
env_data['PythonInterpreter'] = sys.executable
env_data['ContainerImagePrepareDebug'] = CONF['minion_debug']
for param_key, param_value in PARAMETER_MAPPING.items():
if param_key in CONF.keys():
env_data[param_value] = CONF[param_key]
# Parse the minion.conf options to include necessary args and
# yaml files for minion deploy command
if CONF.get('minion_enable_selinux'):
env_data['SELinuxMode'] = 'enforcing'
else:
env_data['SELinuxMode'] = 'permissive'
if CONF.get('minion_ntp_servers', None):
env_data['NtpServer'] = CONF['minion_ntp_servers']
if CONF.get('minion_timezone', None):
env_data['TimeZone'] = CONF['minion_timezone']
else:
env_data['TimeZone'] = utils.get_local_timezone()
# TODO(aschultz): fix this logic, look it up out of undercloud-outputs.yaml
env_data['DockerInsecureRegistryAddress'] = [
'%s:8787' % CONF['minion_local_ip'].split('/')[0]]
env_data['DockerInsecureRegistryAddress'].extend(
CONF['container_insecure_registries'])
env_data['ContainerCli'] = CONF['container_cli']
if CONF.get('container_registry_mirror', None):
env_data['DockerRegistryMirror'] = CONF['container_registry_mirror']
# This parameter the IP address used to bind the local container registry
env_data['LocalContainerRegistry'] = CONF['minion_local_ip'].split('/')[0]
if CONF.get('minion_local_ip', None):
deploy_args.append('--local-ip=%s' % CONF['minion_local_ip'])
if CONF.get('templates', None):
tht_templates = CONF['templates']
deploy_args.append('--templates=%s' % tht_templates)
else:
tht_templates = THT_HOME
deploy_args.append('--templates=%s' % THT_HOME)
if CONF.get('roles_file', constants.MINION_ROLES_FILE):
deploy_args.append('--roles-file=%s' % CONF['roles_file'])
if CONF.get('networks_file'):
deploy_args.append('--networks-file=%s' % CONF['networks_file'])
else:
deploy_args.append('--networks-file=%s' %
os.path.join(tht_templates,
constants.UNDERCLOUD_NETWORKS_FILE))
if yes:
deploy_args += ['-y']
# copy the undercloud output file into our working dir and include it
output_file = _process_undercloud_output(
tempdir, CONF['minion_undercloud_output_file'])
deploy_args += ['-e', output_file]
# copy undercloud password file (the configuration is minion_password_file
# to the place that triple deploy looks for it
# tripleo-<stack name>-passwords.yaml)
_process_undercloud_passwords(
CONF['minion_password_file'],
os.path.join(output_dir, 'tripleo-minion-passwords.yaml'))
if upgrade:
# TODO(aschultz): validate minion upgrade, should be the same as the
# undercloud one.
deploy_args += [
'--upgrade',
'-e', os.path.join(
tht_templates,
"environments/lifecycle/undercloud-upgrade-prepare.yaml")]
if not CONF.get('heat_native', False):
deploy_args.append('--heat-native=False')
else:
deploy_args.append('--heat-native')
if CONF.get('heat_container_image'):
deploy_args.append('--heat-container-image=%s'
% CONF['heat_container_image'])
# These should be loaded first so we can override all the bits later
deploy_args += [
"-e", os.path.join(tht_templates,
'environments/undercloud/undercloud-minion.yaml'),
'-e', os.path.join(tht_templates, 'environments/use-dns-for-vips.yaml')
]
# If a container images file is used, copy it into the tempdir to make it
# later into other deployment artifacts and user-provided files.
_container_images_config(CONF, deploy_args, env_data, tempdir)
if CONF.get('enable_heat_engine'):
deploy_args += ['-e', os.path.join(
tht_templates, "environments/services/heat-engine.yaml")]
if CONF.get('enable_ironic_conductor'):
deploy_args += ['-e', os.path.join(
tht_templates, "environments/services/ironic-conductor.yaml")]
if CONF.get('minion_service_certificate'):
# We assume that the certificate is trusted
env_data['InternalTLSCAFile'] = ''
env_data.update(
_get_public_tls_parameters(
CONF.get('minion_service_certificate')))
u = CONF.get('deployment_user') or utils.get_deployment_user()
env_data['DeploymentUser'] = u
# TODO(cjeanner) drop that once using oslo.privsep
deploy_args += ['--deployment-user', u]
deploy_args += ['--output-dir=%s' % output_dir]
utils.makedirs(output_dir)
# TODO(aschultz): move this to a central class
if CONF.get('net_config_override', None):
data_file = CONF['net_config_override']
if os.path.abspath(data_file) != data_file:
data_file = os.path.join(USER_HOME, data_file)
if not os.path.exists(data_file):
msg = _("Could not find net_config_override file '%s'") % data_file
LOG.error(msg)
raise RuntimeError(msg)
# NOTE(bogdando): Process templated net config override data:
# * get a list of used instack_env j2 tags (j2 vars, like {{foo}}),
# * fetch values for the tags from the known mappins,
# * raise, if there is unmatched tags left
# * render the template into a JSON dict
net_config_env, template_source = _get_jinja_env_source(data_file)
# Create rendering context from the known to be present mappings for
# identified instack_env tags to generated in env_data minion heat
# params. Fall back to config opts, when env_data misses a param.
context = {}
for tag in INSTACK_NETCONF_MAPPING.keys():
mapped_value = INSTACK_NETCONF_MAPPING[tag]
if mapped_value in env_data.keys() or mapped_value in CONF.keys():
try:
context[tag] = CONF[mapped_value]
except cfg.NoSuchOptError:
context[tag] = env_data.get(mapped_value, None)
# this returns a unicode string, convert it in into json
net_config_str = net_config_env.get_template(
os.path.split(data_file)[-1]).render(context).replace(
"'", '"').replace('&quot;', '"')
try:
net_config_json = json.loads(net_config_str)
except ValueError:
net_config_json = json.loads("{%s}" % net_config_str)
if 'network_config' not in net_config_json:
msg = ('Unsupported data format in net_config_override '
'file %s: %s' % (data_file, net_config_str))
LOG.error(msg)
raise exceptions.DeploymentError(msg)
env_data['UndercloudNetConfigOverride'] = net_config_json
params_file = os.path.join(tempdir, 'minion_parameters.yaml')
utils.write_env_file(env_data, params_file, registry_overwrites)
deploy_args += ['-e', params_file]
if CONF.get('hieradata_override', None):
data_file = CONF['hieradata_override']
if os.path.abspath(data_file) != data_file:
data_file = os.path.join(USER_HOME, data_file)
if not os.path.exists(data_file):
msg = _("Could not find hieradata_override file '%s'") % data_file
LOG.error(msg)
raise RuntimeError(msg)
deploy_args += ['--hieradata-override=%s' % data_file]
if CONF.get('minion_hostname'):
utils.set_hostname(CONF.get('minion_hostname'))
if CONF.get('minion_enable_validations') and not no_validations:
undercloud_preflight.minion_check(verbose_level, upgrade)
if CONF.get('custom_env_files'):
for custom_file in CONF['custom_env_files']:
deploy_args += ['-e', custom_file]
if verbose_level > 1:
deploy_args.append('--debug')
deploy_args.append('--log-file=%s' % CONF['minion_log_file'])
# Always add a drop-in for the ephemeral minion heat stack
# virtual state tracking (the actual file will be created later)
stack_vstate_dropin = os.path.join(
tht_templates, 'minion-stack-vstate-dropin.yaml')
deploy_args += ["-e", stack_vstate_dropin]
if force_stack_update:
deploy_args += ["--force-stack-update"]
roles_file = os.path.join(tht_templates, constants.MINION_ROLES_FILE)
cmd = ["sudo", "--preserve-env", "openstack", "tripleo", "deploy",
"--standalone", "--standalone-role", "UndercloudMinion", "--stack",
"minion", "-r", roles_file]
cmd += deploy_args[:]
# In dry-run, also report the expected heat stack virtual state/action
if dry_run:
stack_update_mark = os.path.join(
constants.STANDALONE_EPHEMERAL_STACK_VSTATE,
'update_mark_minion')
if os.path.isfile(stack_update_mark) or force_stack_update:
LOG.warning(_('The heat stack minion virtual state/action '
' would be UPDATE'))
return cmd
def _get_public_tls_parameters(service_certificate_path):
with open(service_certificate_path, "rb") as pem_file:
pem_data = pem_file.read()
cert = x509.load_pem_x509_certificate(pem_data, default_backend())
private_key = serialization.load_pem_private_key(
pem_data,
password=None,
backend=default_backend())
key_pem = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption())
cert_pem = cert.public_bytes(serialization.Encoding.PEM)
return {
'SSLCertificate': cert_pem,
'SSLKey': key_pem
}
def _container_images_config(conf, deploy_args, env_data, tempdir):
if conf.container_images_file:
deploy_args += ['-e', conf.container_images_file]
try:
shutil.copy(os.path.abspath(conf.container_images_file), tempdir)
except Exception:
msg = _('Cannot copy a container images'
'file %s into a tempdir!') % conf.container_images_file
LOG.error(msg)
raise exceptions.DeploymentError(msg)
else:
# no images file was provided. Set a default ContainerImagePrepare
# parameter to trigger the preparation of the required container list
cip = kolla_builder.CONTAINER_IMAGE_PREPARE_PARAM
env_data['ContainerImagePrepare'] = cip

View File

@ -1,162 +0,0 @@
# Copyright 2019 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
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
"""Plugin action implementation"""
import argparse
import logging
import subprocess
from openstackclient.i18n import _
from oslo_config import cfg
from tripleoclient import command
from tripleoclient import constants
from tripleoclient import exceptions
from tripleoclient import utils
from tripleoclient.v1 import minion_config
MINION_FAILURE_MESSAGE = """
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
An error has occured while deploying the Undercloud Minion
See the previous output for details about what went wrong.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
"""
MINION_COMPLETION_MESSAGE = """
##########################################################
The Undercloud Minion has been successfully installed.
##########################################################
"""
MINION_UPGRADE_COMPLETION_MESSAGE = """
##########################################################
The Undercloud Minion has been successfully upgraded.
##########################################################
"""
class InstallUndercloudMinion(command.Command):
"""Install and setup the undercloud minion"""
auth_required = False
log = logging.getLogger(__name__ + ".InstallUndercloudMinion")
osloconfig = cfg.CONF
def get_parser(self, prog_name):
parser = argparse.ArgumentParser(
description=self.get_description(),
prog=prog_name,
add_help=False
)
parser.add_argument('--force-stack-update',
dest='force_stack_update',
action='store_true',
default=False,
help=_("Do a virtual update of the ephemeral "
"heat stack. New or failed deployments "
"always have the stack_action=CREATE. This "
"option enforces stack_action=UPDATE."),
)
parser.add_argument(
'--no-validations',
dest='no_validations',
action='store_true',
default=False,
help=_("Do not perform minion configuration validations"),
)
parser.add_argument(
'--dry-run',
dest='dry_run',
action='store_true',
default=False,
help=_("Print the install command instead of running it"),
)
parser.add_argument('-y', '--yes', default=False,
action='store_true',
help=_("Skip yes/no prompt (assume yes)."))
return parser
def take_action(self, parsed_args):
self.log.warning("[DEPRECATED] This command has been deprecated. "
"The minion functionality is no longer as necessary "
"with the move to nova-less provisioning.")
# Fetch configuration used to add logging to a file
utils.load_config(self.osloconfig, constants.MINION_CONF_PATH)
utils.configure_logging(self.log, self.app_args.verbose_level,
self.osloconfig['minion_log_file'])
self.log.debug("take_action(%s)" % parsed_args)
utils.ensure_run_as_normal_user()
no_validations = parsed_args.dry_run or parsed_args.no_validations
cmd = minion_config.prepare_minion_deploy(
no_validations=no_validations,
verbose_level=self.app_args.verbose_level,
force_stack_update=parsed_args.force_stack_update,
dry_run=parsed_args.dry_run)
self.log.warning("Running: %s" % ' '.join(cmd))
if not parsed_args.dry_run:
try:
subprocess.check_call(cmd)
self.log.warning(MINION_COMPLETION_MESSAGE)
except Exception as e:
self.log.error(MINION_FAILURE_MESSAGE)
self.log.error(e)
raise exceptions.DeploymentError(e)
class UpgradeUndercloudMinion(InstallUndercloudMinion):
"""Upgrade undercloud minion"""
auth_required = False
log = logging.getLogger(__name__ + ".UpgradeUndercloudMinion")
osloconfig = cfg.CONF
def take_action(self, parsed_args):
self.log.warning("[DEPRECATED] This command has been deprecated. "
"The minion functionality is no longer as necessary "
"with the move to nova-less provisioning.")
# Fetch configuration used to add logging to a file
utils.load_config(self.osloconfig, constants.MINION_CONF_PATH)
utils.configure_logging(self.log, self.app_args.verbose_level,
self.osloconfig['minion_log_file'])
self.log.debug("take action(%s)" % parsed_args)
utils.ensure_run_as_normal_user()
cmd = minion_config.\
prepare_minion_deploy(
upgrade=True,
yes=parsed_args.yes,
no_validations=parsed_args.
no_validations,
verbose_level=self.app_args.verbose_level,
force_stack_update=parsed_args.force_stack_update)
self.log.warning("Running: %s" % ' '.join(cmd))
if not parsed_args.dry_run:
try:
subprocess.check_call(cmd)
self.log.warning(MINION_UPGRADE_COMPLETION_MESSAGE)
except Exception as e:
self.log.error(MINION_FAILURE_MESSAGE)
self.log.error(e)
raise exceptions.DeploymentError(e)

View File

@ -18,16 +18,3 @@
- openstack-tox-pep8
- openstack-tox-py36
- openstack-tox-py37
- tripleo-ci-centos-8-containers-undercloud-minion:
vars:
consumer_job: true
build_container_images: false
remove_tags:
- build
dependencies:
- tripleo-ci-centos-8-content-provider
files:
- ^config-generator/.*minion.*$
- ^tripleoclient/config/.*minion.*$
- ^tripleoclient/v1/.*minion.*$
- ^tripleoclient/v1/tripleo_deploy.py$