diff --git a/tripleoclient/config/__init__.py b/tripleoclient/config/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tripleoclient/config/base.py b/tripleoclient/config/base.py new file mode 100644 index 000000000..70d8b448c --- /dev/null +++ b/tripleoclient/config/base.py @@ -0,0 +1,46 @@ +# 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 oslo_config import cfg +from tripleoclient import constants + + +class BaseConfig(object): + + def sort_opts(self, opts): + """Sort oslo config options by name + + :param opts: list of olo cfg opts + :return list - sorted by name + """ + def sort_cfg(cfg): + return cfg.name + return sorted(opts, key=sort_cfg) + + def get_base_opts(self): + _opts = [ + # TODO(aschultz): rename undercloud_output_dir + cfg.StrOpt('output_dir', + default=constants.UNDERCLOUD_OUTPUT_DIR, + help=( + 'Directory to output state, processed heat ' + 'templates, ansible deployment files.'), + ), + cfg.BoolOpt('cleanup', + default=False, + help=('Cleanup temporary files'), + ), + ] + return self.sort_opts(_opts) diff --git a/tripleoclient/config/standalone.py b/tripleoclient/config/standalone.py new file mode 100644 index 000000000..0d9db788b --- /dev/null +++ b/tripleoclient/config/standalone.py @@ -0,0 +1,191 @@ +# 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 oslo_config import cfg +from tripleoclient.config.base import BaseConfig + +NETCONFIG_TAGS_EXAMPLE = """ +"network_config": [ + { + "type": "ovs_bridge", + "name": "br-ctlplane", + "ovs_extra": [ + "br-set-external-id br-ctlplane bridge-id br-ctlplane" + ], + "members": [ + { + "type": "interface", + "name": "{{LOCAL_INTERFACE}}", + "primary": "true", + "mtu": {{LOCAL_MTU}}, + "dns_servers": {{UNDERCLOUD_NAMESERVERS}} + } + ], + "addresses": [ + { + "ip_netmask": "{{PUBLIC_INTERFACE_IP}}" + } + ], + "routes": {{SUBNETS_STATIC_ROUTES}}, + "mtu": {{LOCAL_MTU}} +} +] +""" + + +class StandaloneConfig(BaseConfig): + + def get_enable_service_opts(self, cinder=False, ironic=False, + ironic_inspector=False, mistral=False, + novajoin=False, tempest=False, + telemetry=False, tripleo_ui=False, + validations=False, zaqar=False): + _opts = [ + # service enablement + cfg.BoolOpt('enable_cinder', + default=cinder, + help=( + 'Whether to install the Volume service. It is not ' + 'currently used in the undercloud.')), + cfg.BoolOpt('enable_ironic', + default=ironic, + help=('Whether to enable the ironic service.')), + cfg.BoolOpt('enable_ironic_inspector', + default=ironic_inspector, + help=( + 'Whether to enable the ironic inspector service.') + ), + cfg.BoolOpt('enable_mistral', + default=mistral, + help=('Whether to enable the mistral service.')), + cfg.BoolOpt('enable_novajoin', + default=novajoin, + help=('Whether to install novajoin metadata service ' + 'in the Undercloud.') + ), + cfg.BoolOpt('enable_tempest', + default=tempest, + help=('Whether to install Tempest in the Undercloud.' + 'This is a no-op for containerized undercloud.') + ), + cfg.BoolOpt('enable_telemetry', + default=telemetry, + help=('Whether to install Telemetry services ' + '(ceilometer, gnocchi, aodh, panko ) in the ' + 'Undercloud.') + ), + cfg.BoolOpt('enable_ui', + default=tripleo_ui, + help=('Whether to install the TripleO UI.') + ), + cfg.BoolOpt('enable_validations', + default=validations, + help=( + 'Whether to install requirements to run the ' + 'TripleO validations.') + ), + cfg.BoolOpt('enable_zaqar', + default=zaqar, + help=('Whether to enable the zaqar service.')), + ] + return self.sort_opts(_opts) + + def get_base_opts(self): + _base_opts = super(StandaloneConfig, self).get_base_opts() + _opts = [ + # deployment options + cfg.StrOpt('deployment_user', + help=( + 'User used to run openstack undercloud install ' + 'command which will be used to add the user to the ' + 'docker group, required to upload containers'), + ), + cfg.StrOpt('hieradata_override', + default='', + help=( + 'Path to hieradata override file. Relative paths ' + 'get computed inside of $HOME. When it points to a ' + 'heat env file, it is passed in t-h-t via "-e ' + '", as is. When the file contains legacy ' + 'instack data, it is wrapped with ' + 'UndercloudExtraConfig and also passed in for ' + 't-h-t as a temp file created in output_dir. Note, ' + 'instack hiera data may be not t-h-t compatible ' + 'and will highly likely require a manual revision.') + ), + cfg.StrOpt('net_config_override', + default='', + help=( + 'Path to network config override template.' + 'Relative paths get computed inside of the ' + 'given heat templates directory. Must be in ' + 'json format.' + 'Its content overrides anything in t-h-t ' + 'UndercloudNetConfigOverride. The processed ' + 'template is then passed in Heat via the top ' + 'scope undercloud_parameters.yaml file created in ' + 'output_dir and used to configure the networking ' + 'via run-os-net-config. If you wish to disable ' + 'you can set this location to an empty file.' + 'Templated for instack j2 tags ' + 'may be used, ' + 'for example:\n%s ') % NETCONFIG_TAGS_EXAMPLE + ), + cfg.StrOpt('templates', + default='', + help=('heat templates file to override.') + ), + cfg.StrOpt('roles_file', + default=None, + help=('Roles file to override for heat. ' + 'The file path is related to the templates path') + ), + cfg.BoolOpt('heat_native', + default=True, + help=('Use native heat templates.')), + cfg.StrOpt('heat_container_image', + default='', + help=('URL for the heat container image to use.') + ), + cfg.StrOpt('container_images_file', + default='', + help=( + 'Heat environment file with parameters for all ' + 'required container images. Or alternatively, ' + 'parameter "ContainerImagePrepare" to drive the ' + 'required image preparation.')), + cfg.ListOpt('custom_env_files', + default=[], + help=('List of any custom environment yaml files to ' + 'use')), + # docker config bits + cfg.StrOpt('docker_registry_mirror', + default='', + help=( + 'An optional docker \'registry-mirror\' that will ' + 'beconfigured in /etc/docker/daemon.json.') + ), + cfg.ListOpt('docker_insecure_registries', + default=[], + help=('Used to add custom insecure registries in ' + '/etc/sysconfig/docker.') + ), + ] + return self.sort_opts(_base_opts + _opts) + + def get_opts(self): + return self.sort_opts(self.get_base_opts() + + self.get_enable_service_opts()) diff --git a/tripleoclient/config/undercloud.py b/tripleoclient/config/undercloud.py new file mode 100644 index 000000000..3563e6547 --- /dev/null +++ b/tripleoclient/config/undercloud.py @@ -0,0 +1,338 @@ +# 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 oslo_config import cfg +from tripleoclient.config.standalone import StandaloneConfig + +# Control plane network name +SUBNETS_DEFAULT = ['ctlplane-subnet'] + +# Deprecated options +_deprecated_opt_network_gateway = [cfg.DeprecatedOpt( + 'network_gateway', group='DEFAULT')] +_deprecated_opt_network_cidr = [cfg.DeprecatedOpt( + 'network_cidr', group='DEFAULT')] +_deprecated_opt_dhcp_start = [cfg.DeprecatedOpt( + 'dhcp_start', group='DEFAULT')] +_deprecated_opt_dhcp_end = [cfg.DeprecatedOpt('dhcp_end', group='DEFAULT')] +_deprecated_opt_inspection_iprange = [cfg.DeprecatedOpt( + 'inspection_iprange', group='DEFAULT')] + + +class UndercloudConfig(StandaloneConfig): + def get_undercloud_service_opts(self): + return super(UndercloudConfig, self).get_enable_service_opts( + cinder=False, + ironic=True, + ironic_inspector=True, + mistral=True, + novajoin=False, + tempest=True, + telemetry=False, + tripleo_ui=True, + validations=True, + zaqar=True) + + def get_base_opts(self): + _base_opts = super(UndercloudConfig, self).get_base_opts() + _opts = [ + cfg.StrOpt('undercloud_hostname', + help=( + 'Fully qualified hostname (including domain) to ' + 'set on the Undercloud. 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 ' + 'install will configure all system hostname ' + 'settings.'), + ), + cfg.StrOpt('local_ip', + default='192.168.24.1/24', + help=( + 'IP information for the interface on the ' + 'Undercloud that will be handling the PXE boots ' + 'and DHCP for Overcloud instances. 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.StrOpt('undercloud_public_host', + deprecated_name='undercloud_public_vip', + default='192.168.24.2', + help=('Virtual IP or DNS address to use for the public ' + 'endpoints of Undercloud services. Only used ' + 'with SSL.') + ), + cfg.StrOpt('undercloud_admin_host', + deprecated_name='undercloud_admin_vip', + default='192.168.24.3', + help=('Virtual IP or DNS address to use for the admin ' + 'endpoints of Undercloud services. Only used ' + 'with SSL.') + ), + cfg.ListOpt('undercloud_nameservers', + default=[], + help=( + 'DNS nameserver(s) to use for the undercloud ' + 'node.'), + ), + cfg.ListOpt('undercloud_ntp_servers', + default=[], + help=('List of ntp servers to use.')), + cfg.StrOpt('overcloud_domain_name', + default='localdomain', + help=( + 'DNS domain name to use when deploying the ' + 'overcloud. The overcloud parameter "CloudDomain" ' + 'must be set to a matching value.') + ), + cfg.ListOpt('subnets', + default=SUBNETS_DEFAULT, + help=('List of routed network subnets for ' + 'provisioning and introspection. Comma ' + 'separated list of names/tags. For each network ' + 'a section/group needs to be added to the ' + 'configuration file with these parameters set: ' + 'cidr, dhcp_start, dhcp_end, ' + 'inspection_iprange, gateway and ' + 'masquerade_network.' + '\n\n' + 'Example:\n\n' + 'subnets = subnet1,subnet2\n' + '\n' + 'An example section/group in config file:\n' + '\n' + '[subnet1]\n' + 'cidr = 192.168.10.0/24\n' + 'dhcp_start = 192.168.10.100\n' + 'dhcp_end = 192.168.10.200\n' + 'inspection_iprange = 192.168.10.20,' + '192.168.10.90\n' + 'gateway = 192.168.10.254\n' + 'masquerade = True' + '\n' + '[subnet2]\n' + '. . .\n')), + cfg.StrOpt('local_subnet', + default=SUBNETS_DEFAULT[0], + help=( + 'Name of the local subnet, where the PXE boot and ' + 'DHCP interfaces for overcloud instances is ' + 'located. The IP address of the ' + 'local_ip/local_interface should reside ' + 'in this subnet.')), + cfg.StrOpt('undercloud_service_certificate', + default='', + help=( + 'Certificate file to use for OpenStack service SSL ' + 'connections. Setting this enables SSL for the ' + 'OpenStack API endpoints, leaving it unset ' + 'disables SSL.') + ), + cfg.BoolOpt('generate_service_certificate', + default=True, + help=( + 'When set to True, an SSL certificate will be ' + 'generated as part of the undercloud install and ' + 'this certificate will be used in place of the ' + 'value for undercloud_service_certificate. The ' + 'resulting certificate will be written to ' + '/etc/pki/tls/certs/' + 'undercloud-[undercloud_public_host].pem. This ' + 'certificate is signed by CA selected by the ' + '"certificate_generation_ca" option.') + ), + cfg.StrOpt('certificate_generation_ca', + default='local', + help=('The certmonger nickname of the CA from which ' + 'the certificate will be requested. This is used ' + 'only if the generate_service_certificate option ' + 'is set. Note that if the "local" CA is selected ' + 'the certmonger\'s local CA certificate will be ' + 'extracted to /etc/pki/ca-trust/source/anchors/' + 'cm-local-ca.pem and subsequently added to the ' + 'trust chain.') + + ), + cfg.StrOpt('service_principal', + default='', + help=( + 'The kerberos principal for the service that will ' + 'use the certificate. This is only needed if your ' + 'CA requires a kerberos principal. e.g. with ' + 'FreeIPA.') + ), + cfg.StrOpt('local_interface', + default='eth1', + help=('Network interface on the Undercloud that will ' + 'be handling the PXE boots and DHCP for ' + 'Overcloud instances.') + ), + cfg.IntOpt('local_mtu', + default=1500, + help=('MTU to use for the local_interface.') + ), + + cfg.StrOpt('inspection_interface', + default='br-ctlplane', + deprecated_name='discovery_interface', + help=( + 'Network interface on which inspection dnsmasq ' + 'will listen. If in doubt, use the default value.') + ), + cfg.BoolOpt('inspection_extras', + default=True, + help=( + 'Whether to enable extra hardware collection ' + 'during the inspection process. Requires ' + 'python-hardware or python-hardware-detect ' + 'package on the introspection image.')), + cfg.BoolOpt('inspection_runbench', + default=False, + deprecated_name='discovery_runbench', + help=( + 'Whether to run benchmarks when inspecting ' + 'nodes. Requires inspection_extras set to True.') + ), + cfg.BoolOpt('enable_node_discovery', + default=False, + help=( + 'Makes ironic-inspector enroll any unknown node ' + 'that PXE-boots introspection ramdisk in Ironic. ' + 'By default, the "fake" driver is used for new ' + 'nodes (it is automatically enabled when this ' + 'option is set to True). Set ' + 'discovery_default_driver to override. ' + 'Introspection rules can also be used to specify ' + 'driver information for newly enrolled nodes.') + ), + cfg.StrOpt('discovery_default_driver', + default='ipmi', + help=( + 'The default driver or hardware type to use for ' + 'newly discovered nodes (requires ' + 'enable_node_discovery set to True). It is ' + 'automatically added to enabled_hardware_types.') + ), + cfg.BoolOpt('undercloud_debug', + default=True, + help=( + 'Whether to enable the debug log level for ' + 'Undercloud OpenStack services.') + ), + cfg.BoolOpt('undercloud_update_packages', + default=False, + help=( + 'Whether to update packages during the Undercloud ' + 'install. This is a no-op for containerized ' + 'undercloud.') + ), + cfg.BoolOpt('enable_container_images_build', + default=True, + help=( + 'Whether to enable docker container images to be ' + 'build on the undercloud.') + ), + cfg.StrOpt('ipa_otp', + default='', + help=( + 'One Time Password to register Undercloud node ' + 'with an IPA server. Required when ' + 'enable_novajoin = True.') + ), + cfg.BoolOpt('ipxe_enabled', + default=True, + help=('Whether to use iPXE for deploy and ' + 'inspection.'), + deprecated_name='ipxe_deploy', + ), + cfg.IntOpt('scheduler_max_attempts', + default=30, min=1, + help=( + 'Maximum number of attempts the scheduler will ' + 'make when deploying the instance. You should keep ' + 'it greater or equal to the number of bare metal ' + 'nodes you expect to deploy at once to work around ' + 'potential race condition when scheduling.')), + cfg.BoolOpt('clean_nodes', + default=False, + help=( + 'Whether to clean overcloud nodes (wipe the hard ' + 'drive) between deployments and after the ' + 'introspection.')), + cfg.ListOpt('enabled_hardware_types', + default=['ipmi', 'redfish', 'ilo', 'idrac'], + help=('List of enabled bare metal hardware types ' + '(next generation drivers).')), + cfg.BoolOpt('enable_routed_networks', + default=False, + help=('Enable support for routed ctlplane networks.')), + cfg.BoolOpt('enable_swift_encryption', + default=False, + help=( + 'Whether to enable Swift encryption at-rest or ' + 'not.' + )), + ] + return self.sort_opts(_base_opts + _opts) + + def get_opts(self): + _base_opts = self.get_base_opts() + _service_opts = self.get_undercloud_service_opts() + return self.sort_opts(_base_opts + _service_opts) + + def get_subnet_opts(self): + _subnets_opts = [ + cfg.StrOpt('cidr', + default='192.168.24.0/24', + deprecated_opts=_deprecated_opt_network_cidr, + help=('Network CIDR for the Neutron-managed subnet for ' + 'Overcloud instances.')), + cfg.StrOpt('dhcp_start', + default='192.168.24.5', + deprecated_opts=_deprecated_opt_dhcp_start, + help=( + 'Start of DHCP allocation range for PXE and DHCP ' + 'of Overcloud instances on this network.')), + cfg.StrOpt('dhcp_end', + default='192.168.24.24', + deprecated_opts=_deprecated_opt_dhcp_end, + help=('End of DHCP allocation range for PXE and DHCP ' + 'of Overcloud instances on this network.')), + cfg.StrOpt('inspection_iprange', + default='192.168.24.100,192.168.24.120', + deprecated_opts=_deprecated_opt_inspection_iprange, + help=( + 'Temporary IP range that will be given to nodes on ' + 'this network during the inspection process. ' + 'Should not overlap with the range defined by ' + 'dhcp_start and dhcp_end, but should be in the ' + 'same ip subnet.' + )), + cfg.StrOpt('gateway', + default='192.168.24.1', + deprecated_opts=_deprecated_opt_network_gateway, + help=( + 'Network gateway for the Neutron-managed network ' + 'for Overcloud instances on this network.')), + cfg.BoolOpt('masquerade', + default=False, + help=( + 'The network will be masqueraded for external ' + 'access.')), + ] + return self.sort_opts(_subnets_opts) diff --git a/tripleoclient/tests/config/__init__.py b/tripleoclient/tests/config/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tripleoclient/tests/config/test_config_base.py b/tripleoclient/tests/config/test_config_base.py new file mode 100644 index 000000000..c80f6a184 --- /dev/null +++ b/tripleoclient/tests/config/test_config_base.py @@ -0,0 +1,42 @@ +# 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 oslo_config import cfg +from tripleoclient.config.base import BaseConfig +from tripleoclient.tests import base + + +class TestBaseConfig(base.TestCase): + def setUp(self): + super(TestBaseConfig, self).setUp() + # Get the class object to test + self.config = BaseConfig() + + def test_sort_opts(self): + _opts = [ + cfg.BoolOpt('b', default=True), + cfg.BoolOpt('a', default=True) + ] + expected = [ + cfg.BoolOpt('a', default=True), + cfg.BoolOpt('b', default=True) + ] + ret = self.config.sort_opts(_opts) + self.assertEqual(expected, ret) + + def test_get_base_opts(self): + ret = self.config.get_base_opts() + expected = ['cleanup', 'output_dir'] + self.assertEqual(expected, [x.name for x in ret]) diff --git a/tripleoclient/tests/config/test_config_standalone.py b/tripleoclient/tests/config/test_config_standalone.py new file mode 100644 index 000000000..c1d9b54c5 --- /dev/null +++ b/tripleoclient/tests/config/test_config_standalone.py @@ -0,0 +1,109 @@ +# 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.standalone import StandaloneConfig +from tripleoclient.tests import base + + +class TestStandaloneConfig(base.TestCase): + def setUp(self): + super(TestStandaloneConfig, self).setUp() + # Get the class object to test + self.config = StandaloneConfig() + + def test_get_base_opts(self): + ret = self.config.get_base_opts() + expected = ['cleanup', + 'container_images_file', + 'custom_env_files', + 'deployment_user', + 'docker_insecure_registries', + 'docker_registry_mirror', + 'heat_container_image', + 'heat_native', + 'hieradata_override', + 'net_config_override', + 'output_dir', + 'roles_file', + 'templates'] + self.assertEqual(expected, [x.name for x in ret]) + + def test_get_service_opts(self): + ret = self.config.get_enable_service_opts() + expected = ['enable_cinder', + 'enable_ironic', + 'enable_ironic_inspector', + 'enable_mistral', + 'enable_novajoin', + 'enable_telemetry', + 'enable_tempest', + 'enable_ui', + 'enable_validations', + 'enable_zaqar'] + self.assertEqual(expected, [x.name for x in ret]) + for x in ret: + self.assertEqual(x.default, False, "%s config not False" % x.name) + + def test_get_service_opts_enabled(self): + ret = self.config.get_enable_service_opts(cinder=True, + ironic=True, + ironic_inspector=True, + mistral=True, + novajoin=True, + telemetry=True, + tempest=True, + tripleo_ui=True, + validations=True, + zaqar=True) + expected = ['enable_cinder', + 'enable_ironic', + 'enable_ironic_inspector', + 'enable_mistral', + 'enable_novajoin', + 'enable_telemetry', + 'enable_tempest', + 'enable_ui', + 'enable_validations', + 'enable_zaqar'] + self.assertEqual(expected, [x.name for x in ret]) + for x in ret: + self.assertEqual(x.default, True, "%s config not True" % x.name) + + def test_get_opts(self): + ret = self.config.get_opts() + expected = ['cleanup', + 'container_images_file', + 'custom_env_files', + 'deployment_user', + 'docker_insecure_registries', + 'docker_registry_mirror', + 'enable_cinder', + 'enable_ironic', + 'enable_ironic_inspector', + 'enable_mistral', + 'enable_novajoin', + 'enable_telemetry', + 'enable_tempest', + 'enable_ui', + 'enable_validations', + 'enable_zaqar', + 'heat_container_image', + 'heat_native', + 'hieradata_override', + 'net_config_override', + 'output_dir', + 'roles_file', + 'templates'] + self.assertEqual(expected, [x.name for x in ret]) diff --git a/tripleoclient/tests/config/test_config_undercloud.py b/tripleoclient/tests/config/test_config_undercloud.py new file mode 100644 index 000000000..cca5c675d --- /dev/null +++ b/tripleoclient/tests/config/test_config_undercloud.py @@ -0,0 +1,155 @@ +# 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.undercloud import UndercloudConfig +from tripleoclient.tests import base + + +class TestUndercloudConfig(base.TestCase): + def setUp(self): + super(TestUndercloudConfig, self).setUp() + # Get the class object to test + self.config = UndercloudConfig() + + def test_get_base_opts(self): + ret = self.config.get_base_opts() + expected = ['certificate_generation_ca', + 'clean_nodes', + 'cleanup', + 'container_images_file', + 'custom_env_files', + 'deployment_user', + 'discovery_default_driver', + 'docker_insecure_registries', + 'docker_registry_mirror', + 'enable_container_images_build', + 'enable_node_discovery', + 'enable_routed_networks', + 'enable_swift_encryption', + 'enabled_hardware_types', + 'generate_service_certificate', + 'heat_container_image', + 'heat_native', + 'hieradata_override', + 'inspection_extras', + 'inspection_interface', + 'inspection_runbench', + 'ipa_otp', + 'ipxe_enabled', + 'local_interface', + 'local_ip', + 'local_mtu', + 'local_subnet', + 'net_config_override', + 'output_dir', + 'overcloud_domain_name', + 'roles_file', + 'scheduler_max_attempts', + 'service_principal', + 'subnets', + 'templates', + 'undercloud_admin_host', + 'undercloud_debug', + 'undercloud_hostname', + 'undercloud_nameservers', + 'undercloud_ntp_servers', + 'undercloud_public_host', + 'undercloud_service_certificate', + 'undercloud_update_packages'] + self.assertEqual(expected, [x.name for x in ret]) + + def test_get_opts(self): + ret = self.config.get_opts() + expected = ['certificate_generation_ca', + 'clean_nodes', + 'cleanup', + 'container_images_file', + 'custom_env_files', + 'deployment_user', + 'discovery_default_driver', + 'docker_insecure_registries', + 'docker_registry_mirror', + 'enable_cinder', + 'enable_container_images_build', + 'enable_ironic', + 'enable_ironic_inspector', + 'enable_mistral', + 'enable_node_discovery', + 'enable_novajoin', + 'enable_routed_networks', + 'enable_swift_encryption', + 'enable_telemetry', + 'enable_tempest', + 'enable_ui', + 'enable_validations', + 'enable_zaqar', + 'enabled_hardware_types', + 'generate_service_certificate', + 'heat_container_image', + 'heat_native', + 'hieradata_override', + 'inspection_extras', + 'inspection_interface', + 'inspection_runbench', + 'ipa_otp', + 'ipxe_enabled', + 'local_interface', + 'local_ip', + 'local_mtu', + 'local_subnet', + 'net_config_override', + 'output_dir', + 'overcloud_domain_name', + 'roles_file', + 'scheduler_max_attempts', + 'service_principal', + 'subnets', + 'templates', + 'undercloud_admin_host', + 'undercloud_debug', + 'undercloud_hostname', + 'undercloud_nameservers', + 'undercloud_ntp_servers', + 'undercloud_public_host', + 'undercloud_service_certificate', + 'undercloud_update_packages'] + self.assertEqual(expected, [x.name for x in ret]) + + def test_get_subnet_opts(self): + ret = self.config.get_subnet_opts() + expected = ['cidr', + 'dhcp_end', + 'dhcp_start', + 'gateway', + 'inspection_iprange', + 'masquerade'] + self.assertEqual(expected, [x.name for x in ret]) + + def test_get_undercloud_service_opts(self): + ret = self.config.get_undercloud_service_opts() + expected = {'enable_cinder': False, + 'enable_ironic': True, + 'enable_ironic_inspector': True, + 'enable_mistral': True, + 'enable_novajoin': False, + 'enable_telemetry': False, + 'enable_tempest': True, + 'enable_ui': True, + 'enable_validations': True, + 'enable_zaqar': True} + 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/v1/undercloud_config.py b/tripleoclient/v1/undercloud_config.py index 95c189c84..11fecd47f 100644 --- a/tripleoclient/v1/undercloud_config.py +++ b/tripleoclient/v1/undercloud_config.py @@ -30,39 +30,13 @@ from cryptography.hazmat.primitives import serialization from oslo_config import cfg from tripleo_common.image import kolla_builder -from tripleoclient import constants + +from tripleoclient.config.undercloud import SUBNETS_DEFAULT +from tripleoclient.config.undercloud import UndercloudConfig from tripleoclient import exceptions from tripleoclient import utils - from tripleoclient.v1 import undercloud_preflight -NETCONFIG_TAGS_EXAMPLE = """ -"network_config": [ - { - "type": "ovs_bridge", - "name": "br-ctlplane", - "ovs_extra": [ - "br-set-external-id br-ctlplane bridge-id br-ctlplane" - ], - "members": [ - { - "type": "interface", - "name": "{{LOCAL_INTERFACE}}", - "primary": "true", - "mtu": {{LOCAL_MTU}}, - "dns_servers": {{UNDERCLOUD_NAMESERVERS}} - } - ], - "addresses": [ - { - "ip_netmask": "{{PUBLIC_INTERFACE_IP}}" - } - ], - "routes": {{SUBNETS_STATIC_ROUTES}}, - "mtu": {{LOCAL_MTU}} -} -] -""" # Provides mappings for some of the instack_env tags to undercloud heat # params or undercloud.conf opts known here (as a fallback), needed to maintain @@ -108,9 +82,6 @@ TELEMETRY_DOCKER_ENV_YAML = [ 'environments/services/undercloud-panko.yaml', 'environments/services/undercloud-ceilometer.yaml'] -# Control plane network name -SUBNETS_DEFAULT = ['ctlplane-subnet'] - class Paths(object): @property @@ -121,386 +92,31 @@ class Paths(object): CONF = cfg.CONF PATHS = Paths() -# Deprecated options -_deprecated_opt_network_gateway = [cfg.DeprecatedOpt( - 'network_gateway', group='DEFAULT')] -_deprecated_opt_network_cidr = [cfg.DeprecatedOpt( - 'network_cidr', group='DEFAULT')] -_deprecated_opt_dhcp_start = [cfg.DeprecatedOpt( - 'dhcp_start', group='DEFAULT')] -_deprecated_opt_dhcp_end = [cfg.DeprecatedOpt('dhcp_end', group='DEFAULT')] -_deprecated_opt_inspection_iprange = [cfg.DeprecatedOpt( - 'inspection_iprange', group='DEFAULT')] # 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() -_opts = [ - cfg.StrOpt('output_dir', - default=constants.UNDERCLOUD_OUTPUT_DIR, - help=('Directory to output state, processed heat templates, ' - 'ansible deployment files.'), - ), - cfg.BoolOpt('cleanup', - default=False, - help=('Cleanup temporary files'), - ), - cfg.StrOpt('deployment_user', - help=('User used to run openstack undercloud install command ' - 'which will be used to add the user to the docker group, ' - 'required to upload containers'), - ), - cfg.StrOpt('undercloud_hostname', - help=('Fully qualified hostname (including domain) to set on ' - 'the Undercloud. 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 install ' - 'will configure all system hostname settings.'), - ), - cfg.StrOpt('local_ip', - default='192.168.24.1/24', - help=('IP information for the interface on the Undercloud ' - 'that will be handling the PXE boots and DHCP for ' - 'Overcloud instances. 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.StrOpt('undercloud_public_host', - deprecated_name='undercloud_public_vip', - default='192.168.24.2', - help=('Virtual IP or DNS address to use for the public ' - 'endpoints of Undercloud services. Only used with SSL.') - ), - cfg.StrOpt('undercloud_admin_host', - deprecated_name='undercloud_admin_vip', - default='192.168.24.3', - help=('Virtual IP or DNS address to use for the admin ' - 'endpoints of Undercloud services. Only used with SSL.') - ), - cfg.ListOpt('undercloud_nameservers', - default=[], - help=('DNS nameserver(s) to use for the undercloud node.'), - ), - cfg.ListOpt('undercloud_ntp_servers', - default=[], - help=('List of ntp servers to use.')), - cfg.StrOpt('overcloud_domain_name', - default='localdomain', - help=('DNS domain name to use when deploying the overcloud. ' - 'The overcloud parameter "CloudDomain" must be set to a ' - 'matching value.') - ), - cfg.ListOpt('subnets', - default=SUBNETS_DEFAULT, - help=('List of routed network subnets for provisioning ' - 'and introspection. Comma separated list of names/tags. ' - 'For each network a section/group needs to be added to ' - 'the configuration file with these parameters set: ' - 'cidr, dhcp_start, dhcp_end, inspection_iprange, ' - 'gateway and masquerade_network.' - '\n\n' - 'Example:\n\n' - 'subnets = subnet1,subnet2\n' - '\n' - 'An example section/group in config file:\n' - '\n' - '[subnet1]\n' - 'cidr = 192.168.10.0/24\n' - 'dhcp_start = 192.168.10.100\n' - 'dhcp_end = 192.168.10.200\n' - 'inspection_iprange = 192.168.10.20,192.168.10.90\n' - 'gateway = 192.168.10.254\n' - 'masquerade = True' - '\n' - '[subnet2]\n' - '. . .\n')), - cfg.StrOpt('local_subnet', - default=SUBNETS_DEFAULT[0], - help=('Name of the local subnet, where the PXE boot and DHCP ' - 'interfaces for overcloud instances is located. The IP ' - 'address of the local_ip/local_interface should reside ' - 'in this subnet.')), - cfg.StrOpt('undercloud_service_certificate', - default='', - help=('Certificate file to use for OpenStack service SSL ' - 'connections. Setting this enables SSL for the ' - 'OpenStack API endpoints, leaving it unset disables SSL.') - ), - cfg.BoolOpt('generate_service_certificate', - default=True, - help=('When set to True, an SSL certificate will be generated ' - 'as part of the undercloud install and this certificate ' - 'will be used in place of the value for ' - 'undercloud_service_certificate. The resulting ' - 'certificate will be written to ' - '/etc/pki/tls/certs/undercloud-[undercloud_public_host].' - 'pem. This certificate is signed by CA selected by the ' - '"certificate_generation_ca" option.') - ), - cfg.StrOpt('certificate_generation_ca', - default='local', - help=('The certmonger nickname of the CA from which the ' - 'certificate will be requested. This is used only if ' - 'the generate_service_certificate option is set. ' - 'Note that if the "local" CA is selected the ' - 'certmonger\'s local CA certificate will be extracted to ' - '/etc/pki/ca-trust/source/anchors/cm-local-ca.pem and ' - 'subsequently added to the trust chain.') - ), - cfg.StrOpt('service_principal', - default='', - help=('The kerberos principal for the service that will use ' - 'the certificate. This is only needed if your CA ' - 'requires a kerberos principal. e.g. with FreeIPA.') - ), - cfg.StrOpt('local_interface', - default='eth1', - help=('Network interface on the Undercloud that will be ' - 'handling the PXE boots and DHCP for Overcloud ' - 'instances.') - ), - cfg.IntOpt('local_mtu', - default=1500, - help=('MTU to use for the local_interface.') - ), - cfg.StrOpt('hieradata_override', - default='', - help=('Path to hieradata override file. Relative paths get ' - 'computed inside of $HOME. When it points to a heat ' - 'env file, it is passed in t-h-t via "-e ", as is. ' - 'When the file contains legacy instack data, ' - 'it is wrapped with UndercloudExtraConfig and also ' - 'passed in for t-h-t as a temp file created in ' - 'output_dir. Note, instack hiera data may be ' - 'not t-h-t compatible and will highly likely require a ' - 'manual revision.') - ), - cfg.StrOpt('net_config_override', - default='', - help=('Path to network config override template. Relative ' - 'paths get computed inside of the given heat templates ' - 'directory. Must be in json format. ' - 'Its content overrides anything in t-h-t ' - 'UndercloudNetConfigOverride. The processed template ' - 'is then passed in Heat via the top scope ' - 'undercloud_parameters.yaml file created in ' - 'output_dir and used to configure the networking ' - 'via run-os-net-config. If you wish to disable ' - 'you can set this location to an empty file.' - 'Templated for instack j2 tags ' - 'may be used, for example:\n%s ') % NETCONFIG_TAGS_EXAMPLE - ), - cfg.StrOpt('inspection_interface', - default='br-ctlplane', - deprecated_name='discovery_interface', - help=('Network interface on which inspection dnsmasq will ' - 'listen. If in doubt, use the default value.') - ), - cfg.BoolOpt('inspection_extras', - default=True, - help=('Whether to enable extra hardware collection during ' - 'the inspection process. Requires python-hardware or ' - 'python-hardware-detect package on the introspection ' - 'image.')), - cfg.BoolOpt('inspection_runbench', - default=False, - deprecated_name='discovery_runbench', - help=('Whether to run benchmarks when inspecting nodes. ' - 'Requires inspection_extras set to True.') - ), - cfg.BoolOpt('enable_node_discovery', - default=False, - help=('Makes ironic-inspector enroll any unknown node that ' - 'PXE-boots introspection ramdisk in Ironic. By default, ' - 'the "fake" driver is used for new nodes (it is ' - 'automatically enabled when this option is set to True).' - ' Set discovery_default_driver to override. ' - 'Introspection rules can also be used to specify driver ' - 'information for newly enrolled nodes.') - ), - cfg.StrOpt('discovery_default_driver', - default='ipmi', - help=('The default driver or hardware type to use for newly ' - 'discovered nodes (requires enable_node_discovery set to ' - 'True). It is automatically added to ' - 'enabled_hardware_types.') - ), - cfg.BoolOpt('undercloud_debug', - default=True, - help=('Whether to enable the debug log level for Undercloud ' - 'OpenStack services.') - ), - cfg.BoolOpt('undercloud_update_packages', - default=False, - help=('Whether to update packages during the Undercloud ' - 'install. This is a no-op for containerized undercloud.') - ), - cfg.BoolOpt('enable_tempest', - default=True, - help=('Whether to install Tempest in the Undercloud.' - 'This is a no-op for containerized undercloud.') - ), - cfg.BoolOpt('enable_telemetry', - default=False, - help=('Whether to install Telemetry services ' - '(ceilometer, gnocchi, aodh, panko ) in the Undercloud.') - ), - cfg.BoolOpt('enable_ui', - default=True, - help=('Whether to install the TripleO UI.') - ), - cfg.BoolOpt('enable_validations', - default=True, - help=('Whether to install requirements to run the TripleO ' - 'validations.') - ), - cfg.BoolOpt('enable_cinder', - default=False, - help=('Whether to install the Volume service. It is not ' - 'currently used in the undercloud.')), - cfg.BoolOpt('enable_novajoin', - default=False, - help=('Whether to install novajoin metadata service in ' - 'the Undercloud.') - ), - cfg.BoolOpt('enable_container_images_build', - default=True, - help=('Whether to enable docker container images to be build ' - 'on the undercloud.') - ), - cfg.StrOpt('ipa_otp', - default='', - help=('One Time Password to register Undercloud node with ' - 'an IPA server. ' - 'Required when enable_novajoin = True.') - ), - cfg.BoolOpt('ipxe_enabled', - default=True, - help=('Whether to use iPXE for deploy and inspection.'), - deprecated_name='ipxe_deploy', - ), - cfg.IntOpt('scheduler_max_attempts', - default=30, min=1, - help=('Maximum number of attempts the scheduler will make ' - 'when deploying the instance. You should keep it ' - 'greater or equal to the number of bare metal nodes ' - 'you expect to deploy at once to work around ' - 'potential race condition when scheduling.')), - cfg.BoolOpt('clean_nodes', - default=False, - help=('Whether to clean overcloud nodes (wipe the hard drive) ' - 'between deployments and after the introspection.')), - cfg.ListOpt('enabled_hardware_types', - default=['ipmi', 'redfish', 'ilo', 'idrac'], - help=('List of enabled bare metal hardware types (next ' - 'generation drivers).')), - cfg.StrOpt('docker_registry_mirror', - default='', - help=('An optional docker \'registry-mirror\' that will be' - 'configured in /etc/docker/daemon.json.') - ), - cfg.ListOpt('docker_insecure_registries', - default=[], - help=('Used to add custom insecure registries in ' - '/etc/sysconfig/docker.') - ), - cfg.StrOpt('templates', - default='', - help=('heat templates file to override.') - ), - cfg.StrOpt('roles_file', - default=None, - help=('Roles file to override for heat. ' - 'The file path is related to the templates path') - ), - cfg.BoolOpt('heat_native', - default=True, - help=('Use native heat templates.')), - cfg.StrOpt('heat_container_image', - default='', - help=('URL for the heat container image to use.') - ), - cfg.StrOpt('container_images_file', - default='', - help=('Heat environment file with parameters for all required ' - 'container images. Or alternatively, parameter ' - '"ContainerImagePrepare" to drive the required image ' - 'preparation.')), - cfg.BoolOpt('enable_ironic', - default=True, - help=('Whether to enable the ironic service.')), - cfg.BoolOpt('enable_ironic_inspector', - default=True, - help=('Whether to enable the ironic inspector service.')), - cfg.BoolOpt('enable_mistral', - default=True, - help=('Whether to enable the mistral service.')), - cfg.BoolOpt('enable_zaqar', - default=True, - help=('Whether to enable the zaqar service.')), - cfg.ListOpt('custom_env_files', - default=[], - help=('List of any custom environment yaml files to use')), - cfg.BoolOpt('enable_routed_networks', - default=False, - help=('Enable support for routed ctlplane networks.')), - cfg.BoolOpt('enable_swift_encryption', - default=False, - help=('Whether to enable Swift encryption at-rest or not.')), -] +config = UndercloudConfig() # Routed subnets -_subnets_opts = [ - cfg.StrOpt('cidr', - default='192.168.24.0/24', - deprecated_opts=_deprecated_opt_network_cidr, - help=('Network CIDR for the Neutron-managed subnet for ' - 'Overcloud instances.')), - cfg.StrOpt('dhcp_start', - default='192.168.24.5', - deprecated_opts=_deprecated_opt_dhcp_start, - help=('Start of DHCP allocation range for PXE and DHCP of ' - 'Overcloud instances on this network.')), - cfg.StrOpt('dhcp_end', - default='192.168.24.24', - deprecated_opts=_deprecated_opt_dhcp_end, - help=('End of DHCP allocation range for PXE and DHCP of ' - 'Overcloud instances on this network.')), - cfg.StrOpt('inspection_iprange', - default='192.168.24.100,192.168.24.120', - deprecated_opts=_deprecated_opt_inspection_iprange, - help=('Temporary IP range that will be given to nodes on this ' - 'network during the inspection process. Should not ' - 'overlap with the range defined by dhcp_start and ' - 'dhcp_end, but should be in the same ip subnet.')), - cfg.StrOpt('gateway', - default='192.168.24.1', - deprecated_opts=_deprecated_opt_network_gateway, - help=('Network gateway for the Neutron-managed network for ' - 'Overcloud instances on this network.')), - cfg.BoolOpt('masquerade', - default=False, - help=('The network will be masqueraded for external access.')), -] - +_opts = config.get_opts() CONF.register_opts(_opts) def _load_subnets_config_groups(): for group in CONF.subnets: g = cfg.OptGroup(name=group, title=group) - CONF.register_opts(_subnets_opts, group=g) + CONF.register_opts(config.get_subnet_opts(), group=g) LOG = logging.getLogger(__name__ + ".undercloud_config") +# this is needed for the oslo config generator def list_opts(): return [(None, copy.deepcopy(_opts)), - (SUBNETS_DEFAULT[0], copy.deepcopy(_subnets_opts))] + (SUBNETS_DEFAULT[0], copy.deepcopy(config.get_subnet_opts()))] def _load_config():