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)
return address.address
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
def _using_static_ip(self, ihost, personality=None, hostname=None):
@ -1378,7 +1392,7 @@ class ConductorManager(service.PeriodicService):
)
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:
# Write entry for pxecontroller into addn_hosts file
@ -2054,7 +2068,19 @@ class ConductorManager(service.PeriodicService):
pass
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:
return
address_name = cutils.format_address_name(hostname, iface_type)
@ -2062,34 +2088,41 @@ class ConductorManager(service.PeriodicService):
try:
address = self.dbapi.address_get_by_address(ip_address)
address_uuid = address['uuid']
search_addr = self.dbapi.address_get_by_name_and_family(address_name,
address_family)
# 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.uuid == address_uuid and iface_id is None):
LOG.info(f"returning, address '{address_uuid}' exists and iface_id is None")
return
except exception.AddressNotFoundByAddress:
address_uuid = None
except exception.AddressNotFoundByName:
except exception.AddressNotFoundByNameAndFamily:
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:
values['interface_id'] = iface_id
if address_uuid:
address = self.dbapi.address_update(address_uuid, values)
address_pool = None
if pool_uuid:
address_pool = self.dbapi.address_pool_get(pool_uuid)
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()
return address
@ -2110,19 +2143,32 @@ class ConductorManager(service.PeriodicService):
# controller must have cluster-host address already allocated
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(
host.hostname, constants.NETWORK_TYPE_CLUSTER_HOST)
hostname = host.hostname
if cluster_host_address is None:
address_name = cutils.format_address_name(
host.hostname, constants.NETWORK_TYPE_CLUSTER_HOST)
LOG.info("{} address not found. Allocating address for {}.".format(
address_name, host.hostname))
host_network = self.dbapi.network_get_by_type(
constants.NETWORK_TYPE_CLUSTER_HOST)
self._allocate_pool_address(None, host_network.pool_uuid,
address_name)
for pool_uuid in pool_uuid_list:
pool = self.dbapi.address_pool_get(pool_uuid)
cluster_host_address = self._lookup_static_ip_address_family(
host.hostname, constants.NETWORK_TYPE_CLUSTER_HOST, pool.family)
if cluster_host_address is None:
address_name = cutils.format_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):
"""Allocates addresses for a given host.
@ -2142,28 +2188,40 @@ class ConductorManager(service.PeriodicService):
mgmt_interface_id = None
if mgmt_interfaces:
mgmt_interface_id = mgmt_interfaces[0]['id']
hostname = host.hostname
# check for static mgmt IP
mgmt_ip = self._lookup_static_ip_address(
hostname, constants.NETWORK_TYPE_MGMT
)
# 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)
# if no static address, then allocate one
if not mgmt_ip:
mgmt_pool = self.dbapi.network_get_by_type(
constants.NETWORK_TYPE_MGMT
).pool_uuid
address_name = cutils.format_address_name(hostname,
constants.NETWORK_TYPE_MGMT)
mgmt_ip = self._allocate_pool_address(mgmt_interface_id, mgmt_pool,
address_name).address
LOG.info("Allocated mgmt ip {} for host{}".format(mgmt_ip, hostname))
hostname = host.hostname
mgmt_net = self.dbapi.network_get_by_type(constants.NETWORK_TYPE_MGMT)
net_pools = self.dbapi.network_addrpool_get_by_network_id(mgmt_net.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 {mgmt_net.name} does not have network to address pool objects")
pool_uuid_list.append(mgmt_net.pool_uuid)
for pool_uuid in pool_uuid_list:
pool = self.dbapi.address_pool_get(pool_uuid)
# check for static mgmt IP
mgmt_ip = self._lookup_static_ip_address_family(
hostname, constants.NETWORK_TYPE_MGMT, pool.family)
# 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._allocate_cluster_host_address_for_host(host)
@ -2930,6 +2988,7 @@ class ConductorManager(service.PeriodicService):
puppet_common.puppet_apply_manifest(host.hostname,
constants.WORKER,
do_reboot=True)
return host
def unconfigure_ihost(self, context, ihost_obj):
@ -3616,23 +3675,29 @@ class ConductorManager(service.PeriodicService):
if set_address_interface:
if new_interface and 'id' in new_interface:
values = {'interface_id': new_interface['id']}
address = cutils.get_primary_address_by_name(self.dbapi,
cutils.format_address_name(ihost.hostname, new_interface_networktype),
new_interface_networktype)
if address:
self.dbapi.address_update(address['uuid'], values)
try:
addr_name = cutils.format_address_name(
ihost.hostname, new_interface_networktype)
addresses = self.dbapi.address_get_by_name(addr_name)
for address in addresses:
self.dbapi.address_update(address['uuid'], values)
except exception.AddressNotFoundByName:
pass
# Do any potential distributed cloud config
# We do this here where the interface is created.
cutils.perform_distributed_cloud_config(self.dbapi,
new_interface['id'])
if port:
values = {'interface_id': port.interface_id}
address = cutils.get_primary_address_by_name(self.dbapi,
cutils.format_address_name(ihost.hostname, networktype),
networktype)
if address:
if address['interface_id'] is None:
self.dbapi.address_update(address['uuid'], values)
try:
addr_name = cutils.format_address_name(ihost.hostname,
networktype)
addresses = self.dbapi.address_get_by_name(addr_name)
for address in addresses:
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]:
LOG.info("Updating %s host invprovision from %s to %s" %
@ -12359,7 +12424,7 @@ class ConductorManager(service.PeriodicService):
if not drbd_fs_updated:
rc = True
else:
while(loop_timeout <= max_loop):
while (loop_timeout <= max_loop):
if constants.DRBD_PGSQL in (drbd_fs_updated - drbd_fs_resized):
if (not standby_host or (standby_host and
constants.DRBD_PGSQL in self._drbd_fs_sync())):

View File

@ -5154,3 +5154,11 @@ class Connection(object):
@abc.abstractmethod
def kube_app_bundle_destroy_by_file_path(self, 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
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):
address = self._get_address_by_name(
constants.CONTROLLER_HOSTNAME, constants.NETWORK_TYPE_MGMT)

View File

@ -6,6 +6,8 @@
import netaddr
from oslo_log import log
from sysinv.common import constants
from sysinv.common import exception
from sysinv.common import utils
@ -13,6 +15,11 @@ from sysinv.common import utils
from sysinv.puppet import base
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 to encapsulate puppet operations for networking configuration"""
@ -49,16 +56,7 @@ class NetworkingPuppet(base.BasePuppet):
config = self._get_network_config(networktype)
try:
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,
})
config = self._get_network_gateway_config(networktype, config)
# create flag for the mate controller to use FQDN or not
if utils.is_fqdn_ready_to_use(True):
@ -100,16 +98,7 @@ class NetworkingPuppet(base.BasePuppet):
config = self._get_network_config(networktype)
try:
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,
})
config = self._get_network_gateway_config(networktype, config)
return config
@ -132,71 +121,125 @@ class NetworkingPuppet(base.BasePuppet):
try:
network = self.dbapi.network_get_by_type(networktype)
except exception.NetworkTypeNotFound:
# network not configured
LOG.debug(f"Network type {networktype} not found")
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(
str(address_pool.network) + '/' + str(address_pool.prefix))
configdata = dict()
config = dict()
subnet_version = address_pool.family
subnet_network = str(subnet.network)
subnet_netmask = str(subnet.netmask)
subnet_prefixlen = subnet.prefixlen
for pool_uuid in pool_uuid_list:
subnet_start = str(address_pool.ranges[0][0])
subnet_end = str(address_pool.ranges[0][-1])
address_pool = self.dbapi.address_pool_get(pool_uuid)
try:
controller_address = self._get_address_by_name(
constants.CONTROLLER_HOSTNAME, networktype).address
except exception.AddressNotFoundByName:
controller_address = None
family_name = IPv4 if address_pool.family == constants.IPV4_FAMILY else IPv6
configdata.update({family_name: {}})
try:
controller0_address = self._get_address_by_name(
constants.CONTROLLER_0_HOSTNAME, networktype).address
except exception.AddressNotFoundByName:
controller0_address = None
subnet = netaddr.IPNetwork(
str(address_pool.network) + '/' + str(address_pool.prefix))
configdata[family_name].update({'subnet': subnet})
try:
controller1_address = self._get_address_by_name(
constants.CONTROLLER_1_HOSTNAME, networktype).address
except exception.AddressNotFoundByName:
controller1_address = None
configdata[family_name].update({'subnet_version': address_pool.family})
configdata[family_name].update({'subnet_network': str(subnet.network)})
configdata[family_name].update({'subnet_netmask': str(subnet.netmask)})
configdata[family_name].update({'subnet_prefixlen': subnet.prefixlen})
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)
subnet_network_url = self._format_url_address(subnet_network)
try:
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
# dashes
networktype = networktype.replace('-', '_')
return {
'platform::network::%s::params::subnet_version' % networktype:
subnet_version,
'platform::network::%s::params::subnet_network' % networktype:
subnet_network,
'platform::network::%s::params::subnet_network_url' % networktype:
subnet_network_url,
'platform::network::%s::params::subnet_prefixlen' % networktype:
subnet_prefixlen,
'platform::network::%s::params::subnet_netmask' % networktype:
subnet_netmask,
'platform::network::%s::params::subnet_start' % networktype:
subnet_start,
'platform::network::%s::params::subnet_end' % networktype:
subnet_end,
'platform::network::%s::params::controller_address' % networktype:
controller_address,
'platform::network::%s::params::controller_address_url' % networktype:
controller_address_url,
'platform::network::%s::params::controller0_address' % networktype:
controller0_address,
'platform::network::%s::params::controller1_address' % networktype:
controller1_address,
}
for family in configdata:
config[f'platform::network::{networktype}::{family}::params::subnet_version'] = \
configdata[family]['subnet_version']
config[f'platform::network::{networktype}::{family}::params::subnet_network'] = \
configdata[family]['subnet_network']
config[f'platform::network::{networktype}::{family}::params::subnet_network_url'] = \
configdata[family]['subnet_network_url']
config[f'platform::network::{networktype}::{family}::params::subnet_prefixlen'] = \
configdata[family]['subnet_prefixlen']
config[f'platform::network::{networktype}::{family}::params::subnet_netmask'] = \
configdata[family]['subnet_netmask']
config[f'platform::network::{networktype}::{family}::params::subnet_start'] = \
configdata[family]['subnet_start']
config[f'platform::network::{networktype}::{family}::params::subnet_end'] = \
configdata[family]['subnet_end']
config[f'platform::network::{networktype}::{family}::params::controller_address'] = \
configdata[family]['controller_address']
config[f'platform::network::{networktype}::{family}::params::controller_address_url'] = \
configdata[family]['controller_address_url']
config[f'platform::network::{networktype}::{family}::params::controller0_address'] = \
configdata[family]['controller0_address']
config[f'platform::network::{networktype}::{family}::params::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):
return self._get_interface_config(constants.NETWORK_TYPE_PXEBOOT)
@ -219,6 +262,53 @@ class NetworkingPuppet(base.BasePuppet):
def _get_admin_interface_config(self):
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):
default_global_parameters = {
@ -546,10 +636,9 @@ class NetworkingPuppet(base.BasePuppet):
self.context, network_interface)
interface_devices = interface.get_interface_devices(
self.context, network_interface)
network_id = interface.find_network_id_by_networktype(
self.context, networktype)
# Convert the dash to underscore because puppet parameters cannot
# have dashes
network = self.context['networks'].get(networktype, None)
networktype = networktype.replace('-', '_')
config.update({
'platform::network::%s::params::interface_name' % networktype:
@ -560,13 +649,23 @@ class NetworkingPuppet(base.BasePuppet):
network_interface.imtu
})
interface_address = interface.get_interface_primary_address(
self.context, network_interface, network_id)
if interface_address:
addresses = self.context['addresses'].get(network_interface['ifname'], [])
for address in addresses:
family = "ipv4" if address.family == constants.IPV4_FAMILY else "ipv6"
config.update({
'platform::network::%s::params::interface_address' %
networktype:
interface_address['address']
f'platform::network::{networktype}::{family}::params::interface_address':
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

View File

@ -25,6 +25,7 @@
import copy
import mock
import os.path
import netaddr
import subprocess
import tempfile
import uuid
@ -5924,6 +5925,34 @@ class ManagerTestCaseInternal(base.BaseHostTestCase):
self.service = manager.ConductorManager('test-host', 'test-topic')
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):
# create test interface
ihost = self._create_test_host(
@ -5969,3 +5998,138 @@ class ManagerTestCaseInternal(base.BaseHostTestCase):
self.service._remove_lease_for_address(ihost.hostname,
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'])