Merge "Introduce Puppet variables for primary and secondary pool addresses."

This commit is contained in:
Zuul 2024-03-13 13:33:01 +00:00 committed by Gerrit Code Review
commit 1b9bc6ed76
6 changed files with 1165 additions and 144 deletions

View File

@ -1037,6 +1037,20 @@ class ConductorManager(service.PeriodicService):
True) True)
return address.address return address.address
except exception.AddressNotFoundByName: except exception.AddressNotFoundByName:
LOG.info(f"cannot find address with name={name}")
return None
def _lookup_static_ip_address_family(self, name, networktype, family):
""""Find a statically configured address based on name, network type,
and address family."""
try:
# address names are refined by network type to ensure they are
# unique across different address pools
name = cutils.format_address_name(name, networktype)
address = self.dbapi.address_get_by_name_and_family(name, family)
return address.address
except exception.AddressNotFoundByNameAndFamily:
LOG.info(f"cannot find address with name={name}, family={family}")
return None return None
def _using_static_ip(self, ihost, personality=None, hostname=None): def _using_static_ip(self, ihost, personality=None, hostname=None):
@ -1378,7 +1392,7 @@ class ConductorManager(service.PeriodicService):
) )
func = "_generate_dnsmasq_hosts_file" func = "_generate_dnsmasq_hosts_file"
with open(temp_dnsmasq_hosts_file, 'w') as f_out,\ with open(temp_dnsmasq_hosts_file, 'w') as f_out, \
open(temp_dnsmasq_addn_hosts_file, 'w') as f_out_addn: open(temp_dnsmasq_addn_hosts_file, 'w') as f_out_addn:
# Write entry for pxecontroller into addn_hosts file # Write entry for pxecontroller into addn_hosts file
@ -2054,7 +2068,19 @@ class ConductorManager(service.PeriodicService):
pass pass
def _create_or_update_address(self, context, hostname, ip_address, def _create_or_update_address(self, context, hostname, ip_address,
iface_type, iface_id=None): iface_type, iface_id=None, pool_uuid=None):
"""Searches the address database and create or update accordingly
Args:
hostname (str): The host name
ip_address (str): The IP address to be created or updated.
iface_type (str): The interface network type.
iface_id (int, optional): Interface ID that uses this address. Defaults to None.
pool_uuid (str, optional): The address pool uuid. Defaults to None.
Returns:
sysinv.object.address: The updated or created address
"""
if hostname is None or ip_address is None: if hostname is None or ip_address is None:
return return
address_name = cutils.format_address_name(hostname, iface_type) address_name = cutils.format_address_name(hostname, iface_type)
@ -2062,34 +2088,41 @@ class ConductorManager(service.PeriodicService):
try: try:
address = self.dbapi.address_get_by_address(ip_address) address = self.dbapi.address_get_by_address(ip_address)
address_uuid = address['uuid'] address_uuid = address['uuid']
search_addr = self.dbapi.address_get_by_name_and_family(address_name,
address_family)
# If name is already set, return # If name is already set, return
search_addr = cutils.get_primary_address_by_name(self.dbapi,
address_name,
iface_type, True)
if search_addr: if search_addr:
if (search_addr.uuid == address_uuid and iface_id is None): if (search_addr.uuid == address_uuid and iface_id is None):
LOG.info(f"returning, address '{address_uuid}' exists and iface_id is None")
return return
except exception.AddressNotFoundByAddress: except exception.AddressNotFoundByAddress:
address_uuid = None address_uuid = None
except exception.AddressNotFoundByName: except exception.AddressNotFoundByNameAndFamily:
pass pass
network = self.dbapi.network_get_by_type(iface_type)
address_pool_uuid = network.pool_uuid
address_pool = self.dbapi.address_pool_get(address_pool_uuid)
values = {
'name': address_name,
'family': address_family,
'prefix': address_pool.prefix,
'address': ip_address,
'address_pool_id': address_pool.id,
}
if iface_id: address_pool = None
values['interface_id'] = iface_id if pool_uuid:
if address_uuid: address_pool = self.dbapi.address_pool_get(pool_uuid)
address = self.dbapi.address_update(address_uuid, values)
else: else:
address = self.dbapi.address_create(values) network = self.dbapi.network_get_by_type(iface_type)
address_pool = self.dbapi.address_pool_get(network.pool_uuid)
if address_pool:
values = {
'name': address_name,
'family': address_family,
'prefix': address_pool.prefix,
'address': ip_address,
'address_pool_id': address_pool.id,
}
if iface_id:
values['interface_id'] = iface_id
if address_uuid:
address = self.dbapi.address_update(address_uuid, values)
else:
address = self.dbapi.address_create(values)
self._generate_dnsmasq_hosts_file() self._generate_dnsmasq_hosts_file()
return address return address
@ -2110,19 +2143,32 @@ class ConductorManager(service.PeriodicService):
# controller must have cluster-host address already allocated # controller must have cluster-host address already allocated
if (host.personality != constants.CONTROLLER): if (host.personality != constants.CONTROLLER):
network = self.dbapi.network_get_by_type(constants.NETWORK_TYPE_CLUSTER_HOST)
net_pools = self.dbapi.network_addrpool_get_by_network_id(network.id)
pool_uuid_list = list()
if net_pools:
for net_pool in net_pools:
pool_uuid_list.append(net_pool.address_pool_uuid)
else:
# we are coming from an upgrade without data-migration implemented for the
# dual stack feature
LOG.warning(f"Network {network.name} does not have network to address pool objects")
pool_uuid_list.append(network.pool_uuid)
cluster_host_address = self._lookup_static_ip_address( hostname = host.hostname
host.hostname, constants.NETWORK_TYPE_CLUSTER_HOST)
if cluster_host_address is None: for pool_uuid in pool_uuid_list:
address_name = cutils.format_address_name( pool = self.dbapi.address_pool_get(pool_uuid)
host.hostname, constants.NETWORK_TYPE_CLUSTER_HOST)
LOG.info("{} address not found. Allocating address for {}.".format( cluster_host_address = self._lookup_static_ip_address_family(
address_name, host.hostname)) host.hostname, constants.NETWORK_TYPE_CLUSTER_HOST, pool.family)
host_network = self.dbapi.network_get_by_type(
constants.NETWORK_TYPE_CLUSTER_HOST) if cluster_host_address is None:
self._allocate_pool_address(None, host_network.pool_uuid, address_name = cutils.format_address_name(
address_name) hostname, constants.NETWORK_TYPE_CLUSTER_HOST)
resp_addr = self._allocate_pool_address(None, pool.uuid, address_name)
LOG.info(f"{address_name} address not found."
f" Allocating address {resp_addr.address} for {hostname}.")
def _allocate_addresses_for_host(self, context, host): def _allocate_addresses_for_host(self, context, host):
"""Allocates addresses for a given host. """Allocates addresses for a given host.
@ -2142,28 +2188,40 @@ class ConductorManager(service.PeriodicService):
mgmt_interface_id = None mgmt_interface_id = None
if mgmt_interfaces: if mgmt_interfaces:
mgmt_interface_id = mgmt_interfaces[0]['id'] mgmt_interface_id = mgmt_interfaces[0]['id']
hostname = host.hostname
# check for static mgmt IP hostname = host.hostname
mgmt_ip = self._lookup_static_ip_address( mgmt_net = self.dbapi.network_get_by_type(constants.NETWORK_TYPE_MGMT)
hostname, constants.NETWORK_TYPE_MGMT net_pools = self.dbapi.network_addrpool_get_by_network_id(mgmt_net.id)
) pool_uuid_list = list()
# make sure address in address table and update dnsmasq host file if net_pools:
if mgmt_ip: for net_pool in net_pools:
LOG.info("Static mgmt ip {} for host{}".format(mgmt_ip, hostname)) pool_uuid_list.append(net_pool.address_pool_uuid)
self._create_or_update_address(context, hostname, mgmt_ip, else:
constants.NETWORK_TYPE_MGMT, # we are coming from an upgrade without data-migration implemented for the
mgmt_interface_id) # dual stack feature
# if no static address, then allocate one LOG.warning(f"Network {mgmt_net.name} does not have network to address pool objects")
if not mgmt_ip: pool_uuid_list.append(mgmt_net.pool_uuid)
mgmt_pool = self.dbapi.network_get_by_type(
constants.NETWORK_TYPE_MGMT for pool_uuid in pool_uuid_list:
).pool_uuid pool = self.dbapi.address_pool_get(pool_uuid)
address_name = cutils.format_address_name(hostname,
constants.NETWORK_TYPE_MGMT) # check for static mgmt IP
mgmt_ip = self._allocate_pool_address(mgmt_interface_id, mgmt_pool, mgmt_ip = self._lookup_static_ip_address_family(
address_name).address hostname, constants.NETWORK_TYPE_MGMT, pool.family)
LOG.info("Allocated mgmt ip {} for host{}".format(mgmt_ip, hostname))
# make sure address in address table and update dnsmasq host file
if mgmt_ip:
LOG.info("Static mgmt ip {} for host{}".format(mgmt_ip, hostname))
self._create_or_update_address(context, hostname, mgmt_ip,
constants.NETWORK_TYPE_MGMT,
mgmt_interface_id, pool_uuid)
# if no static address, then allocate one
if not mgmt_ip:
address_name = cutils.format_address_name(hostname,
constants.NETWORK_TYPE_MGMT)
mgmt_ip = self._allocate_pool_address(mgmt_interface_id, pool_uuid,
address_name).address
LOG.info(f"Allocated mgmt ip {mgmt_ip} for host={hostname}")
self._generate_dnsmasq_hosts_file(existing_host=host) self._generate_dnsmasq_hosts_file(existing_host=host)
self._allocate_cluster_host_address_for_host(host) self._allocate_cluster_host_address_for_host(host)
@ -2930,6 +2988,7 @@ class ConductorManager(service.PeriodicService):
puppet_common.puppet_apply_manifest(host.hostname, puppet_common.puppet_apply_manifest(host.hostname,
constants.WORKER, constants.WORKER,
do_reboot=True) do_reboot=True)
return host return host
def unconfigure_ihost(self, context, ihost_obj): def unconfigure_ihost(self, context, ihost_obj):
@ -3616,23 +3675,29 @@ class ConductorManager(service.PeriodicService):
if set_address_interface: if set_address_interface:
if new_interface and 'id' in new_interface: if new_interface and 'id' in new_interface:
values = {'interface_id': new_interface['id']} values = {'interface_id': new_interface['id']}
address = cutils.get_primary_address_by_name(self.dbapi, try:
cutils.format_address_name(ihost.hostname, new_interface_networktype), addr_name = cutils.format_address_name(
new_interface_networktype) ihost.hostname, new_interface_networktype)
if address: addresses = self.dbapi.address_get_by_name(addr_name)
self.dbapi.address_update(address['uuid'], values) for address in addresses:
self.dbapi.address_update(address['uuid'], values)
except exception.AddressNotFoundByName:
pass
# Do any potential distributed cloud config # Do any potential distributed cloud config
# We do this here where the interface is created. # We do this here where the interface is created.
cutils.perform_distributed_cloud_config(self.dbapi, cutils.perform_distributed_cloud_config(self.dbapi,
new_interface['id']) new_interface['id'])
if port: if port:
values = {'interface_id': port.interface_id} values = {'interface_id': port.interface_id}
address = cutils.get_primary_address_by_name(self.dbapi, try:
cutils.format_address_name(ihost.hostname, networktype), addr_name = cutils.format_address_name(ihost.hostname,
networktype) networktype)
if address: addresses = self.dbapi.address_get_by_name(addr_name)
if address['interface_id'] is None: for address in addresses:
self.dbapi.address_update(address['uuid'], values) if address['interface_id'] is None:
self.dbapi.address_update(address['uuid'], values)
except exception.AddressNotFoundByName:
pass
if ihost.invprovision not in [constants.PROVISIONED, constants.PROVISIONING, constants.UPGRADING]: if ihost.invprovision not in [constants.PROVISIONED, constants.PROVISIONING, constants.UPGRADING]:
LOG.info("Updating %s host invprovision from %s to %s" % LOG.info("Updating %s host invprovision from %s to %s" %
@ -12359,7 +12424,7 @@ class ConductorManager(service.PeriodicService):
if not drbd_fs_updated: if not drbd_fs_updated:
rc = True rc = True
else: else:
while(loop_timeout <= max_loop): while (loop_timeout <= max_loop):
if constants.DRBD_PGSQL in (drbd_fs_updated - drbd_fs_resized): if constants.DRBD_PGSQL in (drbd_fs_updated - drbd_fs_resized):
if (not standby_host or (standby_host and if (not standby_host or (standby_host and
constants.DRBD_PGSQL in self._drbd_fs_sync())): constants.DRBD_PGSQL in self._drbd_fs_sync())):

View File

@ -5154,3 +5154,11 @@ class Connection(object):
@abc.abstractmethod @abc.abstractmethod
def kube_app_bundle_destroy_by_file_path(self, file_path): def kube_app_bundle_destroy_by_file_path(self, file_path):
"""Delete records from kube_app_bundle that match a file path""" """Delete records from kube_app_bundle that match a file path"""
@abc.abstractmethod
def address_get_by_name_and_family(self, name, family):
""" Search database address using name and family
:param name: address name.
:param family: address family (4 or 6).
"""

View File

@ -183,6 +183,14 @@ class BasePuppet(object):
return address return address
def _get_address_by_name_and_family(self, name, family, networktype):
"""
Retrieve an address entry by name and scoped by network type
"""
address_name = utils.format_address_name(name, networktype)
return self.dbapi.address_get_by_name_and_family(address_name,
family)
def _get_management_address(self): def _get_management_address(self):
address = self._get_address_by_name( address = self._get_address_by_name(
constants.CONTROLLER_HOSTNAME, constants.NETWORK_TYPE_MGMT) constants.CONTROLLER_HOSTNAME, constants.NETWORK_TYPE_MGMT)

View File

@ -6,6 +6,8 @@
import netaddr import netaddr
from oslo_log import log
from sysinv.common import constants from sysinv.common import constants
from sysinv.common import exception from sysinv.common import exception
from sysinv.common import utils from sysinv.common import utils
@ -13,6 +15,11 @@ from sysinv.common import utils
from sysinv.puppet import base from sysinv.puppet import base
from sysinv.puppet import interface from sysinv.puppet import interface
LOG = log.getLogger(__name__)
IPv4 = constants.IP_FAMILIES[constants.IPV4_FAMILY].lower()
IPv6 = constants.IP_FAMILIES[constants.IPV6_FAMILY].lower()
class NetworkingPuppet(base.BasePuppet): class NetworkingPuppet(base.BasePuppet):
"""Class to encapsulate puppet operations for networking configuration""" """Class to encapsulate puppet operations for networking configuration"""
@ -49,16 +56,7 @@ class NetworkingPuppet(base.BasePuppet):
config = self._get_network_config(networktype) config = self._get_network_config(networktype)
try: config = self._get_network_gateway_config(networktype, config)
gateway_address = self._get_address_by_name(
constants.CONTROLLER_GATEWAY, networktype).address
except exception.AddressNotFoundByName:
gateway_address = None
config.update({
'platform::network::%s::params::gateway_address' % networktype:
gateway_address,
})
# create flag for the mate controller to use FQDN or not # create flag for the mate controller to use FQDN or not
if utils.is_fqdn_ready_to_use(True): if utils.is_fqdn_ready_to_use(True):
@ -100,16 +98,7 @@ class NetworkingPuppet(base.BasePuppet):
config = self._get_network_config(networktype) config = self._get_network_config(networktype)
try: config = self._get_network_gateway_config(networktype, config)
gateway_address = self._get_address_by_name(
constants.CONTROLLER_GATEWAY, networktype).address
except exception.AddressNotFoundByName:
gateway_address = None
config.update({
'platform::network::%s::params::gateway_address' % networktype:
gateway_address,
})
return config return config
@ -132,71 +121,125 @@ class NetworkingPuppet(base.BasePuppet):
try: try:
network = self.dbapi.network_get_by_type(networktype) network = self.dbapi.network_get_by_type(networktype)
except exception.NetworkTypeNotFound: except exception.NetworkTypeNotFound:
# network not configured LOG.debug(f"Network type {networktype} not found")
return {} return {}
address_pool = self.dbapi.address_pool_get(network.pool_uuid) net_pools = self.dbapi.network_addrpool_get_by_network_id(network.id)
pool_uuid_list = list()
if net_pools:
for net_pool in net_pools:
pool_uuid_list.append(net_pool.address_pool_uuid)
else:
# we are coming from an upgrade without data-migration implemented for the
# dual stack feature
LOG.warning(f"Network {network.name} does not have network to address pool objects")
pool_uuid_list.append(network.pool_uuid)
subnet = netaddr.IPNetwork( configdata = dict()
str(address_pool.network) + '/' + str(address_pool.prefix)) config = dict()
subnet_version = address_pool.family for pool_uuid in pool_uuid_list:
subnet_network = str(subnet.network)
subnet_netmask = str(subnet.netmask)
subnet_prefixlen = subnet.prefixlen
subnet_start = str(address_pool.ranges[0][0]) address_pool = self.dbapi.address_pool_get(pool_uuid)
subnet_end = str(address_pool.ranges[0][-1])
try: family_name = IPv4 if address_pool.family == constants.IPV4_FAMILY else IPv6
controller_address = self._get_address_by_name( configdata.update({family_name: {}})
constants.CONTROLLER_HOSTNAME, networktype).address
except exception.AddressNotFoundByName:
controller_address = None
try: subnet = netaddr.IPNetwork(
controller0_address = self._get_address_by_name( str(address_pool.network) + '/' + str(address_pool.prefix))
constants.CONTROLLER_0_HOSTNAME, networktype).address configdata[family_name].update({'subnet': subnet})
except exception.AddressNotFoundByName:
controller0_address = None
try: configdata[family_name].update({'subnet_version': address_pool.family})
controller1_address = self._get_address_by_name( configdata[family_name].update({'subnet_network': str(subnet.network)})
constants.CONTROLLER_1_HOSTNAME, networktype).address configdata[family_name].update({'subnet_netmask': str(subnet.netmask)})
except exception.AddressNotFoundByName: configdata[family_name].update({'subnet_prefixlen': subnet.prefixlen})
controller1_address = None configdata[family_name].update({'subnet_start': str(address_pool.ranges[0][0])})
configdata[family_name].update({'subnet_end': str(address_pool.ranges[0][-1])})
controller_address_url = self._format_url_address(controller_address) try:
subnet_network_url = self._format_url_address(subnet_network) controller_address = self._get_address_by_name_and_family(
constants.CONTROLLER_HOSTNAME, address_pool.family, networktype).address
except exception.AddressNotFoundByNameAndFamily:
controller_address = None
configdata[family_name].update({'controller_address': controller_address})
try:
controller0_address = self._get_address_by_name_and_family(
constants.CONTROLLER_0_HOSTNAME, address_pool.family, networktype).address
except exception.AddressNotFoundByNameAndFamily:
controller0_address = None
configdata[family_name].update({'controller0_address': controller0_address})
try:
controller1_address = self._get_address_by_name_and_family(
constants.CONTROLLER_1_HOSTNAME, address_pool.family, networktype).address
except exception.AddressNotFoundByNameAndFamily:
controller1_address = None
configdata[family_name].update({'controller1_address': controller1_address})
configdata[family_name].update({'controller_address_url':
self._format_url_address(controller_address)})
configdata[family_name].update({'subnet_network_url':
self._format_url_address(str(subnet.network))})
# Convert the dash to underscore because puppet parameters cannot have # Convert the dash to underscore because puppet parameters cannot have
# dashes # dashes
networktype = networktype.replace('-', '_') networktype = networktype.replace('-', '_')
return { for family in configdata:
'platform::network::%s::params::subnet_version' % networktype: config[f'platform::network::{networktype}::{family}::params::subnet_version'] = \
subnet_version, configdata[family]['subnet_version']
'platform::network::%s::params::subnet_network' % networktype: config[f'platform::network::{networktype}::{family}::params::subnet_network'] = \
subnet_network, configdata[family]['subnet_network']
'platform::network::%s::params::subnet_network_url' % networktype: config[f'platform::network::{networktype}::{family}::params::subnet_network_url'] = \
subnet_network_url, configdata[family]['subnet_network_url']
'platform::network::%s::params::subnet_prefixlen' % networktype: config[f'platform::network::{networktype}::{family}::params::subnet_prefixlen'] = \
subnet_prefixlen, configdata[family]['subnet_prefixlen']
'platform::network::%s::params::subnet_netmask' % networktype: config[f'platform::network::{networktype}::{family}::params::subnet_netmask'] = \
subnet_netmask, configdata[family]['subnet_netmask']
'platform::network::%s::params::subnet_start' % networktype: config[f'platform::network::{networktype}::{family}::params::subnet_start'] = \
subnet_start, configdata[family]['subnet_start']
'platform::network::%s::params::subnet_end' % networktype: config[f'platform::network::{networktype}::{family}::params::subnet_end'] = \
subnet_end, configdata[family]['subnet_end']
'platform::network::%s::params::controller_address' % networktype: config[f'platform::network::{networktype}::{family}::params::controller_address'] = \
controller_address, configdata[family]['controller_address']
'platform::network::%s::params::controller_address_url' % networktype: config[f'platform::network::{networktype}::{family}::params::controller_address_url'] = \
controller_address_url, configdata[family]['controller_address_url']
'platform::network::%s::params::controller0_address' % networktype: config[f'platform::network::{networktype}::{family}::params::controller0_address'] = \
controller0_address, configdata[family]['controller0_address']
'platform::network::%s::params::controller1_address' % networktype: config[f'platform::network::{networktype}::{family}::params::controller1_address'] = \
controller1_address, configdata[family]['controller1_address']
}
if network.primary_pool_family \
and (network.primary_pool_family).lower() in configdata.keys():
family = network.primary_pool_family.lower()
config[f'platform::network::{networktype}::params::subnet_version'] = \
configdata[family]['subnet_version']
config[f'platform::network::{networktype}::params::subnet_network'] = \
configdata[family]['subnet_network']
config[f'platform::network::{networktype}::params::subnet_network_url'] = \
configdata[family]['subnet_network_url']
config[f'platform::network::{networktype}::params::subnet_prefixlen'] = \
configdata[family]['subnet_prefixlen']
config[f'platform::network::{networktype}::params::subnet_netmask'] = \
configdata[family]['subnet_netmask']
config[f'platform::network::{networktype}::params::subnet_start'] = \
configdata[family]['subnet_start']
config[f'platform::network::{networktype}::params::subnet_end'] = \
configdata[family]['subnet_end']
config[f'platform::network::{networktype}::params::controller_address'] = \
configdata[family]['controller_address']
config[f'platform::network::{networktype}::params::controller_address_url'] = \
configdata[family]['controller_address_url']
config[f'platform::network::{networktype}::params::controller0_address'] = \
configdata[family]['controller0_address']
config[f'platform::network::{networktype}::params::controller1_address'] = \
configdata[family]['controller1_address']
else:
LOG.error(f"Network {network.name}, type {network.type} does not have a valid"
f" primary pool address family: {network.primary_pool_family}.")
return config
def _get_pxeboot_interface_config(self): def _get_pxeboot_interface_config(self):
return self._get_interface_config(constants.NETWORK_TYPE_PXEBOOT) return self._get_interface_config(constants.NETWORK_TYPE_PXEBOOT)
@ -219,6 +262,53 @@ class NetworkingPuppet(base.BasePuppet):
def _get_admin_interface_config(self): def _get_admin_interface_config(self):
return self._get_interface_config(constants.NETWORK_TYPE_ADMIN) return self._get_interface_config(constants.NETWORK_TYPE_ADMIN)
def _get_network_gateway_config(self, networktype, config):
try:
network = self.dbapi.network_get_by_type(networktype)
except exception.NetworkTypeNotFound:
LOG.debug(f"Network type {networktype} not found")
return {}
net_pools = self.dbapi.network_addrpool_get_by_network_id(network.id)
pool_uuid_list = list()
if net_pools:
for net_pool in net_pools:
pool_uuid_list.append(net_pool.address_pool_uuid)
else:
# we are coming from an upgrade without data-migration implemented for the
# dual stack feature
LOG.warning(f"Network {network.name} does not have network to address pool objects")
pool_uuid_list.append(network.pool_uuid)
configdata = dict()
for pool_uuid in pool_uuid_list:
address_pool = self.dbapi.address_pool_get(pool_uuid)
family = IPv4 if address_pool.family == constants.IPV4_FAMILY else IPv6
configdata.update({family: {}})
try:
gateway_address = self._get_address_by_name_and_family(
constants.CONTROLLER_GATEWAY, address_pool.family, networktype).address
except exception.AddressNotFoundByNameAndFamily:
gateway_address = None
configdata[family].update({'gateway_address': gateway_address})
for family in configdata:
config.update({f'platform::network::{networktype}::{family}::params::gateway_address':
configdata[family]['gateway_address']})
if network.primary_pool_family \
and (network.primary_pool_family).lower() in configdata.keys():
family = network.primary_pool_family.lower()
config.update({f'platform::network::{networktype}::params::gateway_address':
configdata[family]['gateway_address']})
else:
LOG.error(f"Network {network.name}, type {network.type} does not have a valid"
f" primary pool address family: {network.primary_pool_family}.")
return config
def _set_ptp_instance_global_parameters(self, ptp_instances, ptp_parameters_instance): def _set_ptp_instance_global_parameters(self, ptp_instances, ptp_parameters_instance):
default_global_parameters = { default_global_parameters = {
@ -546,10 +636,9 @@ class NetworkingPuppet(base.BasePuppet):
self.context, network_interface) self.context, network_interface)
interface_devices = interface.get_interface_devices( interface_devices = interface.get_interface_devices(
self.context, network_interface) self.context, network_interface)
network_id = interface.find_network_id_by_networktype(
self.context, networktype)
# Convert the dash to underscore because puppet parameters cannot # Convert the dash to underscore because puppet parameters cannot
# have dashes # have dashes
network = self.context['networks'].get(networktype, None)
networktype = networktype.replace('-', '_') networktype = networktype.replace('-', '_')
config.update({ config.update({
'platform::network::%s::params::interface_name' % networktype: 'platform::network::%s::params::interface_name' % networktype:
@ -560,13 +649,23 @@ class NetworkingPuppet(base.BasePuppet):
network_interface.imtu network_interface.imtu
}) })
interface_address = interface.get_interface_primary_address( addresses = self.context['addresses'].get(network_interface['ifname'], [])
self.context, network_interface, network_id) for address in addresses:
if interface_address: family = "ipv4" if address.family == constants.IPV4_FAMILY else "ipv6"
config.update({ config.update({
'platform::network::%s::params::interface_address' % f'platform::network::{networktype}::{family}::params::interface_address':
networktype: address.address
interface_address['address']
}) })
if network:
for address in addresses:
family = "ipv4" if address.family == constants.IPV4_FAMILY else "ipv6"
prim_family = network.primary_pool_family.lower()
if prim_family == family:
config.update({
f'platform::network::{networktype}::params::interface_address':
address.address
})
break
return config return config

View File

@ -25,6 +25,7 @@
import copy import copy
import mock import mock
import os.path import os.path
import netaddr
import subprocess import subprocess
import tempfile import tempfile
import uuid import uuid
@ -5924,6 +5925,34 @@ class ManagerTestCaseInternal(base.BaseHostTestCase):
self.service = manager.ConductorManager('test-host', 'test-topic') self.service = manager.ConductorManager('test-host', 'test-topic')
self.service.dbapi = dbapi.get_instance() self.service.dbapi = dbapi.get_instance()
def _create_test_ihost(self, **kwargs):
# ensure the system ID for proper association
kwargs['forisystemid'] = self.system['id']
ihost_dict = utils.get_test_ihost(**kwargs)
# Let DB generate ID if it isn't specified explicitly
if 'id' not in kwargs:
del ihost_dict['id']
ihost = self.service.dbapi.ihost_create(ihost_dict)
return ihost
def create_ipv6_pools(self):
mgmt_subnet6 = netaddr.IPNetwork('fd01::/64')
oam_subnet6 = netaddr.IPNetwork('fd00::/64')
cluster_host_subnet6 = netaddr.IPNetwork('fd02::/64')
cluster_pod_subnet6 = netaddr.IPNetwork('fd03::/64')
cluster_service_subnet6 = netaddr.IPNetwork('fd04::/112')
multicast_subnet6 = netaddr.IPNetwork('ff08::1:1:0/124')
storage_subnet6 = netaddr.IPNetwork('fd05::/64')
admin_subnet6 = netaddr.IPNetwork('fd09::/64')
self._create_test_address_pool(name="management-ipv6", subnet=mgmt_subnet6)
self._create_test_address_pool(name="oam-ipv6", subnet=oam_subnet6)
self._create_test_address_pool(name="cluster-host-ipv6", subnet=cluster_host_subnet6)
self._create_test_address_pool(name="cluster-pod-ipv6", subnet=cluster_pod_subnet6)
self._create_test_address_pool(name="cluster-service-ipv6", subnet=cluster_service_subnet6)
self._create_test_address_pool(name="multicast-ipv6", subnet=multicast_subnet6)
self._create_test_address_pool(name="storage-ipv6", subnet=storage_subnet6)
self._create_test_address_pool(name="admin-ipv6", subnet=admin_subnet6)
def test_remove_lease_for_address(self): def test_remove_lease_for_address(self):
# create test interface # create test interface
ihost = self._create_test_host( ihost = self._create_test_host(
@ -5969,3 +5998,138 @@ class ManagerTestCaseInternal(base.BaseHostTestCase):
self.service._remove_lease_for_address(ihost.hostname, self.service._remove_lease_for_address(ihost.hostname,
constants.NETWORK_TYPE_MGMT) constants.NETWORK_TYPE_MGMT)
def test_configure_ihost_allocate_addresses_for_host(self):
# Test skipped to prevent error message in Jenkins. Error thrown is:
# in test_configure_ihost_allocate_addresses_for_host
# with open(self.dnsmasq_hosts_file, 'w') as f:
# IOError: [Errno 13] Permission denied: '/tmp/dnsmasq.hosts'
# self.skipTest("Skipping to prevent failure notification on Jenkins")
self.context = context.get_admin_context()
self.service._generate_dnsmasq_hosts_file = mock.Mock()
self.service._puppet = mock.Mock()
self.service._update_pxe_config = mock.Mock()
# create a basic ihost object
ihost = self._create_test_ihost()
self.create_ipv6_pools()
net_mgmt = self.dbapi.network_get_by_type(constants.NETWORK_TYPE_MGMT)
pool_mgmt6 = self.dbapi.address_pool_query({"name": "management-ipv6"})
pool_mgmt4 = self.dbapi.address_pool_query({"name": "management"})
dbutils.create_test_network_addrpool(address_pool_id=pool_mgmt6.id, network_id=net_mgmt.id)
net_clhost = self.dbapi.network_get_by_type(constants.NETWORK_TYPE_CLUSTER_HOST)
pool_clhost6 = self.dbapi.address_pool_query({"name": "cluster-host-ipv6"})
pool_clhost4 = self.dbapi.address_pool_query({"name": "cluster-host"})
dbutils.create_test_network_addrpool(address_pool_id=pool_clhost6.id, network_id=net_clhost.id)
worker_name = 'newhost'
ihost['mgmt_mac'] = '00:11:22:33:44:55'
ihost['hostname'] = worker_name
ihost['invprovision'] = 'unprovisioned'
ihost['personality'] = 'worker'
ihost['administrative'] = 'locked'
ihost['operational'] = 'disabled'
ihost['availability'] = 'not-installed'
ihost['serialid'] = '1234567890abc'
ihost['boot_device'] = 'sda'
ihost['rootfs_device'] = 'sda'
ihost['hw_settle'] = '0'
ihost['install_output'] = 'text'
ihost['console'] = 'ttyS0,115200'
self.service.configure_ihost(self.context, ihost)
addr_mgmt4 = self.dbapi.address_get_by_name_and_family(
f"{worker_name}-{constants.NETWORK_TYPE_MGMT}",
constants.IPV4_FAMILY)
self.assertEqual(addr_mgmt4.pool_uuid, pool_mgmt4.uuid)
self.assertEqual(addr_mgmt4.family, pool_mgmt4.family)
addr_mgmt6 = self.dbapi.address_get_by_name_and_family(
f"{worker_name}-{constants.NETWORK_TYPE_MGMT}",
constants.IPV6_FAMILY)
self.assertEqual(addr_mgmt6.pool_uuid, pool_mgmt6.uuid)
self.assertEqual(addr_mgmt6.family, pool_mgmt6.family)
addr_clhost4 = self.dbapi.address_get_by_name_and_family(
f"{worker_name}-{constants.NETWORK_TYPE_CLUSTER_HOST}",
constants.IPV4_FAMILY)
self.assertEqual(addr_clhost4.pool_uuid, pool_clhost4.uuid)
addr_clhost6 = self.dbapi.address_get_by_name_and_family(
f"{worker_name}-{constants.NETWORK_TYPE_CLUSTER_HOST}",
constants.IPV6_FAMILY)
self.assertEqual(addr_clhost6.pool_uuid, pool_clhost6.uuid)
def test_configure_ihost_allocate_addresses_for_host_no_net_pool_object(self):
# the data-migration for upgrade was not implemented yet for the dual-stack feature
# this test aims to validate this condition
# self.skipTest("Skipping to prevent failure notification on Jenkins")
self.context = context.get_admin_context()
self.service._generate_dnsmasq_hosts_file = mock.Mock()
self.service._puppet = mock.Mock()
self.service._update_pxe_config = mock.Mock()
# create a basic ihost object
ihost = self._create_test_ihost()
self.create_ipv6_pools()
pool_mgmt4 = self.dbapi.address_pool_query({"name": "management"})
pool_clhost4 = self.dbapi.address_pool_query({"name": "cluster-host"})
net_pools = self.dbapi.network_addrpool_get_all()
for net_pool in net_pools:
self.dbapi.network_addrpool_destroy(net_pool.uuid)
worker_name = 'newhost'
ihost['mgmt_mac'] = '00:11:22:33:44:55'
ihost['hostname'] = worker_name
ihost['invprovision'] = 'unprovisioned'
ihost['personality'] = 'worker'
ihost['administrative'] = 'locked'
ihost['operational'] = 'disabled'
ihost['availability'] = 'not-installed'
ihost['serialid'] = '1234567890abc'
ihost['boot_device'] = 'sda'
ihost['rootfs_device'] = 'sda'
ihost['hw_settle'] = '0'
ihost['install_output'] = 'text'
ihost['console'] = 'ttyS0,115200'
self.assertRaises(exception.AddressNotFoundByNameAndFamily,
self.dbapi.address_get_by_name_and_family,
f"{worker_name}-{constants.NETWORK_TYPE_MGMT}",
constants.IPV4_FAMILY)
self.assertRaises(exception.AddressNotFoundByNameAndFamily,
self.dbapi.address_get_by_name_and_family,
f"{worker_name}-{constants.NETWORK_TYPE_CLUSTER_HOST}",
constants.IPV6_FAMILY)
self.service.configure_ihost(self.context, ihost)
addr_mgmt4 = self.dbapi.address_get_by_name_and_family(
f"{worker_name}-{constants.NETWORK_TYPE_MGMT}",
constants.IPV4_FAMILY)
self.assertEqual(addr_mgmt4.pool_uuid, pool_mgmt4.uuid)
self.assertEqual(addr_mgmt4.family, pool_mgmt4.family)
self.assertRaises(exception.AddressNotFoundByNameAndFamily,
self.dbapi.address_get_by_name_and_family,
f"{worker_name}-{constants.NETWORK_TYPE_MGMT}",
constants.IPV6_FAMILY)
addr_clhost4 = self.dbapi.address_get_by_name_and_family(
f"{worker_name}-{constants.NETWORK_TYPE_CLUSTER_HOST}",
constants.IPV4_FAMILY)
self.assertEqual(addr_clhost4.pool_uuid, pool_clhost4.uuid)
self.assertRaises(exception.AddressNotFoundByNameAndFamily,
self.dbapi.address_get_by_name_and_family,
f"{worker_name}-{constants.NETWORK_TYPE_CLUSTER_HOST}",
constants.IPV6_FAMILY)

View File

@ -0,0 +1,677 @@
# Copyright (c) 2024 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import uuid
import os
import yaml
import netaddr
from sysinv.tests.puppet import base
from sysinv.puppet import puppet
from sysinv.tests.db import base as dbbase
from sysinv.common import constants
from sysinv.tests.db import utils as dbutils
from sysinv.db import api as db_api
class NetworkingTestCaseMixin(base.PuppetTestCaseMixin):
""" This PlatformFirewallTestCaseMixin needs to be used with a subclass
of BaseHostTestCase
"""
@puppet.puppet_context
def _update_context(self):
# interface is added as an operator by systemconfig.puppet_plugins
self.context = self.operator.interface._create_interface_context(self.host) # pylint: disable=no-member
# Update the puppet context with generated interface context
self.operator.context.update(self.context)
def _setup_context(self):
self.ports = []
self.interfaces = []
self.addresses = []
self.routes = []
self._setup_configuration()
self._update_context()
def _setup_configuration(self):
pass
def _create_hieradata_directory(self):
hiera_path = os.path.join(os.environ['VIRTUAL_ENV'], 'hieradata')
if not os.path.exists(hiera_path):
os.mkdir(hiera_path, 0o755)
return hiera_path
def _get_config_filename(self, hiera_directory):
class_name = self.__class__.__name__
return os.path.join(hiera_directory, class_name) + ".yaml"
def _find_network_by_type(self, networktype):
for network in self.networks:
if network['type'] == networktype:
return network
def _get_network_ids_by_type(self, networktype):
if isinstance(networktype, list):
networktypelist = networktype
elif networktype:
networktypelist = [networktype]
else:
networktypelist = []
networks = []
for network_type in networktypelist:
network = self._find_network_by_type(network_type)
networks.append(str(network['id']))
return networks
def _create_ethernet_test(self, ifname=None, ifclass=None,
networktype=None, host_id=None, **kwargs):
if not host_id:
host_id = self.host.id
interface_id = len(self.interfaces)
if not ifname:
ifname = (networktype or 'eth') + str(interface_id)
if not ifclass:
ifclass = constants.INTERFACE_CLASS_NONE
if ifclass == constants.INTERFACE_CLASS_PLATFORM:
networks = self._get_network_ids_by_type(networktype)
else:
networks = []
interface = {'id': interface_id,
'uuid': str(uuid.uuid4()),
'forihostid': host_id,
'ifname': ifname,
'iftype': constants.INTERFACE_TYPE_ETHERNET,
'imac': '02:11:22:33:44:' + str(10 + interface_id),
'uses': [],
'used_by': [],
'ifclass': ifclass,
'networks': networks,
'networktype': networktype,
'imtu': 1500,
'sriov_numvfs': kwargs.get('sriov_numvfs', 0),
'sriov_vf_driver': kwargs.get('iface_sriov_vf_driver', None)}
db_interface = dbutils.create_test_interface(**interface)
for network in networks:
dbutils.create_test_interface_network_assign(db_interface['id'], network)
self.interfaces.append(db_interface)
port_id = len(self.ports)
port = {'id': port_id,
'uuid': str(uuid.uuid4()),
'name': 'eth' + str(port_id),
'interface_id': interface_id,
'host_id': host_id,
'mac': interface['imac'],
'driver': kwargs.get('driver', 'ixgbe'),
'dpdksupport': kwargs.get('dpdksupport', True),
'pdevice': kwargs.get('pdevice',
"Ethernet Controller X710 for 10GbE SFP+ [1572]"),
'pciaddr': kwargs.get('pciaddr',
'0000:00:00.' + str(port_id + 1)),
'dev_id': kwargs.get('dev_id', 0),
'sriov_vf_driver': kwargs.get('port_sriov_vf_driver', None),
'sriov_vf_pdevice_id': kwargs.get('sriov_vf_pdevice_id', None),
'sriov_vfs_pci_address': kwargs.get('sriov_vfs_pci_address', '')}
db_port = dbutils.create_test_ethernet_port(**port)
self.ports.append(db_port)
return db_port, db_interface
class NetworkingTestTestCaseControllerDualStackIPv4Primary(NetworkingTestCaseMixin,
dbbase.BaseHostTestCase):
def __init__(self, *args, **kwargs):
super(NetworkingTestTestCaseControllerDualStackIPv4Primary, self).__init__(*args, **kwargs)
self.test_interfaces = dict()
def setUp(self):
super(NetworkingTestTestCaseControllerDualStackIPv4Primary, self).setUp()
self.dbapi = db_api.get_instance()
self._setup_context()
def _update_context(self):
# ensure DB entries are updated prior to updating the context which
# will re-read the entries from the DB.
self.host.save(self.admin_context)
super(NetworkingTestTestCaseControllerDualStackIPv4Primary, self)._update_context()
def _setup_configuration(self):
# Create a single port/interface for basic function testing
print("=== _setup_configuration")
self.host = self._create_test_host(personality=constants.CONTROLLER)
_, c0_oam = self._create_ethernet_test("oam0",
constants.INTERFACE_CLASS_PLATFORM,
constants.NETWORK_TYPE_OAM, self.host.id)
_, c0_mgmt = self._create_ethernet_test("mgmt0",
constants.INTERFACE_CLASS_PLATFORM,
constants.NETWORK_TYPE_MGMT, self.host.id)
_, c0_clhost = self._create_ethernet_test("cluster0",
constants.INTERFACE_CLASS_PLATFORM,
constants.NETWORK_TYPE_CLUSTER_HOST, self.host.id)
_, c0_pxe = self._create_ethernet_test("pxe0",
constants.INTERFACE_CLASS_PLATFORM,
constants.NETWORK_TYPE_PXEBOOT, self.host.id)
self.host_c1 = self._create_test_host(personality=constants.CONTROLLER,
unit=1)
port, c1_oam = self._create_ethernet_test("oam0",
constants.INTERFACE_CLASS_PLATFORM,
constants.NETWORK_TYPE_OAM, self.host_c1.id)
port, c1_mgmt = self._create_ethernet_test("mgmt0",
constants.INTERFACE_CLASS_PLATFORM,
constants.NETWORK_TYPE_MGMT, self.host_c1.id)
port, c1_clhost = self._create_ethernet_test("cluster0",
constants.INTERFACE_CLASS_PLATFORM,
constants.NETWORK_TYPE_CLUSTER_HOST, self.host_c1.id)
port, c1_pxe = self._create_ethernet_test("pxe0",
constants.INTERFACE_CLASS_PLATFORM,
constants.NETWORK_TYPE_PXEBOOT, self.host_c1.id)
self.create_ipv6_pools()
# associate addresses with its interfaces
addresses = self.dbapi.addresses_get_all()
for addr in addresses:
for hostname in [self.host.hostname, self.host_c1.hostname]:
if addr.name == f"{hostname}-{constants.NETWORK_TYPE_OAM}":
if hostname == constants.CONTROLLER_0_HOSTNAME:
values = {'interface_id': c0_oam.id}
self.dbapi.address_update(addr.uuid, values)
elif hostname == constants.CONTROLLER_1_HOSTNAME:
values = {'interface_id': c1_oam.id}
self.dbapi.address_update(addr.uuid, values)
elif addr.name == f"{hostname}-{constants.NETWORK_TYPE_MGMT}":
if hostname == constants.CONTROLLER_0_HOSTNAME:
values = {'interface_id': c0_mgmt.id}
self.dbapi.address_update(addr.uuid, values)
elif hostname == constants.CONTROLLER_1_HOSTNAME:
values = {'interface_id': c1_mgmt.id}
self.dbapi.address_update(addr.uuid, values)
elif addr.name == f"{hostname}-{constants.NETWORK_TYPE_CLUSTER_HOST}":
if hostname == constants.CONTROLLER_0_HOSTNAME:
values = {'interface_id': c0_clhost.id}
self.dbapi.address_update(addr.uuid, values)
elif hostname == constants.CONTROLLER_1_HOSTNAME:
values = {'interface_id': c1_clhost.id}
self.dbapi.address_update(addr.uuid, values)
elif addr.name == f"{hostname}-{constants.NETWORK_TYPE_PXEBOOT}":
if hostname == constants.CONTROLLER_0_HOSTNAME:
values = {'interface_id': c0_pxe.id}
self.dbapi.address_update(addr.uuid, values)
elif hostname == constants.CONTROLLER_1_HOSTNAME:
values = {'interface_id': c1_pxe.id}
self.dbapi.address_update(addr.uuid, values)
# associate addresses with its pools
for net_type in [constants.NETWORK_TYPE_OAM,
constants.NETWORK_TYPE_MGMT,
constants.NETWORK_TYPE_CLUSTER_HOST,
constants.NETWORK_TYPE_PXEBOOT]:
net = self.dbapi.network_get_by_type(net_type)
net_pools = self.dbapi.network_addrpool_get_by_network_id(net.id)
for net_pool in net_pools:
address_pool = self.dbapi.address_pool_get(net_pool.address_pool_uuid)
addresses = self.dbapi.addresses_get_all()
for addr in addresses:
if (addr.name.endswith(f"-{net_type}")) \
and (addr.family == address_pool.family):
values = {'address_pool_id': address_pool.id}
self.dbapi.address_update(addr.uuid, values)
def create_ipv6_pools(self):
to_add = [
(constants.NETWORK_TYPE_MGMT, (netaddr.IPNetwork('fd01::/64'),
'management-ipv6')),
(constants.NETWORK_TYPE_OAM, (netaddr.IPNetwork('fd00::/64'),
'oam-ipv6')),
(constants.NETWORK_TYPE_ADMIN, (netaddr.IPNetwork('fd09::/64'),
'admin-ipv6')),
(constants.NETWORK_TYPE_CLUSTER_HOST, (netaddr.IPNetwork('fd03::/64'),
'cluster-host-ipv6')),
(constants.NETWORK_TYPE_CLUSTER_POD, (netaddr.IPNetwork('fd03::/64'),
'cluster-pod-ipv6')),
(constants.NETWORK_TYPE_CLUSTER_SERVICE, (netaddr.IPNetwork('fd04::/112'),
'cluster-service-ipv6')),
(constants.NETWORK_TYPE_STORAGE, (netaddr.IPNetwork('fd05::/64'),
'storage-ipv6'))
]
hosts = [constants.CONTROLLER_HOSTNAME,
constants.CONTROLLER_0_HOSTNAME,
constants.CONTROLLER_1_HOSTNAME]
for cfgdata in to_add:
net = self.dbapi.network_get_by_type(cfgdata[0])
pool = self._create_test_address_pool(name=cfgdata[1][1],
subnet=cfgdata[1][0])
network_addrpool = dbutils.create_test_network_addrpool(address_pool_id=pool.id,
network_id=net.id)
self._create_test_addresses(hostnames=hosts, subnet=cfgdata[1][0],
network_type=cfgdata[0], start=2)
if cfgdata[0] in [constants.NETWORK_TYPE_MGMT, constants.NETWORK_TYPE_OAM]:
self._create_test_addresses(hostnames=[constants.CONTROLLER_GATEWAY],
subnet=cfgdata[1][0],
network_type=cfgdata[0], start=1, stop=2)
self.network_addrpools.append(network_addrpool)
def test_generate_networking_host_config(self):
hieradata_directory = self._create_hieradata_directory()
config_filename = self._get_config_filename(hieradata_directory)
with open(config_filename, 'w') as config_file:
config = self.operator.networking.get_host_config(self.host) # pylint: disable=no-member
yaml.dump(config, config_file, default_flow_style=False)
print(config_filename)
hiera_data = dict()
with open(config_filename, 'r') as config_file:
hiera_data = yaml.safe_load(config_file)
for family in ['ipv4', 'ipv6']:
for net_type in [constants.NETWORK_TYPE_MGMT, constants.NETWORK_TYPE_CLUSTER_HOST,
constants.NETWORK_TYPE_PXEBOOT, constants.NETWORK_TYPE_OAM]:
type = net_type.replace('-', '_')
for field in ["interface_address"]:
test_key = f'platform::network::{type}::{family}::params::interface_address'
if net_type == constants.NETWORK_TYPE_PXEBOOT and family == 'ipv6':
# there are no ipv6 allocations for pxe
self.assertNotIn(test_key, hiera_data.keys())
else:
self.assertIn(test_key, hiera_data.keys())
for net_type in [constants.NETWORK_TYPE_MGMT, constants.NETWORK_TYPE_CLUSTER_HOST,
constants.NETWORK_TYPE_PXEBOOT, constants.NETWORK_TYPE_OAM]:
for field in ["interface_address", "interface_devices", "interface_name", "mtu"]:
test_key = f'platform::network::{type}::params::{field}'
self.assertIn(test_key, hiera_data.keys())
# ipv4 is the primary, chack the addresses match
for net_type in [constants.NETWORK_TYPE_MGMT, constants.NETWORK_TYPE_CLUSTER_HOST,
constants.NETWORK_TYPE_PXEBOOT, constants.NETWORK_TYPE_OAM]:
self.assertEqual(hiera_data[f'platform::network::{type}::params::interface_address'],
hiera_data[f'platform::network::{type}::ipv4::params::interface_address'])
def test_generate_networking_system_config(self):
hieradata_directory = self._create_hieradata_directory()
config_filename = self._get_config_filename(hieradata_directory)
with open(config_filename, 'w') as config_file:
config = self.operator.networking.get_system_config() # pylint: disable=no-member
yaml.dump(config, config_file, default_flow_style=False)
print(config_filename)
hiera_data = dict()
with open(config_filename, 'r') as config_file:
hiera_data = yaml.safe_load(config_file)
for family in ['ipv4', 'ipv6']:
for net_type in [constants.NETWORK_TYPE_MGMT, constants.NETWORK_TYPE_ADMIN,
constants.NETWORK_TYPE_CLUSTER_HOST, constants.NETWORK_TYPE_PXEBOOT,
constants.NETWORK_TYPE_STORAGE, constants.NETWORK_TYPE_OAM]:
type = net_type.replace('-', '_')
for field in ["controller0_address", "controller1_address", "controller_address",
"controller_address_url", "subnet_end", "subnet_netmask", "subnet_network",
"subnet_network_url", "subnet_prefixlen", "subnet_start", "subnet_version"]:
test_key = f'platform::network::{type}::{family}::params::{field}'
if net_type == constants.NETWORK_TYPE_PXEBOOT and family == 'ipv6':
# there are no ipv6 allocations for pxe
self.assertNotIn(test_key, hiera_data.keys())
else:
self.assertIn(test_key, hiera_data.keys())
# check the primary pool (no family indication) presence
for net_type in [constants.NETWORK_TYPE_MGMT, constants.NETWORK_TYPE_ADMIN,
constants.NETWORK_TYPE_CLUSTER_HOST, constants.NETWORK_TYPE_PXEBOOT,
constants.NETWORK_TYPE_STORAGE, constants.NETWORK_TYPE_OAM]:
for field in ["controller0_address", "controller1_address", "controller_address",
"controller_address_url", "subnet_end", "subnet_netmask", "subnet_network",
"subnet_network_url", "subnet_prefixlen", "subnet_start", "subnet_version"]:
test_key = f'platform::network::{type}::params::{field}'
self.assertIn(test_key, hiera_data.keys())
# check if the the primary pool subnet_version is with the correct value
for net_type in [constants.NETWORK_TYPE_MGMT, constants.NETWORK_TYPE_ADMIN,
constants.NETWORK_TYPE_CLUSTER_HOST, constants.NETWORK_TYPE_PXEBOOT,
constants.NETWORK_TYPE_STORAGE, constants.NETWORK_TYPE_OAM]:
for field in ["subnet_version"]:
test_key = f'platform::network::{type}::params::{field}'
self.assertEqual(constants.IPV4_FAMILY, hiera_data[test_key])
class NetworkingTestTestCaseControllerDualStackIPv6Primary(NetworkingTestCaseMixin,
dbbase.BaseIPv6Mixin,
dbbase.BaseHostTestCase):
def __init__(self, *args, **kwargs):
super(NetworkingTestTestCaseControllerDualStackIPv6Primary, self).__init__(*args, **kwargs)
self.test_interfaces = dict()
def setUp(self):
super(NetworkingTestTestCaseControllerDualStackIPv6Primary, self).setUp()
self.dbapi = db_api.get_instance()
self._setup_context()
def _update_context(self):
# ensure DB entries are updated prior to updating the context which
# will re-read the entries from the DB.
self.host.save(self.admin_context)
super(NetworkingTestTestCaseControllerDualStackIPv6Primary, self)._update_context()
def create_ipv4_pools(self):
to_add = [
(constants.NETWORK_TYPE_MGMT, (netaddr.IPNetwork('192.168.204.0/24'),
'management-ipv4')),
(constants.NETWORK_TYPE_OAM, (netaddr.IPNetwork('10.10.10.0/24'),
'oam-ipv4')),
(constants.NETWORK_TYPE_ADMIN, (netaddr.IPNetwork('10.10.30.0/24'),
'admin-ipv4')),
(constants.NETWORK_TYPE_CLUSTER_HOST, (netaddr.IPNetwork('192.168.206.0/24'),
'cluster-host-ipv4')),
(constants.NETWORK_TYPE_CLUSTER_POD, (netaddr.IPNetwork('172.16.0.0/16'),
'cluster-pod-ipv4')),
(constants.NETWORK_TYPE_CLUSTER_SERVICE, (netaddr.IPNetwork('10.96.0.0/12'),
'cluster-service-ipv4')),
(constants.NETWORK_TYPE_STORAGE, (netaddr.IPNetwork('10.10.20.0/24'),
'storage-ipv4'))
]
hosts = [constants.CONTROLLER_HOSTNAME,
constants.CONTROLLER_0_HOSTNAME,
constants.CONTROLLER_1_HOSTNAME]
for cfgdata in to_add:
net = self.dbapi.network_get_by_type(cfgdata[0])
pool = self._create_test_address_pool(name=cfgdata[1][1],
subnet=cfgdata[1][0])
network_addrpool = dbutils.create_test_network_addrpool(address_pool_id=pool.id,
network_id=net.id)
self._create_test_addresses(hostnames=hosts, subnet=cfgdata[1][0],
network_type=cfgdata[0], start=2)
if cfgdata[0] in [constants.NETWORK_TYPE_MGMT, constants.NETWORK_TYPE_OAM]:
self._create_test_addresses(hostnames=[constants.CONTROLLER_GATEWAY],
subnet=cfgdata[1][0],
network_type=cfgdata[0], start=1, stop=2)
self.network_addrpools.append(network_addrpool)
def _setup_configuration(self):
self.host = self._create_test_host(personality=constants.CONTROLLER)
_, c0_oam = self._create_ethernet_test("oam0",
constants.INTERFACE_CLASS_PLATFORM,
constants.NETWORK_TYPE_OAM, self.host.id)
_, c0_mgmt = self._create_ethernet_test("mgmt0",
constants.INTERFACE_CLASS_PLATFORM,
constants.NETWORK_TYPE_MGMT, self.host.id)
_, c0_clhost = self._create_ethernet_test("cluster0",
constants.INTERFACE_CLASS_PLATFORM,
constants.NETWORK_TYPE_CLUSTER_HOST, self.host.id)
_, c0_pxe = self._create_ethernet_test("pxe0",
constants.INTERFACE_CLASS_PLATFORM,
constants.NETWORK_TYPE_PXEBOOT, self.host.id)
self.host_c1 = self._create_test_host(personality=constants.CONTROLLER,
unit=1)
_, c1_oam = self._create_ethernet_test("oam0",
constants.INTERFACE_CLASS_PLATFORM,
constants.NETWORK_TYPE_OAM, self.host_c1.id)
_, c1_mgmt = self._create_ethernet_test("mgmt0",
constants.INTERFACE_CLASS_PLATFORM,
constants.NETWORK_TYPE_MGMT, self.host_c1.id)
_, c1_clhost = self._create_ethernet_test("cluster0",
constants.INTERFACE_CLASS_PLATFORM,
constants.NETWORK_TYPE_CLUSTER_HOST, self.host_c1.id)
_, c1_pxe = self._create_ethernet_test("pxe0",
constants.INTERFACE_CLASS_PLATFORM,
constants.NETWORK_TYPE_PXEBOOT, self.host_c1.id)
self.create_ipv4_pools()
# associate addresses with its interfaces
addresses = self.dbapi.addresses_get_all()
for addr in addresses:
for hostname in [self.host.hostname, self.host_c1.hostname]:
if addr.name == f"{hostname}-{constants.NETWORK_TYPE_OAM}":
if hostname == constants.CONTROLLER_0_HOSTNAME:
values = {'interface_id': c0_oam.id}
self.dbapi.address_update(addr.uuid, values)
elif hostname == constants.CONTROLLER_1_HOSTNAME:
values = {'interface_id': c1_oam.id}
self.dbapi.address_update(addr.uuid, values)
elif addr.name == f"{hostname}-{constants.NETWORK_TYPE_MGMT}":
if hostname == constants.CONTROLLER_0_HOSTNAME:
values = {'interface_id': c0_mgmt.id}
self.dbapi.address_update(addr.uuid, values)
elif hostname == constants.CONTROLLER_1_HOSTNAME:
values = {'interface_id': c1_mgmt.id}
self.dbapi.address_update(addr.uuid, values)
elif addr.name == f"{hostname}-{constants.NETWORK_TYPE_CLUSTER_HOST}":
if hostname == constants.CONTROLLER_0_HOSTNAME:
values = {'interface_id': c0_clhost.id}
self.dbapi.address_update(addr.uuid, values)
elif hostname == constants.CONTROLLER_1_HOSTNAME:
values = {'interface_id': c1_clhost.id}
self.dbapi.address_update(addr.uuid, values)
elif addr.name == f"{hostname}-{constants.NETWORK_TYPE_PXEBOOT}":
if hostname == constants.CONTROLLER_0_HOSTNAME:
values = {'interface_id': c0_pxe.id}
self.dbapi.address_update(addr.uuid, values)
elif hostname == constants.CONTROLLER_1_HOSTNAME:
values = {'interface_id': c1_pxe.id}
self.dbapi.address_update(addr.uuid, values)
# associate addresses with its pools
for net_type in [constants.NETWORK_TYPE_OAM,
constants.NETWORK_TYPE_MGMT,
constants.NETWORK_TYPE_CLUSTER_HOST,
constants.NETWORK_TYPE_PXEBOOT]:
net = self.dbapi.network_get_by_type(net_type)
net_pools = self.dbapi.network_addrpool_get_by_network_id(net.id)
for net_pool in net_pools:
address_pool = self.dbapi.address_pool_get(net_pool.address_pool_uuid)
addresses = self.dbapi.addresses_get_all()
for addr in addresses:
if (addr.name.endswith(f"-{net_type}")) \
and (addr.family == address_pool.family):
values = {'address_pool_id': address_pool.id}
self.dbapi.address_update(addr.uuid, values)
def test_generate_networking_system_config(self):
hieradata_directory = self._create_hieradata_directory()
config_filename = self._get_config_filename(hieradata_directory)
with open(config_filename, 'w') as config_file:
config = self.operator.networking.get_system_config() # pylint: disable=no-member
yaml.dump(config, config_file, default_flow_style=False)
print(config_filename)
hiera_data = dict()
with open(config_filename, 'r') as config_file:
hiera_data = yaml.safe_load(config_file)
for family in ['ipv4', 'ipv6']:
for net_type in [constants.NETWORK_TYPE_MGMT, constants.NETWORK_TYPE_ADMIN,
constants.NETWORK_TYPE_CLUSTER_HOST, constants.NETWORK_TYPE_PXEBOOT,
constants.NETWORK_TYPE_STORAGE, constants.NETWORK_TYPE_OAM]:
type = net_type.replace('-', '_')
for field in ["controller0_address", "controller1_address", "controller_address",
"controller_address_url", "subnet_end", "subnet_netmask", "subnet_network",
"subnet_network_url", "subnet_prefixlen", "subnet_start", "subnet_version"]:
test_key = f'platform::network::{type}::{family}::params::{field}'
if net_type == constants.NETWORK_TYPE_PXEBOOT and family == 'ipv6':
# there are no ipv6 allocations for pxe
self.assertNotIn(test_key, hiera_data.keys())
else:
self.assertIn(test_key, hiera_data.keys())
# check the primary pool (no family indication) presence
for net_type in [constants.NETWORK_TYPE_MGMT, constants.NETWORK_TYPE_ADMIN,
constants.NETWORK_TYPE_CLUSTER_HOST, constants.NETWORK_TYPE_PXEBOOT,
constants.NETWORK_TYPE_STORAGE, constants.NETWORK_TYPE_OAM]:
for field in ["controller0_address", "controller1_address", "controller_address",
"controller_address_url", "subnet_end", "subnet_netmask", "subnet_network",
"subnet_network_url", "subnet_prefixlen", "subnet_start", "subnet_version"]:
test_key = f'platform::network::{type}::params::{field}'
self.assertIn(test_key, hiera_data.keys())
# check if the the primary pool subnet_version is with the correct value
for net_type in [constants.NETWORK_TYPE_MGMT, constants.NETWORK_TYPE_ADMIN,
constants.NETWORK_TYPE_CLUSTER_HOST, constants.NETWORK_TYPE_PXEBOOT,
constants.NETWORK_TYPE_STORAGE, constants.NETWORK_TYPE_OAM]:
for field in ["subnet_version"]:
test_key = f'platform::network::{type}::params::{field}'
self.assertEqual(constants.IPV6_FAMILY, hiera_data[test_key])
def test_generate_networking_system_config_no_net_pool_object(self):
"""This test aims to validate if a system can operate without network-addrpool
objects since this can happen if an upgrade is executed and the data-migration
for the diual-stack feature is not implemented yet;
"""
net_pools = self.dbapi.network_addrpool_get_all()
for net_pool in net_pools:
self.dbapi.network_addrpool_destroy(net_pool.uuid)
hieradata_directory = self._create_hieradata_directory()
config_filename = self._get_config_filename(hieradata_directory)
with open(config_filename, 'w') as config_file:
config = self.operator.networking.get_system_config() # pylint: disable=no-member
yaml.dump(config, config_file, default_flow_style=False)
print(config_filename)
hiera_data = dict()
with open(config_filename, 'r') as config_file:
hiera_data = yaml.safe_load(config_file)
for family in ['ipv6']:
for net_type in [constants.NETWORK_TYPE_MGMT, constants.NETWORK_TYPE_ADMIN,
constants.NETWORK_TYPE_CLUSTER_HOST, constants.NETWORK_TYPE_PXEBOOT,
constants.NETWORK_TYPE_STORAGE, constants.NETWORK_TYPE_OAM]:
type = net_type.replace('-', '_')
for field in ["controller0_address", "controller1_address", "controller_address",
"controller_address_url", "subnet_end", "subnet_netmask", "subnet_network",
"subnet_network_url", "subnet_prefixlen", "subnet_start", "subnet_version"]:
test_key = f'platform::network::{type}::{family}::params::{field}'
if net_type == constants.NETWORK_TYPE_PXEBOOT and family == 'ipv6':
# there are no ipv6 allocations for pxe
self.assertNotIn(test_key, hiera_data.keys())
else:
self.assertIn(test_key, hiera_data.keys())
# check the primary pool (no family indication) presence
for net_type in [constants.NETWORK_TYPE_MGMT, constants.NETWORK_TYPE_ADMIN,
constants.NETWORK_TYPE_CLUSTER_HOST, constants.NETWORK_TYPE_PXEBOOT,
constants.NETWORK_TYPE_STORAGE, constants.NETWORK_TYPE_OAM]:
for field in ["controller0_address", "controller1_address", "controller_address",
"controller_address_url", "subnet_end", "subnet_netmask", "subnet_network",
"subnet_network_url", "subnet_prefixlen", "subnet_start", "subnet_version"]:
test_key = f'platform::network::{type}::params::{field}'
self.assertIn(test_key, hiera_data.keys())
# check if the the primary pool subnet_version is with the correct value
for net_type in [constants.NETWORK_TYPE_MGMT, constants.NETWORK_TYPE_ADMIN,
constants.NETWORK_TYPE_CLUSTER_HOST, constants.NETWORK_TYPE_PXEBOOT,
constants.NETWORK_TYPE_STORAGE, constants.NETWORK_TYPE_OAM]:
for field in ["subnet_version"]:
test_key = f'platform::network::{type}::params::{field}'
self.assertEqual(constants.IPV6_FAMILY, hiera_data[test_key])
def test_generate_networking_host_config(self):
hieradata_directory = self._create_hieradata_directory()
config_filename = self._get_config_filename(hieradata_directory)
with open(config_filename, 'w') as config_file:
config = self.operator.networking.get_host_config(self.host) # pylint: disable=no-member
yaml.dump(config, config_file, default_flow_style=False)
print(config_filename)
hiera_data = dict()
with open(config_filename, 'r') as config_file:
hiera_data = yaml.safe_load(config_file)
for family in ['ipv4', 'ipv6']:
for net_type in [constants.NETWORK_TYPE_MGMT, constants.NETWORK_TYPE_CLUSTER_HOST,
constants.NETWORK_TYPE_PXEBOOT, constants.NETWORK_TYPE_OAM]:
type = net_type.replace('-', '_')
for field in ["interface_address"]:
test_key = f'platform::network::{type}::{family}::params::interface_address'
if net_type == constants.NETWORK_TYPE_PXEBOOT and family == 'ipv6':
# there are no ipv6 allocations for pxe
self.assertNotIn(test_key, hiera_data.keys())
else:
self.assertIn(test_key, hiera_data.keys())
for net_type in [constants.NETWORK_TYPE_MGMT, constants.NETWORK_TYPE_CLUSTER_HOST,
constants.NETWORK_TYPE_PXEBOOT, constants.NETWORK_TYPE_OAM]:
for field in ["interface_address", "interface_devices", "interface_name", "mtu"]:
test_key = f'platform::network::{type}::params::{field}'
self.assertIn(test_key, hiera_data.keys())
# ipv6 is the primary, check the addresses match
for net_type in [constants.NETWORK_TYPE_MGMT, constants.NETWORK_TYPE_CLUSTER_HOST,
constants.NETWORK_TYPE_PXEBOOT, constants.NETWORK_TYPE_OAM]:
self.assertEqual(hiera_data[f'platform::network::{type}::params::interface_address'],
hiera_data[f'platform::network::{type}::ipv6::params::interface_address'])
def test_generate_networking_host_config_no_net_pool_objects(self):
"""This test aims to validate if a system can operate without network-addrpool
objects since this can happen if an upgrade is executed and the data-migration
for the diual-stack feature is not implemented yet;
"""
net_pools = self.dbapi.network_addrpool_get_all()
for net_pool in net_pools:
self.dbapi.network_addrpool_destroy(net_pool.uuid)
hieradata_directory = self._create_hieradata_directory()
config_filename = self._get_config_filename(hieradata_directory)
with open(config_filename, 'w') as config_file:
config = self.operator.networking.get_host_config(self.host) # pylint: disable=no-member
yaml.dump(config, config_file, default_flow_style=False)
print(config_filename)
hiera_data = dict()
with open(config_filename, 'r') as config_file:
hiera_data = yaml.safe_load(config_file)
for family in ['ipv4', 'ipv6']:
for net_type in [constants.NETWORK_TYPE_MGMT, constants.NETWORK_TYPE_CLUSTER_HOST,
constants.NETWORK_TYPE_PXEBOOT, constants.NETWORK_TYPE_OAM]:
type = net_type.replace('-', '_')
for field in ["interface_address"]:
test_key = f'platform::network::{type}::{family}::params::interface_address'
if net_type == constants.NETWORK_TYPE_PXEBOOT and family == 'ipv6':
# there are no ipv6 allocations for pxe
self.assertNotIn(test_key, hiera_data.keys())
else:
self.assertIn(test_key, hiera_data.keys())
for net_type in [constants.NETWORK_TYPE_MGMT, constants.NETWORK_TYPE_CLUSTER_HOST,
constants.NETWORK_TYPE_PXEBOOT, constants.NETWORK_TYPE_OAM]:
for field in ["interface_address", "interface_devices", "interface_name", "mtu"]:
test_key = f'platform::network::{type}::params::{field}'
self.assertIn(test_key, hiera_data.keys())
# ipv6 is the primary, check the addresses match
for net_type in [constants.NETWORK_TYPE_MGMT, constants.NETWORK_TYPE_CLUSTER_HOST,
constants.NETWORK_TYPE_PXEBOOT, constants.NETWORK_TYPE_OAM]:
self.assertEqual(hiera_data[f'platform::network::{type}::params::interface_address'],
hiera_data[f'platform::network::{type}::ipv6::params::interface_address'])