Allow DNS server configuration for kubernetes

In kubernetes deployments, a DNS server is required to locate
the registry servers used to download the kubernetes images.
Currently, when config_controller is run, the 8.8.8.8
nameserver is used, with no way to change it. Some users
need to specify their own name server to be used during
the execution of config_controller.

This change allows the user to specify up to three DNS
servers when running config_controller interactively or with
a config file. If using a config file, add the following
section to the config file (only one nameserver is required,
but up to three are allowed):

[DNS]
NAMESERVER_1=8.8.8.8
NAMESERVER_2=8.8.4.4
NAMESERVER_3=9.9.9.9

Change-Id: I59556138a11c6f627f45886a2da6b8a1ad9d89e1
Closes-bug: 1812449
Signed-off-by: Bart Wensley <barton.wensley@windriver.com>
This commit is contained in:
Bart Wensley 2019-01-24 07:22:05 -06:00
parent c8a7dc76a2
commit d96ce5291c
8 changed files with 251 additions and 24 deletions

View File

@ -29,4 +29,5 @@ from configutilities.common.utils import validate_address # noqa: F401
from configutilities.common.utils import ip_version_to_string # noqa: F401
from configutilities.common.utils import lag_mode_to_str # noqa: F401
from configutilities.common.utils import validate_openstack_password # noqa: F401
from configutilities.common.utils import validate_nameserver_address_str # noqa: F401
from configutilities.common.utils import extract_openstack_password_rules_from_file # noqa: F401

View File

@ -23,6 +23,7 @@ from configutilities.common.utils import is_mtu_valid
from configutilities.common.utils import get_service
from configutilities.common.utils import get_optional
from configutilities.common.utils import validate_address_str
from configutilities.common.utils import validate_nameserver_address_str
from configutilities.common.exceptions import ConfigFail
from configutilities.common.exceptions import ValidateFail
@ -944,8 +945,25 @@ class ConfigValidator(object):
raise ConfigFail("SDN Configuration is no longer supported")
def validate_dns(self):
if self.conf.has_section('DNS'):
raise ConfigFail("DNS Configuration is no longer supported")
# DNS config is optional
if not self.conf.has_section('DNS'):
return
if self.cgcs_conf is not None:
self.cgcs_conf.add_section('cDNS')
for x in range(0, 3):
if self.conf.has_option('DNS', 'NAMESERVER_' + str(x + 1)):
dns_address_str = self.conf.get('DNS', 'NAMESERVER_' + str(
x + 1))
try:
dns_address = validate_nameserver_address_str(
dns_address_str)
if self.cgcs_conf is not None:
self.cgcs_conf.set('cDNS', 'NAMESERVER_' + str(x + 1),
str(dns_address))
except ValidateFail as e:
raise ConfigFail(
"Invalid DNS NAMESERVER value of %s.\nReason: %s" %
(dns_address_str, e))
def validate_ntp(self):
if self.conf.has_section('NTP'):

View File

@ -28,6 +28,7 @@ from configutilities import validate_network_str
from configutilities import validate_address_str
from configutilities import validate_address
from configutilities import ip_version_to_string
from configutilities import validate_nameserver_address_str
from configutilities import validate_openstack_password
from configutilities import DEFAULT_DOMAIN_NAME
from netaddr import IPNetwork
@ -463,6 +464,10 @@ class ConfigAssistant():
# SDN config
self.enable_sdn = False
# DNS config
self.nameserver_addresses = ["8.8.8.8", "8.8.4.4", ""]
# HTTPS
self.enable_https = False
# Network config
@ -2666,6 +2671,64 @@ class ConfigAssistant():
""" Cluster host interface configuration complete"""
self.cluster_host_interface_configured = True
def get_dns_servers(self):
"""Produce a comma separated list of DNS servers."""
servers = [str(s) for s in self.nameserver_addresses if s]
return ",".join(servers)
def input_dns_config(self):
"""Allow user to input DNS config and perform validation."""
print("\nDomain Name System (DNS):")
print("-------------------------\n")
print(textwrap.fill(
"Configuring DNS servers accessible through the external "
"OAM network allows domain names to be mapped to IP "
"addresses.", 80))
print(textwrap.fill(
"The configuration of at least one DNS server is mandatory. To "
"skip the configuration of one or more nameservers (1 to 3 are "
"allowed), enter C to continue to the next configuration item.",
80))
print('')
if self.external_oam_subnet.version == 6:
self.nameserver_addresses = ["2001:4860:4860::8888",
"2001:4860:4860::8844", ""]
for server in range(0, len(self.nameserver_addresses)):
while True:
user_input = raw_input(
"Nameserver " + str(server + 1) + " [" +
str(self.nameserver_addresses[server]) + "]: ")
if user_input.lower() == 'q':
raise UserQuit
elif user_input.lower() == 'c':
if server == 0:
print("At least one DNS server is required.")
continue
for x in range(server, len(self.nameserver_addresses)):
self.nameserver_addresses[x] = ""
return
elif user_input == "":
user_input = self.nameserver_addresses[server]
# Pressing enter with a blank default will continue
if user_input == "":
return
try:
try:
ip_input = validate_nameserver_address_str(
user_input, self.external_oam_subnet.version)
except ValidateFail as e:
print('{}'.format(e))
continue
self.nameserver_addresses[server] = ip_input
break
except (AddrFormatError, ValueError):
print("Invalid address - please enter a valid IPv4 "
"address")
def input_authentication_config(self):
"""Allow user to input authentication config and perform validation.
"""
@ -2711,6 +2774,8 @@ class ConfigAssistant():
self.management_interface_configured = True
self.external_oam_interface_configured = True
self.default_pxeboot_config()
if not self.kubernetes:
self.nameserver_addresses = ["", "", ""]
if utils.is_cpe():
self.system_mode = sysinv_constants.SYSTEM_MODE_DUPLEX
@ -2747,6 +2812,8 @@ class ConfigAssistant():
if self.kubernetes:
self.input_cluster_host_config()
self.input_external_oam_config()
if self.kubernetes:
self.input_dns_config()
self.input_authentication_config()
def is_valid_management_multicast_subnet(self, ip_subnet):
@ -3079,6 +3146,19 @@ class ConfigAssistant():
self.external_oam_interface_configured = True
# DNS configuration
if self.kubernetes:
if config.has_section('cDNS'):
self.nameserver_addresses = ["", "", ""]
for x in range(0, len(self.nameserver_addresses)):
if config.has_option('cDNS',
'NAMESERVER_' + str(x + 1)):
cvalue = config.get('cDNS',
'NAMESERVER_' + str(x + 1))
if cvalue != "NC" and cvalue != "":
self.nameserver_addresses[x] = \
IPAddress(cvalue)
# SDN Network configuration
if config.has_option('cSDN', 'ENABLE_SDN'):
raise ConfigFail("The option ENABLE_SDN is no longer "
@ -3511,6 +3591,18 @@ class ConfigAssistant():
else:
print("External OAM address: " + str(self.external_oam_address_0))
if self.kubernetes:
print("\nDNS Configuration")
print("-----------------")
dns_config = False
for x in range(0, len(self.nameserver_addresses)):
if self.nameserver_addresses[x]:
print("Nameserver " + str(x + 1) + ": " +
str(self.nameserver_addresses[x]))
dns_config = True
if not dns_config:
print("External DNS servers not configured")
if self.region_config:
print("\nRegion Configuration")
print("--------------------")
@ -3796,6 +3888,17 @@ class ConfigAssistant():
f.write("EXTERNAL_OAM_1_ADDRESS=" +
str(self.external_oam_address_1) + "\n")
if self.kubernetes:
# DNS configuration
f.write("\n[cDNS]")
f.write("\n# DNS Configuration\n")
for x in range(0, len(self.nameserver_addresses)):
if self.nameserver_addresses[x]:
f.write("NAMESERVER_" + str(x + 1) + "=" +
str(self.nameserver_addresses[x]) + "\n")
else:
f.write("NAMESERVER_" + str(x + 1) + "=NC" + "\n")
# Network configuration
f.write("\n[cNETWORK]")
f.write("\n# Data Network Configuration\n")
@ -5188,6 +5291,17 @@ class ConfigAssistant():
"required_patches": "N/A"}
client.sysinv.load.create(**patch)
def _populate_dns_config(self, client):
# Retrieve the list of dns servers to get the uuid
dns_list = client.sysinv.idns.list()
dns_record = dns_list[0]
values = {
'nameservers': self.get_dns_servers(),
'action': 'apply'
}
patch = sysinv.dict_to_patch(values)
client.sysinv.idns.update(dns_record.uuid, patch)
def populate_initial_config(self):
"""Populate initial system inventory configuration"""
try:
@ -5195,6 +5309,8 @@ class ConfigAssistant():
self._populate_system_config(client)
self._populate_load_config(client)
self._populate_network_config(client)
if self.kubernetes:
self._populate_dns_config(client)
controller = self._populate_controller_config(client)
# ceph_mon config requires controller host to be created
self._inventory_config_complete_wait(client, controller)

View File

@ -0,0 +1,86 @@
[cSYSTEM]
# System Configuration
SYSTEM_MODE=duplex
TIMEZONE=UTC
[cPXEBOOT]
# PXEBoot Network Support Configuration
PXECONTROLLER_FLOATING_HOSTNAME=pxecontroller
[cMGMT]
# Management Network Configuration
MANAGEMENT_INTERFACE_NAME=eth1
MANAGEMENT_INTERFACE=eth1
MANAGEMENT_MTU=1500
MANAGEMENT_SUBNET=192.168.204.0/24
LAG_MANAGEMENT_INTERFACE=no
CONTROLLER_FLOATING_ADDRESS=192.168.204.2
CONTROLLER_0_ADDRESS=192.168.204.3
CONTROLLER_1_ADDRESS=192.168.204.4
NFS_MANAGEMENT_ADDRESS_1=192.168.204.5
NFS_MANAGEMENT_ADDRESS_2=192.168.204.6
CONTROLLER_FLOATING_HOSTNAME=controller
CONTROLLER_HOSTNAME_PREFIX=controller-
OAMCONTROLLER_FLOATING_HOSTNAME=oamcontroller
DYNAMIC_ADDRESS_ALLOCATION=yes
MANAGEMENT_MULTICAST_SUBNET=239.1.1.0/28
[cINFRA]
# Infrastructure Network Configuration
INFRASTRUCTURE_INTERFACE_NAME=NC
INFRASTRUCTURE_INTERFACE=NC
INFRASTRUCTURE_VLAN=NC
INFRASTRUCTURE_MTU=NC
INFRASTRUCTURE_SUBNET=NC
LAG_INFRASTRUCTURE_INTERFACE=no
INFRASTRUCTURE_BOND_MEMBER_0=NC
INFRASTRUCTURE_BOND_MEMBER_1=NC
INFRASTRUCTURE_BOND_POLICY=NC
CONTROLLER_0_INFRASTRUCTURE_ADDRESS=NC
CONTROLLER_1_INFRASTRUCTURE_ADDRESS=NC
NFS_INFRASTRUCTURE_ADDRESS_1=NC
STORAGE_0_INFRASTRUCTURE_ADDRESS=NC
STORAGE_1_INFRASTRUCTURE_ADDRESS=NC
CONTROLLER_INFRASTRUCTURE_HOSTNAME_SUFFIX=NC
INFRASTRUCTURE_START_ADDRESS=NC
INFRASTRUCTURE_END_ADDRESS=NC
[cCLUSTER]
# Cluster Host Network Configuration
CLUSTER_INTERFACE_NAME=eth1
CLUSTER_INTERFACE=eth1
CLUSTER_VLAN=NC
CLUSTER_MTU=1500
CLUSTER_SUBNET=192.168.206.0/24
LAG_CLUSTER_INTERFACE=no
[cEXT_OAM]
# External OAM Network Configuration
EXTERNAL_OAM_INTERFACE_NAME=eth0
EXTERNAL_OAM_INTERFACE=eth0
EXTERNAL_OAM_VLAN=NC
EXTERNAL_OAM_MTU=1500
LAG_EXTERNAL_OAM_INTERFACE=no
EXTERNAL_OAM_SUBNET=10.10.10.0/24
EXTERNAL_OAM_GATEWAY_ADDRESS=10.10.10.1
EXTERNAL_OAM_FLOATING_ADDRESS=10.10.10.2
EXTERNAL_OAM_0_ADDRESS=10.10.10.3
EXTERNAL_OAM_1_ADDRESS=10.10.10.4
[cDNS]
# DNS Configuration
NAMESERVER_1=1.2.3.4
NAMESERVER_2=5.6.7.8
NAMESERVER_3=NC
[cNETWORK]
# Data Network Configuration
VSWITCH_TYPE=ovs-dpdk
[cSECURITY]
[cREGION]
# Region Configuration
REGION_CONFIG=False
[cAUTHENTICATION]
ADMIN_PASSWORD=Li69nux*

View File

@ -44,6 +44,11 @@ CIDR=10.10.10.0/24
GATEWAY=10.10.10.1
LOGICAL_INTERFACE=LOGICAL_INTERFACE_2
[DNS]
# DNS Configuration
NAMESERVER_1=1.2.3.4
NAMESERVER_2=5.6.7.8
;[PXEBOOT_NETWORK]
;PXEBOOT_CIDR=192.168.203.0/24

View File

@ -19,13 +19,14 @@ import controllerconfig.common.constants as constants
def _test_answerfile(tmpdir, filename,
mock_get_net_device_list,
mock_get_rootfs_node,
compare_results=True):
compare_results=True,
ca_options={}):
""" Test import and generation of answerfile """
mock_get_net_device_list.return_value = \
['eth0', 'eth1', 'eth2']
mock_get_rootfs_node.return_value = '/dev/sda'
assistant = ca.ConfigAssistant()
assistant = ca.ConfigAssistant(**ca_options)
# Create the path to the answerfile
answerfile = os.path.join(
@ -93,3 +94,10 @@ def test_answerfile_region_nuage_vrs(tmpdir):
""" Test import of answerfile with region values for nuage_vrs"""
_test_answerfile(tmpdir, "cgcs_config.region_nuage_vrs")
def test_answerfile_kubernetes(tmpdir):
""" Test import of answerfile with kubernetes values """
_test_answerfile(tmpdir, "cgcs_config.kubernetes",
ca_options={"kubernetes": True})

View File

@ -42,7 +42,7 @@ def _test_system_config(filename):
cr.create_cgcs_config_file(None, system_config, None, None, None, 0,
validate_only=True)
# Validate the region config file.
# Validate the system config file.
# Using onboard validation since the validator's reference version number
# is only set at build-time when validating offboard
validate(system_config, DEFAULT_CONFIG, None, False)
@ -508,14 +508,6 @@ def test_system_config_validation():
with pytest.raises(exceptions.ConfigFail):
validate(system_config, DEFAULT_CONFIG, None, False)
# Test detection of unsupported DNS NAMESERVER
system_config = cr.parse_system_config(simple_systemfile)
system_config.add_section('DNS')
system_config.set('DNS', 'NAMESERVER_1', '8.8.8.8')
with pytest.raises(exceptions.ConfigFail):
cr.create_cgcs_config_file(None, system_config, None, None, None, 0,
validate_only=True)
# Test detection of unsupported NTP NTP_SERVER
system_config = cr.parse_system_config(simple_systemfile)
system_config.add_section('NTP')
@ -606,12 +598,13 @@ def test_pxeboot_range():
validate(system_config, DEFAULT_CONFIG, None, False)
def test_cluster_network():
""" Test import of system_config file for cluster network address """
def test_kubernetes():
""" Test import of system_config file for kubernetes """
# Create the path to the system_config file
systemfile = os.path.join(
os.getcwd(), "controllerconfig/tests/files/", "system_config.cluster")
os.getcwd(), "controllerconfig/tests/files/",
"system_config.kubernetes")
# Test import and generation of answer file
_test_system_config(systemfile)
@ -647,3 +640,10 @@ def test_cluster_network():
validate_only=True)
with pytest.raises(exceptions.ConfigFail):
validate(system_config, DEFAULT_CONFIG, None, False)
# Test absence of optional DNS configuration
system_config = cr.parse_system_config(systemfile)
system_config.remove_section('DNS')
cr.create_cgcs_config_file(None, system_config, None, None, None, 0,
validate_only=True)
validate(system_config, DEFAULT_CONFIG, None, False)

View File

@ -58,15 +58,8 @@ class platform::kubernetes::master::init
# For initial controller install, configure kubernetes from scratch.
$resolv_conf = '/etc/resolv.conf'
# Add a DNS server to allow access to kubernetes repo. This will no longer
# be required once we are using our own internal repo.
file_line { "${resolv_conf} nameserver 8.8.8.8":
path => $resolv_conf,
line => 'nameserver 8.8.8.8',
}
# Configure the master node.
-> file { '/etc/kubernetes/kubeadm.yaml':
file { '/etc/kubernetes/kubeadm.yaml':
ensure => file,
content => template('platform/kubeadm.yaml.erb'),
}