Enable communication from nova-api-proxy to VIM

Prior to kubernetes integration, the nova-api-proxy
communicated with the VIM using port 30003 on the loopback
address. This worked fine when both the nova-api-proxy and
the VIM were running on bare metal.

When nova-api-proxy is running in a pod, it
cannot use the loopback address to communicate with the VIM,
since the loopback address is local to the pod. The
nova-api-proxy pod will instead use the floating management
address (which is present on all systems) to communicate with
the VIM. The problem is that on AIO simplex systems, the
floating management address is also in the loopback subnet.
The fix for this is to use a non-loopback address for
management on AIO simplex systems. This required changes to
configuration scripts/utilities to allow the user to
specify the management subnet on AIO simplex systems,
instead of hardcoding it to a loopback address.

Change-Id: I26c15657471f8214e628c6d43eaab07eb8e744ec
Story: 2003910
Task: 28061
Depends-on: Ib7c08bfc1c661a9fb347308e55cc806b4dd129ad
Signed-off-by: Bart Wensley <barton.wensley@windriver.com>
This commit is contained in:
Bart Wensley 2018-11-22 16:03:53 -06:00
parent d33ee2c75a
commit e37085aba5
9 changed files with 349 additions and 133 deletions

View File

@ -133,7 +133,8 @@ class Network(object):
def parse_config(self, system_config, config_type, network_type,
min_addresses=0, multicast_addresses=0, optional=False,
naming_type=DEFAULT_NAMES):
naming_type=DEFAULT_NAMES,
logical_interface_required=True):
network_prefix = NETWORK_PREFIX_NAMES[naming_type][network_type]
network_name = network_prefix + '_NETWORK'
@ -375,8 +376,10 @@ class Network(object):
gateway_address_str, network_name, e))
# Parse/validate the logical interface
logical_interface_name = system_config.get(
network_name, attr_prefix + 'LOGICAL_INTERFACE')
self.logical_interface = LogicalInterface()
self.logical_interface.parse_config(system_config,
logical_interface_name)
if logical_interface_required or system_config.has_option(
network_name, attr_prefix + 'LOGICAL_INTERFACE'):
logical_interface_name = system_config.get(
network_name, attr_prefix + 'LOGICAL_INTERFACE')
self.logical_interface = LogicalInterface()
self.logical_interface.parse_config(system_config,
logical_interface_name)

View File

@ -69,6 +69,7 @@ class ConfigValidator(object):
self.glance_region = None
self.system_mode = None
self.system_type = None
self.system_dc_role = None
def is_simplex_cpe(self):
return self.system_mode == SYSTEM_MODE_SIMPLEX
@ -79,6 +80,9 @@ class ConfigValidator(object):
def set_system_mode(self, mode):
self.system_mode = mode
def set_system_dc_role(self, dc_role):
self.system_dc_role = dc_role
def set_oam_config(self, use_lag, external_oam_interface_name):
if self.cgcs_conf is not None:
self.cgcs_conf.add_section('cEXT_OAM')
@ -165,6 +169,45 @@ class ConfigValidator(object):
except Exception as e:
raise ConfigFail("Error parsing configuration file: %s" % e)
def validate_aio_simplex_mgmt(self):
# AIO simplex management network configuration
mgmt_prefix = NETWORK_PREFIX_NAMES[self.naming_type][MGMT_TYPE]
self.mgmt_network = Network()
min_addresses = 16
try:
self.mgmt_network.parse_config(self.conf, self.config_type,
MGMT_TYPE,
min_addresses=min_addresses,
multicast_addresses=0,
naming_type=self.naming_type,
logical_interface_required=False)
except ConfigFail:
raise
except Exception as e:
raise ConfigFail("Error parsing configuration file: %s" % e)
if self.mgmt_network.vlan or self.mgmt_network.multicast_cidr or \
self.mgmt_network.start_end_in_config or \
self.mgmt_network.floating_address or \
self.mgmt_network.address_0 or self.mgmt_network.address_1 or \
self.mgmt_network.dynamic_allocation or \
self.mgmt_network.gateway_address or \
self.mgmt_network.logical_interface:
raise ConfigFail("For AIO simplex, only the %s network CIDR can "
"be specified" % mgmt_prefix)
if self.mgmt_network.cidr.version == 6:
raise ConfigFail("IPv6 management network not supported on "
"simplex configuration.")
if self.cgcs_conf is not None:
self.cgcs_conf.add_section('cMGMT')
self.cgcs_conf.set('cMGMT', 'MANAGEMENT_SUBNET',
self.mgmt_network.cidr)
def validate_aio_network(self, subcloud=False):
if not subcloud:
# AIO-SX subcloud supports MGMT_NETWORK & PXEBOOT_NETWORK
@ -172,8 +215,7 @@ class ConfigValidator(object):
raise ConfigFail("PXEBoot Network configuration is not "
"supported.")
if self.conf.has_section('MGMT_NETWORK'):
raise ConfigFail("Management Network configuration is not "
"supported.")
self.validate_aio_simplex_mgmt()
if self.conf.has_section('INFRA_NETWORK'):
raise ConfigFail("Infrastructure Network configuration is not "
"supported.")
@ -192,6 +234,17 @@ class ConfigValidator(object):
"No gateway specified - %s_GATEWAY must be specified"
% oam_prefix)
# Check overlap with management network
if self.mgmt_network is not None:
try:
self.configured_networks.append(self.mgmt_network.cidr)
check_network_overlap(self.oam_network.cidr,
self.configured_networks)
except ValidateFail:
raise ConfigFail("%s CIDR %s overlaps with another configured "
"network" %
(oam_prefix, str(self.mgmt_network.cidr)))
self.set_oam_config(use_lag, external_oam_interface_name)
def validate_version(self):

View File

@ -288,6 +288,10 @@ class ConfigPage(WizardPage):
if mode:
validator.set_system_mode(mode)
dc_role = get_opt('SYSTEM', 'DISTRIBUTED_CLOUD_ROLE')
if dc_role:
validator.set_system_dc_role(dc_role)
for method in self.validator_methods:
getattr(validator, method)()
@ -808,14 +812,12 @@ class SYSTEMPage(ConfigPage):
event.Skip()
def skip_not_required_pages(self, skip):
# Skip PXEBOOT, MGMT, BMC and INFRA pages
# Skip PXEBOOT, BMC and INFRA pages
self.parent.skip_page(PXEBootPage, skip)
self.parent.skip_page(MGMTPage, skip)
self.parent.skip_page(INFRAPage, skip)
# Remove the sections that are not required
config.remove_section("PXEBOOT_NETWORK")
config.remove_section("MGMT_NETWORK")
config.remove_section("BOARD_MANAGEMENT_NETWORK")
config.remove_section("INFRA_NETWORK")
@ -894,117 +896,134 @@ class MGMTPage(ConfigPage):
LINK_SPEED_10G,
LINK_SPEED_25G]
self.section = "MGMT_NETWORK"
self.validator_methods = ["validate_pxeboot", "validate_mgmt"]
if get_opt('SYSTEM', 'SYSTEM_MODE') != 'simplex':
self.validator_methods = ["validate_pxeboot", "validate_mgmt"]
self.help_text = (
"The management network is used for internal communication "
"between platform components. IP addresses on this network "
"are reachable only within the data center.")
else:
self.validator_methods = ["validate_aio_simplex_mgmt"]
self.help_text = (
"The management network is used for internal communication "
"between platform components. IP addresses on this network "
"are reachable only within the host.")
self.title = "Management Network"
self.help_text = (
"The management network is used for internal communication "
"between platform components. IP addresses on this network "
"are reachable only within the data center.")
self.set_fields()
self.do_setup()
self.bind_events()
def set_fields(self):
self.fields['mgmt_port1'] = Field(
text="Management interface",
type=TYPES.string,
initial="enp0s8",
transient=True
)
self.fields['lag_help'] = Field(
text="A management bond interface provides redundant "
"connections for the management network. When selected, the "
"field above specifies the first member of the bond.",
type=TYPES.help,
)
self.fields['LAG_INTERFACE'] = Field(
text="Use management interface link aggregation",
type=TYPES.checkbox,
shows=["LAG_MODE", "mgmt_port2"],
transient=True
)
self.fields['LAG_MODE'] = Field(
text="Management interface bonding policy",
type=TYPES.choice,
choices=self.lag_choices.keys(),
transient=True
)
self.fields['mgmt_port2'] = Field(
text="Second management interface member",
type=TYPES.string,
initial="",
transient=True
)
self.fields['INTERFACE_MTU'] = Field(
text="Management interface MTU",
type=TYPES.int,
initial="1500",
transient=True
)
self.fields['INTERFACE_LINK_CAPACITY'] = Field(
text="Management interface link capacity Mbps",
type=TYPES.choice,
choices=self.mgmt_speed_choices,
initial=self.mgmt_speed_choices[0],
transient=True
)
if config.has_option('PXEBOOT_NETWORK', 'PXEBOOT_CIDR') or \
config.has_option('REGION2_PXEBOOT_NETWORK', 'PXEBOOT_CIDR'):
self.fields['vlan_help'] = Field(
text=("A management VLAN is required because a separate "
"PXEBoot network was configured on the management "
"interface."),
type=TYPES.help
if get_opt('SYSTEM', 'SYSTEM_MODE') != 'simplex':
self.fields['mgmt_port1'] = Field(
text="Management interface",
type=TYPES.string,
initial="enp0s8",
transient=True
)
self.fields['VLAN'] = Field(
text="Management VLAN Identifier",
type=TYPES.int,
self.fields['lag_help'] = Field(
text="A management bond interface provides redundant "
"connections for the management network. When selected, "
"the field above specifies the first member of the bond.",
type=TYPES.help,
)
self.fields['LAG_INTERFACE'] = Field(
text="Use management interface link aggregation",
type=TYPES.checkbox,
shows=["LAG_MODE", "mgmt_port2"],
transient=True
)
self.fields['LAG_MODE'] = Field(
text="Management interface bonding policy",
type=TYPES.choice,
choices=self.lag_choices.keys(),
transient=True
)
self.fields['mgmt_port2'] = Field(
text="Second management interface member",
type=TYPES.string,
initial="",
transient=True
)
self.fields['CIDR'] = Field(
text="Management subnet",
type=TYPES.string,
initial="192.168.204.0/24",
)
self.fields['MULTICAST_CIDR'] = Field(
text="Management multicast subnet",
type=TYPES.string,
initial='239.1.1.0/28'
)
self.fields['INTERFACE_MTU'] = Field(
text="Management interface MTU",
type=TYPES.int,
initial="1500",
transient=True
)
self.fields['INTERFACE_LINK_CAPACITY'] = Field(
text="Management interface link capacity Mbps",
type=TYPES.choice,
choices=self.mgmt_speed_choices,
initial=self.mgmt_speed_choices[0],
transient=True
)
if config.has_option('PXEBOOT_NETWORK', 'PXEBOOT_CIDR') or \
config.has_option('REGION2_PXEBOOT_NETWORK',
'PXEBOOT_CIDR'):
self.fields['vlan_help'] = Field(
text=("A management VLAN is required because a separate "
"PXEBoot network was configured on the management "
"interface."),
type=TYPES.help
)
self.fields['VLAN'] = Field(
text="Management VLAN Identifier",
type=TYPES.int,
initial="",
)
# Start/end ranges
self.fields['use_entire_subnet'] = Field(
text="Restrict management subnet address range",
type=TYPES.checkbox,
shows=["IP_START_ADDRESS", "IP_END_ADDRESS"],
transient=True
)
self.fields['IP_START_ADDRESS'] = Field(
text="Management network start address",
type=TYPES.string,
initial="192.168.204.2",
)
self.fields['IP_END_ADDRESS'] = Field(
text="Management network end address",
type=TYPES.string,
initial="192.168.204.254",
)
self.fields['CIDR'] = Field(
text="Management subnet",
type=TYPES.string,
initial="192.168.204.0/24",
)
# Dynamic addressing
self.fields['dynamic_help'] = Field(
text=(
"IP addresses can be assigned to hosts dynamically or "
"a static IP address can be specified for each host. "
"Note: This choice applies to both the management network "
"and infrastructure network."),
type=TYPES.help,
)
self.fields['DYNAMIC_ALLOCATION'] = Field(
text="Use dynamic IP address allocation",
type=TYPES.checkbox,
initial='Y'
)
self.fields['MULTICAST_CIDR'] = Field(
text="Management multicast subnet",
type=TYPES.string,
initial='239.1.1.0/28'
)
# Start/end ranges
self.fields['use_entire_subnet'] = Field(
text="Restrict management subnet address range",
type=TYPES.checkbox,
shows=["IP_START_ADDRESS", "IP_END_ADDRESS"],
transient=True
)
self.fields['IP_START_ADDRESS'] = Field(
text="Management network start address",
type=TYPES.string,
initial="192.168.204.2",
)
self.fields['IP_END_ADDRESS'] = Field(
text="Management network end address",
type=TYPES.string,
initial="192.168.204.254",
)
# Dynamic addressing
self.fields['dynamic_help'] = Field(
text=(
"IP addresses can be assigned to hosts dynamically or "
"a static IP address can be specified for each host. "
"Note: This choice applies to both the management network "
"and infrastructure network."),
type=TYPES.help,
)
self.fields['DYNAMIC_ALLOCATION'] = Field(
text="Use dynamic IP address allocation",
type=TYPES.checkbox,
initial='Y'
)
else:
self.fields['CIDR'] = Field(
text="Management subnet",
type=TYPES.string,
initial="192.168.204.0/28",
)
def validate_page(self):
super(MGMTPage, self).validate_page()
@ -1013,19 +1032,21 @@ class MGMTPage(ConfigPage):
def get_config(self):
super(MGMTPage, self).get_config()
# Add logical interface
ports = self.fields['mgmt_port1'].get_value()
if self.fields['mgmt_port2'].get_value():
ports += "," + self.fields['mgmt_port2'].get_value()
li = create_li(
lag=self.fields['LAG_INTERFACE'].get_value(),
mode=self.lag_choices.get(self.fields['LAG_MODE'].get_value()),
mtu=self.fields['INTERFACE_MTU'].get_value(),
link_capacity=self.fields['INTERFACE_LINK_CAPACITY'].get_value(),
ports=ports
)
config.set(self.section, 'LOGICAL_INTERFACE', li)
clean_lis()
if get_opt('SYSTEM', 'SYSTEM_MODE') != 'simplex':
# Add logical interface
ports = self.fields['mgmt_port1'].get_value()
if self.fields['mgmt_port2'].get_value():
ports += "," + self.fields['mgmt_port2'].get_value()
li = create_li(
lag=self.fields['LAG_INTERFACE'].get_value(),
mode=self.lag_choices.get(self.fields['LAG_MODE'].get_value()),
mtu=self.fields['INTERFACE_MTU'].get_value(),
link_capacity=self.fields[
'INTERFACE_LINK_CAPACITY'].get_value(),
ports=ports
)
config.set(self.section, 'LOGICAL_INTERFACE', li)
clean_lis()
class INFRAPage(ConfigPage):

View File

@ -87,7 +87,7 @@ LOOPBACK_IFNAME = 'lo'
DEFAULT_MULTICAST_SUBNET_IPV4 = '239.1.1.0/28'
DEFAULT_MULTICAST_SUBNET_IPV6 = 'ff08::1:1:0/124'
DEFAULT_MGMT_ON_LOOPBACK_SUBNET_IPV4 = '127.168.204.0/24'
DEFAULT_MGMT_ON_LOOPBACK_SUBNET_IPV4 = '192.168.204.0/28'
DEFAULT_REGION_NAME = "RegionOne"
DEFAULT_SERVICE_PROJECT_NAME = "services"

View File

@ -1322,13 +1322,47 @@ class ConfigAssistant():
""" Management interface configuration complete"""
self.management_interface_configured = True
def populate_aio_management_config(self):
"""Populate management on aio interface config."""
def input_aio_simplex_management_config(self, management_subnet=None):
"""Allow user to input AIO simplex management config and perform
validation."""
if management_subnet is not None:
self.management_subnet = management_subnet
else:
print "\nManagement Network:"
print "-------------------\n"
print textwrap.fill(
"The management network is used for internal communication "
"between platform components. IP addresses on this network "
"are reachable only within the host.", 80)
print
self.management_subnet = IPNetwork(
constants.DEFAULT_MGMT_ON_LOOPBACK_SUBNET_IPV4)
min_addresses = 16
while True:
user_input = input("Management subnet [" +
str(self.management_subnet) + "]: ")
if user_input.lower() == 'q':
raise UserQuit
elif user_input == "":
user_input = self.management_subnet
try:
tmp_management_subnet = validate_network_str(user_input,
min_addresses)
if tmp_management_subnet.version == 6:
print ("IPv6 management network not supported on " +
"simplex configuration")
continue
self.management_subnet = tmp_management_subnet
break
except ValidateFail as e:
print "{}".format(e)
self.management_interface = constants.LOOPBACK_IFNAME
self.management_interface_name = constants.LOOPBACK_IFNAME
self.management_subnet = IPNetwork(
constants.DEFAULT_MGMT_ON_LOOPBACK_SUBNET_IPV4)
self.management_start_address = self.management_subnet[2]
self.management_end_address = self.management_subnet[-2]
self.controller_floating_address = self.management_start_address
@ -2307,7 +2341,7 @@ class ConfigAssistant():
self.check_storage_config()
if self.system_mode == sysinv_constants.SYSTEM_MODE_SIMPLEX:
self.default_pxeboot_config()
self.populate_aio_management_config()
self.input_aio_simplex_management_config()
else:
# An AIO system cannot function as a Distributed Cloud System
# Controller
@ -2425,9 +2459,16 @@ class ConfigAssistant():
# Management network configuration
if self.system_mode == sysinv_constants.SYSTEM_MODE_SIMPLEX and \
not self.subcloud_config():
# For AIO-SX subcloud, mgmt n/w will be on a separate
# physical interface instead of the loopback interface.
self.populate_aio_management_config()
# For AIO-SX, only the management subnet is configurable
# (unless this is a subcloud).
if config.has_option('cMGMT', 'MANAGEMENT_SUBNET'):
management_subnet = IPNetwork(config.get(
'cMGMT', 'MANAGEMENT_SUBNET'))
else:
management_subnet = IPNetwork(
constants.DEFAULT_MGMT_ON_LOOPBACK_SUBNET_IPV4)
self.input_aio_simplex_management_config(
management_subnet=management_subnet)
else:
self.management_interface_name = config.get(
'cMGMT', 'MANAGEMENT_INTERFACE_NAME')

View File

@ -0,0 +1,25 @@
[LOGICAL_INTERFACE_1]
LAG_INTERFACE=N
;LAG_MODE=
INTERFACE_MTU=1500
;INTERFACE_LINK_CAPACITY=
INTERFACE_PORTS=eth0
[MGMT_NETWORK]
CIDR=192.168.42.0/28
[OAM_NETWORK]
IP_ADDRESS=10.10.10.20
CIDR=10.10.10.0/24
GATEWAY=10.10.10.1
LOGICAL_INTERFACE=LOGICAL_INTERFACE_1
[AUTHENTICATION]
ADMIN_PASSWORD=Li69nux*
[VERSION]
RELEASE = TEST.SW.VERSION
[SYSTEM]
SYSTEM_TYPE=All-in-one
SYSTEM_MODE=simplex

View File

@ -107,6 +107,62 @@ def test_system_config_simplex():
_test_system_config(systemfile)
def test_system_config_simplex_mgmt():
""" Test import of system_config file for AIO-simplex with management
configuration"""
# Create the path to the system_config file
systemfile = os.path.join(
os.getcwd(), "controllerconfig/tests/files/",
"system_config.simplex_mgmt")
_test_system_config(systemfile)
# Test MGMT_NETWORK parameters that are not allowed
system_config = cr.parse_system_config(systemfile)
system_config.set('MGMT_NETWORK', 'GATEWAY', '192.168.42.1')
with pytest.raises(exceptions.ConfigFail):
cr.create_cgcs_config_file(None, system_config, None, None, None, 0,
validate_only=True)
with pytest.raises(exceptions.ConfigFail):
validate(system_config, DEFAULT_CONFIG, None, False)
system_config = cr.parse_system_config(systemfile)
system_config.set('MGMT_NETWORK', 'LOGICAL_INTERFACE',
'LOGICAL_INTERFACE_1')
with pytest.raises(exceptions.ConfigFail):
cr.create_cgcs_config_file(None, system_config, None, None, None, 0,
validate_only=True)
with pytest.raises(exceptions.ConfigFail):
validate(system_config, DEFAULT_CONFIG, None, False)
# Test overlap with OAM network
system_config = cr.parse_system_config(systemfile)
system_config.set('MGMT_NETWORK', 'CIDR', '10.10.10.0/24')
with pytest.raises(exceptions.ConfigFail):
cr.create_cgcs_config_file(None, system_config, None, None, None, 0,
validate_only=True)
with pytest.raises(exceptions.ConfigFail):
validate(system_config, DEFAULT_CONFIG, None, False)
# Test IPv6 management CIDR (not supported)
system_config = cr.parse_system_config(systemfile)
system_config.set('MGMT_NETWORK', 'CIDR', 'FD01::0000/64')
with pytest.raises(exceptions.ConfigFail):
cr.create_cgcs_config_file(None, system_config, None, None, None, 0,
validate_only=True)
with pytest.raises(exceptions.ConfigFail):
validate(system_config, DEFAULT_CONFIG, None, False)
# Test management CIDR that is too small
system_config = cr.parse_system_config(systemfile)
system_config.set('MGMT_NETWORK', 'CIDR', '192.168.42.0/29')
with pytest.raises(exceptions.ConfigFail):
cr.create_cgcs_config_file(None, system_config, None, None, None, 0,
validate_only=True)
with pytest.raises(exceptions.ConfigFail):
validate(system_config, DEFAULT_CONFIG, None, False)
def test_system_config_validation():
""" Test detection of various errors in system_config file """

View File

@ -86,10 +86,20 @@ define network_address (
$address,
$ifname,
) {
# In AIO simplex configurations, the management addresses are assigned to the
# loopback interface. These addresses must be assigned using the host scope
# or assignment is prevented (can't have multiple global scope addresses on
# the loopback interface).
if $ifname == 'lo' {
$options = 'scope host'
} else {
$options = ''
}
# addresses should only be configured if running in simplex, otherwise SM
# will configure them on the active controller.
exec { "Configuring ${name} IP address":
command => "ip addr replace ${address} dev ${ifname}",
command => "ip addr replace ${address} dev ${ifname} ${options}",
onlyif => "test -f /etc/platform/simplex",
}
}

View File

@ -181,6 +181,13 @@ class NfvPuppet(openstack.OpenstackBasePuppet):
nova_oslo_messaging_data['password'],
}
config.update(rabbit_config)
# Listen to nova api proxy on management address
nova_api_proxy_config = {
'nfv::nfvi::compute_rest_api_host':
self._get_management_address(),
}
config.update(nova_api_proxy_config)
else:
# The openstack auth info is still required as the VIM will
# audit some keystone entities (e.g. tenants). Point it to