diff --git a/setup.cfg b/setup.cfg index d6f705479..72932933d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -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 diff --git a/tox.ini b/tox.ini index 095d53ede..5b9ec0f1f 100644 --- a/tox.ini +++ b/tox.ini @@ -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 = diff --git a/tripleoclient/config/minion.py b/tripleoclient/config/minion.py deleted file mode 100644 index 0bb33a558..000000000 --- a/tripleoclient/config/minion.py +++ /dev/null @@ -1,157 +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', - help=_('Host timezone to be used. If no timezone is ' - 'specified, the existing timezone configuration ' - 'is used.')), - cfg.StrOpt('minion_service_certificate', - 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) diff --git a/tripleoclient/tests/config/test_config_minion.py b/tripleoclient/tests/config/test_config_minion.py deleted file mode 100644 index 62dc76cb8..000000000 --- a/tripleoclient/tests/config/test_config_minion.py +++ /dev/null @@ -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])) diff --git a/tripleoclient/tests/v1/undercloud/minion/__init__.py b/tripleoclient/tests/v1/undercloud/minion/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tripleoclient/tests/v1/undercloud/minion/test_config.py b/tripleoclient/tests/v1/undercloud/minion/test_config.py deleted file mode 100644 index 83581ed77..000000000 --- a/tripleoclient/tests/v1/undercloud/minion/test_config.py +++ /dev/null @@ -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-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-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) diff --git a/tripleoclient/tests/v1/undercloud/minion/test_install.py b/tripleoclient/tests/v1/undercloud/minion/test_install.py deleted file mode 100644 index aef372963..000000000 --- a/tripleoclient/tests/v1/undercloud/minion/test_install.py +++ /dev/null @@ -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']) diff --git a/tripleoclient/v1/minion_config.py b/tripleoclient/v1/minion_config.py deleted file mode 100644 index aaca59450..000000000 --- a/tripleoclient/v1/minion_config.py +++ /dev/null @@ -1,413 +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'): - env_data['NtpServer'] = CONF['minion_ntp_servers'] - - env_data['TimeZone'] = (CONF.get('minion_timezone') or - 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'): - 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'): - deploy_args.append('--local-ip=%s' % CONF['minion_local_ip']) - - tht_templates = CONF.get('templates') or THT_HOME - deploy_args.append('--templates=%s' % tht_templates) - - if CONF.get('roles_file'): - deploy_args.append('--roles-file=%s' % CONF['roles_file']) - - networks_file = (CONF.get('networks_file') or - os.path.join(tht_templates, - constants.UNDERCLOUD_NETWORKS_FILE)) - deploy_args.append('--networks-file=%s' % 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--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'): - 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'): - 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) - - # 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('"', '"') - 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'): - 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-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 diff --git a/tripleoclient/v1/undercloud_minion.py b/tripleoclient/v1/undercloud_minion.py deleted file mode 100644 index e5abe471a..000000000 --- a/tripleoclient/v1/undercloud_minion.py +++ /dev/null @@ -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) diff --git a/zuul.d/layout.yaml b/zuul.d/layout.yaml index 0d15272d9..30d40e44f 100644 --- a/zuul.d/layout.yaml +++ b/zuul.d/layout.yaml @@ -19,19 +19,6 @@ - openstack-tox-pep8 - openstack-tox-py36 - openstack-tox-py38 - - 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$ - tripleo-ci-centos-8-standalone: vars: enable_validation: true