1848 lines
69 KiB
Python
1848 lines
69 KiB
Python
#
|
|
# Copyright (c) 2017-2024 Wind River Systems, Inc.
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
#
|
|
|
|
import collections
|
|
import copy
|
|
import re
|
|
import six
|
|
|
|
from netaddr import IPAddress
|
|
from netaddr import IPNetwork
|
|
|
|
from oslo_log import log
|
|
from sysinv._i18n import _
|
|
from sysinv.common import constants
|
|
from sysinv.common import device as dconstants
|
|
from sysinv.common import exception
|
|
from sysinv.common import interface
|
|
from sysinv.common import utils
|
|
from sysinv.conductor import openstack
|
|
from sysinv.puppet import base
|
|
from sysinv.puppet import quoted_str
|
|
|
|
|
|
LOG = log.getLogger(__name__)
|
|
|
|
PLATFORM_NETWORK_TYPES = [constants.NETWORK_TYPE_PXEBOOT,
|
|
constants.NETWORK_TYPE_MGMT,
|
|
constants.NETWORK_TYPE_CLUSTER_HOST,
|
|
constants.NETWORK_TYPE_OAM,
|
|
constants.NETWORK_TYPE_IRONIC,
|
|
constants.NETWORK_TYPE_STORAGE,
|
|
constants.NETWORK_TYPE_ADMIN]
|
|
|
|
DATA_NETWORK_TYPES = [constants.NETWORK_TYPE_DATA]
|
|
|
|
DATA_INTERFACE_CLASSES = [constants.INTERFACE_CLASS_DATA]
|
|
|
|
PCI_NETWORK_TYPES = [constants.NETWORK_TYPE_PCI_SRIOV,
|
|
constants.NETWORK_TYPE_PCI_PASSTHROUGH]
|
|
|
|
PCI_INTERFACE_CLASSES = [constants.INTERFACE_CLASS_PCI_PASSTHROUGH,
|
|
constants.INTERFACE_CLASS_PCI_SRIOV]
|
|
|
|
ACTIVE_STANDBY_AE_MODES = ['active_backup', 'active-backup', 'active_standby']
|
|
BALANCED_AE_MODES = ['balanced', 'balanced-xor']
|
|
LACP_AE_MODES = ['802.3ad']
|
|
|
|
LOOPBACK_IFNAME = 'lo'
|
|
LOOPBACK_METHOD = 'loopback'
|
|
STATIC_METHOD = 'static'
|
|
MANUAL_METHOD = 'manual'
|
|
DHCP_METHOD = 'dhcp'
|
|
|
|
NETWORK_CONFIG_RESOURCE = 'platform::network::interfaces::network_config'
|
|
SRIOV_CONFIG_RESOURCE = 'platform::network::interfaces::sriov::sriov_config'
|
|
FPGA_CONFIG_RESOURCE = 'platform::network::interfaces::fpga::fpga_config'
|
|
ADDRESS_CONFIG_RESOURCE = 'platform::network::addresses::address_config'
|
|
ROUTE_CONFIG_RESOURCE = 'platform::network::routes::route_config'
|
|
|
|
DATA_IFACE_LIST_RESOURCE = 'platform::lmon::params::data_iface_devices'
|
|
|
|
IFACE_UP_OP = 1
|
|
IFACE_PRE_UP_OP = 2
|
|
IFACE_POST_UP_OP = 3
|
|
IFACE_DOWN_OP = 4
|
|
IFACE_PRE_DOWN_OP = 5
|
|
IFACE_POST_DOWN_OP = 6
|
|
|
|
|
|
class InterfacePuppet(base.BasePuppet):
|
|
"""Class to encapsulate puppet operations for interface configuration"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(InterfacePuppet, self).__init__(*args, **kwargs)
|
|
self._openstack = None
|
|
|
|
@property
|
|
def openstack(self):
|
|
if not self._openstack:
|
|
self._openstack = openstack.OpenStackOperator(self.dbapi)
|
|
return self._openstack
|
|
|
|
def get_host_config(self, host):
|
|
"""
|
|
Generate the hiera data for the puppet network config and route config
|
|
resources for the host.
|
|
"""
|
|
# Normalize some of the host info into formats that are easier to
|
|
# use when parsing the interface list.
|
|
context = self._create_interface_context(host)
|
|
|
|
# interface configuration is organized into sets of network_config,
|
|
# route_config and address_config resource hashes (dict)
|
|
config = {
|
|
NETWORK_CONFIG_RESOURCE: {},
|
|
ROUTE_CONFIG_RESOURCE: {},
|
|
ADDRESS_CONFIG_RESOURCE: {},
|
|
SRIOV_CONFIG_RESOURCE: {},
|
|
FPGA_CONFIG_RESOURCE: {},
|
|
DATA_IFACE_LIST_RESOURCE: [],
|
|
}
|
|
|
|
system = self._get_system()
|
|
# For AIO-SX subcloud, mgmt n/w will be on a separate
|
|
# physical interface instead of the loopback interface.
|
|
if system.system_mode != constants.SYSTEM_MODE_SIMPLEX or \
|
|
self._distributed_cloud_role() == \
|
|
constants.DISTRIBUTED_CLOUD_ROLE_SUBCLOUD:
|
|
# Setup the loopback interface first
|
|
generate_loopback_config(config)
|
|
|
|
# Generate the actual interface config resources
|
|
generate_interface_configs(context, config, self.dbapi)
|
|
|
|
# Generate the actual interface config resources
|
|
generate_address_configs(context, config)
|
|
|
|
# Generate data iface list configuration
|
|
generate_data_iface_list_config(context, config)
|
|
|
|
# Update the global context with generated interface context
|
|
self.context.update(context)
|
|
|
|
return config
|
|
|
|
def _create_interface_context(self, host):
|
|
host_interfaces = self.dbapi.iinterface_get_by_ihost(host.uuid)
|
|
networks = self._get_network_type_index()
|
|
address_pools = self._get_address_pool_index()
|
|
network_address_pools = self._get_network_addresspool_index()
|
|
addresses = self._get_address_interface_name_index(host)
|
|
context = {
|
|
'hostname': host.hostname,
|
|
'personality': host.personality,
|
|
'subfunctions': host.subfunctions,
|
|
'system_uuid': host.isystem_uuid,
|
|
'system_mode': self._get_system().system_mode,
|
|
'ports': self._get_port_interface_id_index(host),
|
|
'interfaces': self._get_interface_name_index(host_interfaces),
|
|
'interface_networks': self._get_interface_network_index(
|
|
host_interfaces, addresses, networks, address_pools, network_address_pools),
|
|
'interfaces_datanets': self._get_interface_name_datanets(
|
|
host.hostname, host_interfaces),
|
|
'devices': self._get_port_pciaddr_index(host),
|
|
'addresses': addresses,
|
|
'routes': self._get_routes_interface_name_index(host),
|
|
'networks': networks,
|
|
'address_pools': address_pools,
|
|
'floatingips': self._get_floating_ip_index(networks, address_pools,
|
|
network_address_pools),
|
|
'datanets': self._get_datanetworks(host),
|
|
'vswitchtype': self._vswitch_type(),
|
|
}
|
|
return context
|
|
|
|
def _find_host_interface(self, host_interfaces, networktype):
|
|
"""
|
|
Search the host interface list looking for an interface with a given
|
|
primary network type.
|
|
"""
|
|
for iface in host_interfaces:
|
|
for ni in self.dbapi.interface_network_get_by_interface(
|
|
iface['id']):
|
|
network = self.dbapi.network_get(ni.id)
|
|
if network.type == networktype:
|
|
return iface
|
|
|
|
def _get_port_interface_id_index(self, host):
|
|
"""
|
|
Builds a dictionary of ports indexed by interface id.
|
|
"""
|
|
return interface._get_port_interface_id_index(self.dbapi, host)
|
|
|
|
def _get_interface_name_index(self, host_interfaces):
|
|
"""
|
|
Builds a dictionary of interfaces indexed by interface name.
|
|
"""
|
|
return interface._get_interface_name_index(host_interfaces)
|
|
|
|
def _get_interface_name_datanets(self, hostname, host_interfaces):
|
|
"""
|
|
Builds a dictionary of datanets indexed by interface name.
|
|
"""
|
|
return interface._get_interface_name_datanets(
|
|
self.dbapi, hostname, host_interfaces)
|
|
|
|
def _get_port_pciaddr_index(self, host):
|
|
"""
|
|
Builds a dictionary of port lists indexed by PCI address.
|
|
"""
|
|
devices = collections.defaultdict(list)
|
|
for port in self.dbapi.ethernet_port_get_by_host(host.id):
|
|
devices[port.pciaddr].append(port)
|
|
return devices
|
|
|
|
def _get_address_interface_name_index(self, host):
|
|
"""
|
|
Builds a dictionary of address lists indexed by interface name.
|
|
"""
|
|
return interface._get_address_interface_name_index(self.dbapi, host)
|
|
|
|
def _get_interface_network_index(self, host_interfaces, address_index, network_type_index,
|
|
addrpool_index, network_addrpool_index):
|
|
"""
|
|
Builds a dictionary that associates interfaces with networks and addresses.
|
|
Format:
|
|
{
|
|
<interface_name>: {
|
|
<network_id or None>: {
|
|
'network': <network_object or None>,
|
|
'addresses': [ <address 1>, <address 2>, ... ]
|
|
}
|
|
}
|
|
}
|
|
"""
|
|
|
|
network_id_index = {}
|
|
for network in network_type_index.values():
|
|
network_id_index[network.id] = network
|
|
|
|
interface_index = {}
|
|
for iface in host_interfaces:
|
|
interface_dict = {None: {'network': None, 'addresses': []}}
|
|
for networktype in iface.networktypelist:
|
|
network = network_type_index[networktype]
|
|
interface_dict[network.id] = {'network': network, 'addresses': []}
|
|
interface_index[iface.ifname] = interface_dict
|
|
|
|
for address_list in address_index.values():
|
|
for address in address_list:
|
|
addrpool = addrpool_index.get(address.pool_uuid, None)
|
|
network_addrpool = network_addrpool_index.get(address.pool_uuid, None)
|
|
|
|
network = None
|
|
if addrpool and network_addrpool:
|
|
network = network_id_index[network_addrpool.network_id]
|
|
|
|
network_id = network.id if network else None
|
|
|
|
interface_entry = interface_index.get(address.ifname, None)
|
|
if not interface_entry:
|
|
continue
|
|
|
|
network_entry = interface_entry.get(network_id, None)
|
|
|
|
if not network_entry:
|
|
network_entry = {
|
|
'network': network,
|
|
'addresses': []
|
|
}
|
|
interface_entry[network_id] = network_entry
|
|
|
|
network_entry['addresses'].append(address)
|
|
|
|
return interface_index
|
|
|
|
def _get_routes_interface_name_index(self, host):
|
|
"""
|
|
Builds a dictionary of route lists indexed by interface name.
|
|
"""
|
|
routes = collections.defaultdict(list)
|
|
for route in self.dbapi.routes_get_by_host(host.id):
|
|
routes[route.ifname].append(route)
|
|
|
|
results = collections.defaultdict(list)
|
|
for ifname, entries in six.iteritems(routes):
|
|
entries = sorted(entries, key=lambda r: r['prefix'], reverse=True)
|
|
results[ifname] = entries
|
|
return results
|
|
|
|
def _get_network_type_index(self):
|
|
networks = {}
|
|
for network in self.dbapi.networks_get_all():
|
|
networks[network['type']] = network
|
|
return networks
|
|
|
|
def _get_address_pool_index(self):
|
|
addrpool_index = {}
|
|
addrpools = self.dbapi.address_pools_get_all()
|
|
for addrpool in addrpools:
|
|
addrpool_index[addrpool.uuid] = addrpool
|
|
return addrpool_index
|
|
|
|
def _get_network_addresspool_index(self):
|
|
network_addrpool_index = {}
|
|
network_addrpools = self.dbapi.network_addrpool_get_all()
|
|
for network_addrpool in network_addrpools:
|
|
network_addrpool_index[network_addrpool.address_pool_uuid] = network_addrpool
|
|
return network_addrpool_index
|
|
|
|
def _get_floating_ip_index(self, networks, address_pools, network_address_pools):
|
|
"""
|
|
Builds a dictionary of floating ip addresses indexed by network type.
|
|
"""
|
|
|
|
networktypes = [
|
|
constants.NETWORK_TYPE_MGMT,
|
|
constants.NETWORK_TYPE_PXEBOOT,
|
|
constants.NETWORK_TYPE_CLUSTER_HOST,
|
|
constants.NETWORK_TYPE_IRONIC,
|
|
constants.NETWORK_TYPE_STORAGE,
|
|
constants.NETWORK_TYPE_ADMIN
|
|
]
|
|
|
|
system = self._get_system()
|
|
if system.system_mode != constants.SYSTEM_MODE_SIMPLEX:
|
|
networktypes.append(constants.NETWORK_TYPE_OAM)
|
|
|
|
network_index = {}
|
|
for network in networks.values():
|
|
if network.type in networktypes:
|
|
network_index[network.id] = network
|
|
|
|
floating_ips = collections.defaultdict(list)
|
|
for network_address_pool in network_address_pools.values():
|
|
network = network_index.get(network_address_pool.network_id, None)
|
|
if not network:
|
|
continue
|
|
address_pool = address_pools[network_address_pool.address_pool_uuid]
|
|
if not address_pool.floating_address_id:
|
|
continue
|
|
try:
|
|
address = self.dbapi.address_get_by_id(address_pool.floating_address_id)
|
|
floating_ips[network.type].append(address)
|
|
except exception.AddressNotFoundById:
|
|
pass
|
|
|
|
return floating_ips
|
|
|
|
def _get_datanetworks(self, host):
|
|
dnets = {}
|
|
if constants.WORKER in utils.get_personalities(host):
|
|
dnets = self.dbapi.datanetworks_get_all()
|
|
return dnets
|
|
|
|
|
|
def is_platform_network_type(iface):
|
|
return bool(iface['ifclass'] == constants.INTERFACE_CLASS_PLATFORM)
|
|
|
|
|
|
def is_data_network_type(iface):
|
|
return bool(iface['ifclass'] == constants.INTERFACE_CLASS_DATA)
|
|
|
|
|
|
def is_controller(context):
|
|
"""
|
|
Determine we are creating a manifest for a controller node; regardless of
|
|
whether it has a worker subfunction or not.
|
|
"""
|
|
return bool(context['personality'] == constants.CONTROLLER)
|
|
|
|
|
|
def is_worker_subfunction(context):
|
|
"""
|
|
Determine if we are creating a manifest for a worker node or a worker
|
|
subfunction.
|
|
"""
|
|
if context['personality'] == constants.WORKER:
|
|
return True
|
|
if constants.WORKER in context['subfunctions']:
|
|
return True
|
|
return False
|
|
|
|
|
|
def is_vswitch_type_unaccelerated(context):
|
|
"""
|
|
Determine if the underlying device vswitch type is unaccelerated.
|
|
"""
|
|
if context['vswitchtype'] == constants.VSWITCH_TYPE_NONE:
|
|
return True
|
|
return False
|
|
|
|
|
|
def is_pci_interface(iface):
|
|
"""
|
|
Determine if the interface is one of the PCI device types.
|
|
"""
|
|
return bool(iface['ifclass'] in PCI_INTERFACE_CLASSES)
|
|
|
|
|
|
def is_platform_interface(context, iface):
|
|
"""
|
|
Determine whether the interface needs to be configured in the linux kernel
|
|
as opposed to interfaces that exist purely in the vswitch. This includes
|
|
interfaces that are themselves platform interfaces or interfaces that have
|
|
platform interfaces above them. Both of these groups of interfaces require
|
|
a linux interface that will be used for platform purposes (i.e., pxeboot,
|
|
mgmt, cluster-host, oam).
|
|
"""
|
|
if '_kernel' in iface: # check cached result
|
|
return iface['_kernel']
|
|
else:
|
|
kernel = False
|
|
if is_platform_network_type(iface):
|
|
kernel = True
|
|
else:
|
|
upper_ifnames = iface['used_by'] or []
|
|
for upper_ifname in upper_ifnames:
|
|
upper_iface = context['interfaces'][upper_ifname]
|
|
if is_platform_interface(context, upper_iface):
|
|
kernel = True
|
|
break
|
|
iface['_kernel'] = kernel # cache the result
|
|
return iface['_kernel']
|
|
|
|
|
|
def is_data_interface(context, iface):
|
|
"""
|
|
Determine whether the interface needs to be configured in the vswitch.
|
|
This includes interfaces that are themselves data interfaces or interfaces
|
|
that have data interfaces above them. Both of these groups of interfaces
|
|
require vswitch configuration data.
|
|
"""
|
|
if '_data' in iface: # check cached result
|
|
return iface['_data']
|
|
else:
|
|
data = False
|
|
if is_data_network_type(iface):
|
|
data = True
|
|
else:
|
|
upper_ifnames = iface['used_by'] or []
|
|
for upper_ifname in upper_ifnames:
|
|
upper_iface = context['interfaces'][upper_ifname]
|
|
if is_data_interface(context, upper_iface):
|
|
data = True
|
|
break
|
|
iface['_data'] = data # cache the result
|
|
return iface['_data']
|
|
|
|
|
|
def is_dpdk_compatible(context, iface):
|
|
"""
|
|
Determine whether an interface can be supported in vswitch as a native DPDK
|
|
interface. Since whether an interface is supported or not by the DPDK
|
|
means whether the DPDK has a hardware device driver for the underlying
|
|
physical device this also implies that all non-hardware related interfaces
|
|
are automatically supported in the DPDK. For this reason we report True
|
|
for VLAN and AE interfaces but check the DPDK support status for any
|
|
ethernet interfaces.
|
|
"""
|
|
if '_dpdksupport' in iface: # check the cached result
|
|
return iface['_dpdksupport']
|
|
elif iface['iftype'] == constants.INTERFACE_TYPE_ETHERNET:
|
|
port = get_interface_port(context, iface)
|
|
dpdksupport = port.get('dpdksupport', False)
|
|
else:
|
|
dpdksupport = True
|
|
iface['_dpdksupport'] = dpdksupport # cache the result
|
|
return iface['_dpdksupport']
|
|
|
|
|
|
def is_a_mellanox_device(context, iface):
|
|
"""
|
|
Determine if the underlying device is a Mellanox device.
|
|
"""
|
|
if (iface['iftype'] not in
|
|
[constants.INTERFACE_TYPE_ETHERNET, constants.INTERFACE_TYPE_VF]):
|
|
# We only care about configuring specific settings for related ethernet
|
|
# devices or VFs on top of these
|
|
return False
|
|
|
|
if iface['ifclass'] == constants.INTERFACE_CLASS_PCI_SRIOV:
|
|
port = get_sriov_interface_port(context, iface)
|
|
else:
|
|
port = get_interface_port(context, iface)
|
|
|
|
# port['driver'] may be a string of various comma separated driver names
|
|
if port['driver']:
|
|
drivers = (d.strip() for d in port['driver'].split(','))
|
|
for d in drivers:
|
|
if d in constants.MELLANOX_DRIVERS:
|
|
return True
|
|
return False
|
|
|
|
|
|
def is_an_n3000_i40_device(context, iface):
|
|
"""
|
|
Determine if the underlying device is onboard an N3000 FPGA.
|
|
"""
|
|
if iface['iftype'] != constants.INTERFACE_TYPE_ETHERNET:
|
|
# We only care about configuring specific settings for related ethernet
|
|
# devices.
|
|
return False
|
|
|
|
port = get_interface_port(context, iface)
|
|
if not port:
|
|
return False
|
|
|
|
device_id = interface.get_pci_device_id(port)
|
|
if not device_id:
|
|
return False
|
|
|
|
if device_id == dconstants.PCI_DEVICE_ID_FPGA_INTEL_I40_PF:
|
|
return True
|
|
return False
|
|
|
|
|
|
def get_master_interface(context, iface):
|
|
"""
|
|
Get the interface name of the given interface's master (if any). The
|
|
master interface is the AE interface for any Ethernet interfaces.
|
|
"""
|
|
if '_master' not in iface: # check the cached result
|
|
master = None
|
|
if iface['iftype'] == constants.INTERFACE_TYPE_ETHERNET:
|
|
upper_ifnames = iface['used_by'] or []
|
|
for upper_ifname in upper_ifnames:
|
|
upper_iface = context['interfaces'][upper_ifname]
|
|
if upper_iface['iftype'] == constants.INTERFACE_TYPE_AE:
|
|
master = upper_iface['ifname']
|
|
break
|
|
iface['_master'] = master # cache the result
|
|
return iface['_master']
|
|
|
|
|
|
def is_slave_interface(context, iface):
|
|
"""
|
|
Determine if this interface is a slave interface. A slave interface is an
|
|
interface that is part of an AE interface.
|
|
"""
|
|
if '_slave' not in iface: # check the cached result
|
|
master = get_master_interface(context, iface)
|
|
iface['_slave'] = bool(master) # cache the result
|
|
return iface['_slave']
|
|
|
|
|
|
def get_interface_mtu(context, iface):
|
|
"""
|
|
Determine the MTU value to use for a given interface. We trust that sysinv
|
|
has selected the correct value.
|
|
"""
|
|
return iface['imtu']
|
|
|
|
|
|
def get_interface_datanets(context, iface):
|
|
"""
|
|
Return the list of data networks of the supplied interface
|
|
"""
|
|
return interface.get_interface_datanets(context, iface)
|
|
|
|
|
|
def _get_datanetwork_names(context, iface):
|
|
"""
|
|
Return the CSV list of data networks of the supplied interface
|
|
"""
|
|
return interface._get_datanetwork_names(context, iface)
|
|
|
|
|
|
def get_interface_port(context, iface):
|
|
"""
|
|
Determine the port of the underlying device.
|
|
"""
|
|
return interface.get_interface_port(context, iface)
|
|
|
|
|
|
def get_interface_port_name(context, iface):
|
|
"""
|
|
Determine the port name of the underlying device.
|
|
"""
|
|
assert iface['iftype'] == constants.INTERFACE_TYPE_ETHERNET
|
|
port = get_interface_port(context, iface)
|
|
if port:
|
|
return port['name']
|
|
|
|
|
|
def get_lower_interface(context, iface):
|
|
"""
|
|
Return the interface object that is used to implement a VLAN interface.
|
|
"""
|
|
return interface.get_lower_interface(context, iface)
|
|
|
|
|
|
def get_lower_interface_os_ifname(context, iface):
|
|
"""
|
|
Return the kernel interface name of the lower interface used to implement a
|
|
VLAN interface.
|
|
"""
|
|
lower_iface = get_lower_interface(context, iface)
|
|
return get_interface_os_ifname(context, lower_iface)
|
|
|
|
|
|
def get_vlan_os_ifname(iface):
|
|
"""
|
|
Generate the interface name used in the linux kernel for the given VLAN
|
|
interface.
|
|
"""
|
|
if iface['ifclass'] == constants.INTERFACE_CLASS_PLATFORM:
|
|
return "vlan" + str(iface['vlan_id'])
|
|
# If ifname is in the format vlanNNN or xxx.NNN, replace the dot by '#' to
|
|
# avoid problems when added to /etc/network/interfaces.
|
|
ifname = iface['ifname']
|
|
if re.search("^vlan[0-9]+$", ifname):
|
|
return "vlan#%s" % ifname[4:]
|
|
match = re.search("\.[0-9]+$", ifname)
|
|
if match:
|
|
return ifname[:match.start()] + '#' + ifname[match.start() + 1:]
|
|
return ifname
|
|
|
|
|
|
def get_interface_os_ifname(context, iface):
|
|
"""
|
|
Determine the interface name used in the linux kernel for the given
|
|
interface. Ethernet interfaces uses the original linux device name while
|
|
AE devices can use the user-defined named. VLAN interfaces use ifname as
|
|
Linux interface name.
|
|
"""
|
|
if '_os_ifname' in iface: # check cached result
|
|
return iface['_os_ifname']
|
|
else:
|
|
os_ifname = iface['ifname']
|
|
if iface['iftype'] == constants.INTERFACE_TYPE_ETHERNET:
|
|
os_ifname = get_interface_port_name(context, iface)
|
|
elif iface['iftype'] == constants.INTERFACE_TYPE_VLAN:
|
|
os_ifname = get_vlan_os_ifname(iface)
|
|
elif iface['iftype'] == constants.INTERFACE_TYPE_AE:
|
|
os_ifname = iface['ifname']
|
|
iface['_os_ifname'] = os_ifname # cache the result
|
|
return iface['_os_ifname']
|
|
|
|
|
|
def get_interface_devices(context, iface, devices=None):
|
|
"""
|
|
Determine all the interface devices used in the linux kernel for the given
|
|
interface name. Ethernet interfaces uses the original linux device while AE
|
|
and VLAN interfaces use all the slave devices. Virtual interfaces use a name.
|
|
"""
|
|
if devices is None:
|
|
devices = []
|
|
|
|
if iface['iftype'] == constants.INTERFACE_TYPE_ETHERNET:
|
|
devices.append(get_interface_port_name(context, iface))
|
|
elif iface['iftype'] == constants.INTERFACE_TYPE_VIRTUAL:
|
|
devices.append(iface['ifname'])
|
|
elif iface['iftype'] == constants.INTERFACE_TYPE_VLAN \
|
|
or iface['iftype'] == constants.INTERFACE_TYPE_AE:
|
|
slaves = get_interface_slaves(context, iface)
|
|
for slave in slaves:
|
|
get_interface_devices(context, slave, devices)
|
|
|
|
return devices
|
|
|
|
|
|
def get_interface_routes(context, iface):
|
|
"""
|
|
Determine the list of routes that are applicable to a given interface (if
|
|
any).
|
|
"""
|
|
return context['routes'][iface['ifname']]
|
|
|
|
|
|
def _set_address_netmask(address):
|
|
"""
|
|
The netmask is not supplied by sysinv but is required by the puppet
|
|
resource class.
|
|
"""
|
|
network = IPNetwork(address['address'] + '/' + str(address['prefix']))
|
|
if network.version == 6:
|
|
address['netmask'] = str(network.prefixlen)
|
|
else:
|
|
address['netmask'] = str(network.netmask)
|
|
return address
|
|
|
|
|
|
def get_gateway_address(context, network, address):
|
|
"""
|
|
Gets the corresponding gateway for the provided address
|
|
"""
|
|
|
|
addrpool = context['address_pools'].get(address.pool_uuid, None)
|
|
|
|
if not addrpool:
|
|
return None
|
|
|
|
if (network and network.type == constants.NETWORK_TYPE_MGMT and
|
|
context['personality'] in [constants.WORKER, constants.STORAGE]):
|
|
gateway_address = addrpool.floating_address
|
|
else:
|
|
gateway_address = addrpool.gateway_address
|
|
|
|
return gateway_address
|
|
|
|
|
|
def get_interface_address_method(context, iface, network=None, address=None):
|
|
networktype = network.type if network else None
|
|
|
|
has_static_addr = False
|
|
if address:
|
|
if address.family == constants.IPV4_FAMILY and iface.ipv4_mode == constants.IPV4_STATIC:
|
|
has_static_addr = True
|
|
elif address.family == constants.IPV6_FAMILY and iface.ipv6_mode == constants.IPV6_STATIC:
|
|
has_static_addr = True
|
|
|
|
if iface.ifclass == constants.INTERFACE_CLASS_DATA:
|
|
if has_static_addr:
|
|
return STATIC_METHOD
|
|
# All data interfaces configured in the kernel because they are not
|
|
# natively supported in vswitch or need to be shared with the kernel
|
|
# because of a platform VLAN should be left as manual config
|
|
return MANUAL_METHOD
|
|
elif (iface.ifclass == constants.INTERFACE_CLASS_PLATFORM and networktype is None):
|
|
if has_static_addr:
|
|
return STATIC_METHOD
|
|
return MANUAL_METHOD
|
|
elif not iface.ifclass or iface.ifclass == constants.INTERFACE_CLASS_NONE \
|
|
or not networktype:
|
|
# Interfaces that are configured purely as a dependency from other
|
|
# interfaces (i.e., vlan lower interface, bridge member, bond slave)
|
|
# should be left as manual config
|
|
return MANUAL_METHOD
|
|
elif iface.ifclass in PCI_INTERFACE_CLASSES:
|
|
return MANUAL_METHOD
|
|
else:
|
|
if is_controller(context):
|
|
# All other interface types that exist on a controller are setup
|
|
# statically since the controller themselves run the DHCP server.
|
|
return STATIC_METHOD
|
|
elif networktype == constants.NETWORK_TYPE_MGMT:
|
|
return STATIC_METHOD
|
|
elif networktype == constants.NETWORK_TYPE_CLUSTER_HOST:
|
|
return STATIC_METHOD
|
|
elif networktype == constants.NETWORK_TYPE_STORAGE:
|
|
return STATIC_METHOD
|
|
elif networktype == constants.NETWORK_TYPE_ADMIN:
|
|
return STATIC_METHOD
|
|
elif networktype == constants.NETWORK_TYPE_PXEBOOT:
|
|
if context['personality'] in [constants.WORKER, constants.STORAGE]:
|
|
return DHCP_METHOD
|
|
return MANUAL_METHOD
|
|
else:
|
|
# All other types get their addresses from the controller
|
|
return DHCP_METHOD
|
|
|
|
|
|
def get_interface_traffic_classifier(context, iface):
|
|
"""
|
|
Get the interface traffic classifier command line (if any)
|
|
"""
|
|
for networktype in iface.networktypelist:
|
|
if (networktype == constants.NETWORK_TYPE_MGMT):
|
|
networkspeed = constants.LINK_SPEED_10G
|
|
ifname = get_interface_os_ifname(context, iface)
|
|
return '%s %s %s %s > /dev/null' \
|
|
% (constants.TRAFFIC_CONTROL_SCRIPT,
|
|
ifname,
|
|
networktype,
|
|
networkspeed)
|
|
return None
|
|
|
|
|
|
def get_bridge_interface_name(context, iface):
|
|
"""
|
|
If the given interface is a bridge member then retrieve the bridge
|
|
interface name otherwise return None.
|
|
"""
|
|
if '_bridge' in iface: # check the cached result
|
|
return iface['_bridge']
|
|
else:
|
|
bridge = None
|
|
if (iface['iftype'] == constants.INTERFACE_TYPE_ETHERNET and
|
|
is_data_interface(context, iface) and
|
|
not is_dpdk_compatible(context, iface) and
|
|
not is_vswitch_type_unaccelerated(context)):
|
|
bridge = 'br-' + get_interface_os_ifname(context, iface)
|
|
iface['_bridge'] = bridge # cache the result
|
|
return iface['_bridge']
|
|
|
|
|
|
def is_bridged_interface(context, iface):
|
|
"""
|
|
Determine if this interface is a member of a bridge. A interface is a
|
|
member of a bridge if the interface is a data interface that is not
|
|
accelerated (i.e., a slow data interface).
|
|
"""
|
|
if '_bridged' in iface: # check the cached result
|
|
return iface['_bridged']
|
|
else:
|
|
bridge = get_bridge_interface_name(context, iface)
|
|
iface['_bridged'] = bool(bridge) # cache the result
|
|
return iface['_bridged']
|
|
|
|
|
|
def needs_interface_config(context, iface):
|
|
"""
|
|
Determine whether an interface needs to be configured in the linux kernel.
|
|
This is true if the interface is a platform interface, is required by a
|
|
platform interface (i.e., an AE member, a VLAN lower interface), or is an
|
|
unaccelerated data interface.
|
|
"""
|
|
if is_platform_interface(context, iface):
|
|
return True
|
|
elif not is_worker_subfunction(context):
|
|
return False
|
|
elif is_data_interface(context, iface):
|
|
if is_vswitch_type_unaccelerated(context):
|
|
# a platform interface configuration will use the host interface when
|
|
# the vswitch is unaccelerated.
|
|
return True
|
|
if not is_dpdk_compatible(context, iface):
|
|
# vswitch interfaces for devices that are not natively supported by
|
|
# the DPDK are created as regular Linux devices and then bridged in
|
|
# to vswitch in order for it to be able to use it indirectly.
|
|
return True
|
|
if is_a_mellanox_device(context, iface):
|
|
# Check for Mellanox data interfaces. We must set the MTU sizes of
|
|
# Mellanox data interfaces in case it is not the default. Normally
|
|
# data interfaces are owned by DPDK, they are not managed through
|
|
# Linux but in the Mellanox case, the interfaces are still visible
|
|
# in Linux so in case one needs to set jumbo frames, it has to be
|
|
# set in Linux as well. We only do this for combined nodes or
|
|
# non-controller nodes.
|
|
return True
|
|
elif is_pci_interface(iface):
|
|
return True
|
|
return False
|
|
|
|
|
|
def get_basic_network_config(ifname, ensure='present',
|
|
method='manual', onboot='true',
|
|
hotplug='false', family='inet',
|
|
mtu=None):
|
|
"""
|
|
Builds a basic network config dictionary with all of the fields required to
|
|
format a basic network_config puppet resource.
|
|
"""
|
|
config = {'ifname': ifname,
|
|
'ensure': ensure,
|
|
'family': family,
|
|
'method': method,
|
|
'hotplug': hotplug,
|
|
'onboot': onboot,
|
|
'options': {}}
|
|
if mtu:
|
|
config['options']['mtu'] = str(mtu)
|
|
return config
|
|
|
|
|
|
def is_disable_dad_required(iface, network=None):
|
|
"""
|
|
Disable DAD command is included in the base interface config, only for interfaces associated
|
|
with management and cluster-host networks.
|
|
"""
|
|
|
|
if network:
|
|
return False
|
|
networks = [constants.NETWORK_TYPE_MGMT, constants.NETWORK_TYPE_CLUSTER_HOST]
|
|
for networktype in iface.networktypelist:
|
|
if networktype in networks:
|
|
return True
|
|
return False
|
|
|
|
|
|
def get_interface_sysctl_ifname(context, iface):
|
|
"""
|
|
Get the interface name that is used for sysctl commands
|
|
"""
|
|
os_ifname = get_interface_os_ifname(context, iface)
|
|
if (iface['iftype'] == constants.INTERFACE_TYPE_VLAN):
|
|
return os_ifname.replace('.', '/')
|
|
else:
|
|
return os_ifname
|
|
|
|
|
|
def get_duplex_direct_network_config(context, iface, config, network):
|
|
"""
|
|
Disable dad on the specified interface for duplex-direct config
|
|
"""
|
|
if iface['iftype'] == constants.INTERFACE_TYPE_AE:
|
|
command = ("/sbin/modprobe bonding; "
|
|
"grep %s /sys/class/net/bonding_masters || "
|
|
"echo +%s > /sys/class/net/bonding_masters" % (
|
|
iface['ifname'], iface['ifname']))
|
|
fill_interface_config_option_operation(config['options'], IFACE_PRE_UP_OP, command)
|
|
|
|
if iface['iftype'] == constants.INTERFACE_TYPE_VLAN:
|
|
add_vlan_interface_creation_command(context, iface, config['options'])
|
|
|
|
sysctl_ifname = get_interface_sysctl_ifname(context, iface)
|
|
new_pre_up = "sysctl -wq net.ipv6.conf.%s.accept_dad=0" % sysctl_ifname
|
|
fill_interface_config_option_operation(config['options'], IFACE_PRE_UP_OP, new_pre_up)
|
|
return config
|
|
|
|
|
|
def get_vlan_network_config(context, iface, config):
|
|
"""
|
|
Augments a basic config dictionary with the attributes specific to a VLAN
|
|
interface.
|
|
"""
|
|
lower_os_ifname = get_lower_interface_os_ifname(context, iface)
|
|
options = {'vlan-raw-device': lower_os_ifname}
|
|
fill_interface_config_option_operation(options, IFACE_PRE_UP_OP,
|
|
'/sbin/modprobe -q 8021q')
|
|
if iface['ifclass'] != constants.INTERFACE_CLASS_PLATFORM:
|
|
add_vlan_interface_creation_command(context, iface, options)
|
|
config['options'].update(options)
|
|
return config
|
|
|
|
|
|
def add_vlan_interface_creation_command(context, iface, options):
|
|
if hasattr(iface, '_has_create_cmd'):
|
|
return
|
|
iface['_has_create_cmd'] = True
|
|
os_ifname = get_interface_os_ifname(context, iface)
|
|
lower_os_ifname = get_lower_interface_os_ifname(context, iface)
|
|
fill_interface_config_option_operation(options, IFACE_PRE_UP_OP,
|
|
'ip link add link %s name %s type vlan id %d' %
|
|
(lower_os_ifname, os_ifname, iface['vlan_id']))
|
|
fill_interface_config_option_operation(options, IFACE_POST_DOWN_OP,
|
|
'ip link del %s' % (os_ifname))
|
|
|
|
|
|
def get_bond_interface_options_ifupdown(iface, primary_iface):
|
|
"""
|
|
Get the interface config attribute for bonding options
|
|
"""
|
|
ae_mode = iface['aemode']
|
|
tx_hash_policy = iface['txhashpolicy']
|
|
options = dict()
|
|
options['bond-miimon'] = '100'
|
|
if ae_mode in ACTIVE_STANDBY_AE_MODES:
|
|
# Requires the active device in an active_standby LAG
|
|
# configuration to be determined based on the lowest MAC address
|
|
options['bond-mode'] = 'active-backup'
|
|
options['bond-primary'] = primary_iface['ifname']
|
|
if iface['primary_reselect']:
|
|
options['bond-primary-reselect'] = iface['primary_reselect']
|
|
else:
|
|
options['bond-xmit-hash-policy'] = tx_hash_policy
|
|
if ae_mode in BALANCED_AE_MODES:
|
|
options['bond-mode'] = 'balance-xor'
|
|
elif ae_mode in LACP_AE_MODES:
|
|
options['bond-mode'] = '802.3ad'
|
|
options['bond-lacp-rate'] = 'fast'
|
|
if iface['uses']:
|
|
bond_slaves = str()
|
|
for iface in iface['uses']:
|
|
bond_slaves += (iface + ' ')
|
|
options['bond-slaves'] = bond_slaves
|
|
return options
|
|
|
|
|
|
def get_bond_network_config(context, iface, config):
|
|
"""
|
|
Augments a basic config dictionary with the attributes specific to a bond
|
|
interface.
|
|
"""
|
|
primary_iface = get_primary_bond_interface(context, iface)
|
|
options = dict()
|
|
bonding_options = None
|
|
iface_mac = iface['imac'].rstrip()
|
|
|
|
options['hwaddress'] = iface_mac
|
|
bonding_options = get_bond_interface_options_ifupdown(iface, primary_iface)
|
|
if bonding_options:
|
|
options.update(bonding_options)
|
|
|
|
if bonding_options:
|
|
fill_interface_config_option_operation(options, IFACE_UP_OP, 'sleep 10')
|
|
config['options'].update(options)
|
|
return config
|
|
|
|
|
|
def get_primary_bond_interface(context, iface):
|
|
"""
|
|
Return the slave interface with the lowest MAC address
|
|
"""
|
|
slaves = get_interface_slaves(context, iface)
|
|
sorted_slaves = sorted(slaves, key=slave_sort_key)
|
|
primary_iface = sorted_slaves[0]
|
|
return primary_iface
|
|
|
|
|
|
def get_interface_slaves(context, iface):
|
|
"""
|
|
Return the slave interface objects for the corresponding
|
|
bond or vlan interface.
|
|
"""
|
|
slaves = iface['uses']
|
|
ifaces = []
|
|
for ifname, iface in six.iteritems(context['interfaces']):
|
|
if ifname in slaves:
|
|
ifaces.append(iface)
|
|
return ifaces
|
|
|
|
|
|
def slave_sort_key(iface):
|
|
"""
|
|
Sort interfaces by lowest MAC address
|
|
"""
|
|
return int(iface['imac'].replace(':', ''), 16)
|
|
|
|
|
|
def get_ethernet_network_config(context, iface, config):
|
|
"""
|
|
Augments a basic config dictionary with the attributes specific to an
|
|
ethernet interface.
|
|
"""
|
|
interface_class = iface['ifclass']
|
|
options = {}
|
|
|
|
if is_bridged_interface(context, iface):
|
|
pass
|
|
elif is_slave_interface(context, iface):
|
|
if not is_data_interface(context, iface):
|
|
# Data interfaces that require a network configuration are not
|
|
# candidates for bonding. They exist because their DPDK drivers
|
|
# rely on the Linux device driver to setup some or all functions
|
|
# on the device (e.g., the Mellanox DPDK driver relies on the
|
|
# Linux driver to set the proper MTU value).
|
|
master = get_master_interface(context, iface)
|
|
options['bond-master'] = master
|
|
osname = get_interface_os_ifname(context, iface)
|
|
command = '/usr/sbin/ip link set dev {} promisc on'.format(osname)
|
|
fill_interface_config_option_operation(options, IFACE_PRE_UP_OP, command)
|
|
# the allow-* is a separated stanza in ifupdown, but without
|
|
# support in puppet-network module, this stanza is needed to
|
|
# make ifup to run the slave's pre-up commands. It will be
|
|
# adjusted during parsing in apply_network_config.sh
|
|
options['allow-{}'.format(master)] = osname
|
|
if interface_class == constants.INTERFACE_CLASS_PCI_SRIOV:
|
|
if iface['iftype'] == constants.INTERFACE_TYPE_ETHERNET:
|
|
sriovfs_path = ("/sys/class/net/%s/device/sriov_numvfs" %
|
|
get_interface_port_name(context, iface))
|
|
command = "echo 0 > %s; echo %s > %s" % (sriovfs_path, iface['sriov_numvfs'],
|
|
sriovfs_path)
|
|
iface_op = get_device_sriov_setup_op(context, iface)
|
|
fill_interface_config_option_operation(options, iface_op, command)
|
|
elif interface_class == constants.INTERFACE_CLASS_PCI_SRIOV:
|
|
if iface['iftype'] == constants.INTERFACE_TYPE_ETHERNET:
|
|
sriovfs_path = ("/sys/class/net/%s/device/sriov_numvfs" %
|
|
get_interface_port_name(context, iface))
|
|
command = "echo 0 > %s; echo %s > %s" % (sriovfs_path, iface['sriov_numvfs'],
|
|
sriovfs_path)
|
|
iface_op = get_device_sriov_setup_op(context, iface)
|
|
fill_interface_config_option_operation(options, iface_op, command)
|
|
elif interface_class == constants.INTERFACE_CLASS_PCI_PASSTHROUGH:
|
|
sriovfs_path = ("/sys/class/net/%s/device/sriov_numvfs" %
|
|
get_interface_port_name(context, iface))
|
|
command = "if [ -f %s ]; then echo 0 > %s; fi" % (
|
|
sriovfs_path, sriovfs_path)
|
|
iface_op = get_device_sriov_setup_op(context, iface)
|
|
fill_interface_config_option_operation(options, iface_op, command)
|
|
|
|
config['options'].update(options)
|
|
return config
|
|
|
|
|
|
def get_route_config(route, ifname):
|
|
"""
|
|
Builds a basic route config dictionary with all of the fields required to
|
|
format a basic network_route puppet resource.
|
|
"""
|
|
if route['prefix']:
|
|
name = '%s/%s' % (route['network'], route['prefix'])
|
|
else:
|
|
name = 'default'
|
|
netmask = IPNetwork(route['network'] + "/" + str(route['prefix'])).netmask
|
|
config = {
|
|
'name': name,
|
|
'ensure': 'present',
|
|
'gateway': route['gateway'],
|
|
'interface': ifname,
|
|
'netmask': str(netmask) if route['prefix'] else '0.0.0.0',
|
|
'network': route['network'] if route['prefix'] else 'default',
|
|
'options': 'metric ' + str(route['metric'])
|
|
|
|
}
|
|
return config
|
|
|
|
|
|
def get_device_sriov_setup_op(context, iface):
|
|
"""
|
|
Determines if the interface has a driver that requires it to be up before
|
|
SR-IOV/virtual function interfaces can be set up. Returns the corresponding
|
|
interface pre/post-up operation code.
|
|
"""
|
|
port = get_interface_port(context, iface)
|
|
|
|
if port['driver'] in constants.DRIVERS_UP_BEFORE_SRIOV:
|
|
return IFACE_POST_UP_OP
|
|
else:
|
|
return IFACE_PRE_UP_OP
|
|
|
|
|
|
def get_sriov_interface_up_requirement(context, iface):
|
|
"""
|
|
Determines if an interface has a driver that requires it to be
|
|
administratively up before VFs can be set up.
|
|
"""
|
|
port = get_interface_port(context, iface)
|
|
|
|
if port['driver'] in constants.DRIVERS_UP_BEFORE_SRIOV:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
|
|
def get_sriov_interface_port(context, iface):
|
|
"""
|
|
Determine the underlying port of the SR-IOV interface.
|
|
"""
|
|
return interface.get_sriov_interface_port(context, iface)
|
|
|
|
|
|
def get_sriov_interface_device_id(context, iface):
|
|
"""
|
|
Determine the underlying PCI device id of the SR-IOV interface.
|
|
"""
|
|
return interface.get_sriov_interface_device_id(context, iface)
|
|
|
|
|
|
def get_sriov_interface_vf_addrs(context, iface, vf_addr_list):
|
|
"""
|
|
Determine the virtual function addresses of SR-IOV interface,
|
|
given the list of vf addresses on the port.
|
|
"""
|
|
return interface.get_sriov_interface_vf_addrs(context, iface, vf_addr_list)
|
|
|
|
|
|
def get_sriov_vf_config(context, iface, port, vf_config):
|
|
"""
|
|
Determine the virtual function config for an SR-IOV interface.
|
|
"""
|
|
|
|
# Calculate the VF addresses to assign to a logical VF interface,
|
|
# taking into account any upper or lower interfaces.
|
|
vf_addr_list = []
|
|
all_vf_addr_list = []
|
|
all_vf_addrs = port.get('sriov_vfs_pci_address', None)
|
|
if all_vf_addrs:
|
|
all_vf_addr_list = all_vf_addrs.split(',')
|
|
vf_addr_list = interface.get_sriov_interface_vf_addrs(
|
|
context, iface, all_vf_addr_list)
|
|
|
|
# Format the vf addresses as quoted strings in order to prevent
|
|
# puppet from treating the address as a time/date value
|
|
vf_addrs = [quoted_str(addr.strip()) for addr in vf_addr_list if addr]
|
|
|
|
# Get the user specified VF driver, if any. If the driver is
|
|
# None, the driver will be determined by the kernel. That is,
|
|
# No explicit bind will be done.
|
|
vf_driver = iface.get('sriov_vf_driver', None)
|
|
if vf_driver:
|
|
if constants.SRIOV_DRIVER_TYPE_VFIO in vf_driver:
|
|
vf_driver = constants.SRIOV_DRIVER_VFIO_PCI
|
|
elif constants.SRIOV_DRIVER_TYPE_NETDEVICE in vf_driver:
|
|
vf_driver = port.get('sriov_vf_driver', None)
|
|
|
|
for addr in vf_addrs:
|
|
rate = iface.get('max_tx_rate', None)
|
|
if rate:
|
|
vfnum = utils.get_sriov_vf_index(addr, all_vf_addr_list)
|
|
vf_config.update({
|
|
addr: {
|
|
'addr': addr,
|
|
'driver': vf_driver,
|
|
'vfnumber': vfnum,
|
|
'max_tx_rate': rate
|
|
}
|
|
})
|
|
else:
|
|
vf_config.update({
|
|
addr: {
|
|
'addr': addr,
|
|
'driver': vf_driver
|
|
}
|
|
})
|
|
|
|
if iface.get('used_by', None):
|
|
upper_ifaces = iface['used_by']
|
|
for upper_ifname in upper_ifaces:
|
|
upper_iface = context['interfaces'][upper_ifname]
|
|
if upper_iface['iftype'] == constants.INTERFACE_TYPE_VF:
|
|
get_sriov_vf_config(context, upper_iface, port, vf_config)
|
|
|
|
|
|
def get_sriov_config(context, iface):
|
|
"""
|
|
Returns an SR-IOV interface config dictionary.
|
|
"""
|
|
vf_config = {}
|
|
|
|
if iface['iftype'] != constants.INTERFACE_TYPE_ETHERNET:
|
|
return {}
|
|
|
|
port = interface.get_sriov_interface_port(context, iface)
|
|
if not port:
|
|
return {}
|
|
|
|
# Include the desired number of VFs if the device supports SR-IOV
|
|
# config via sysfs and is not a sub-interface
|
|
num_vfs = None
|
|
if iface['iftype'] != constants.INTERFACE_TYPE_VF:
|
|
num_vfs = iface['sriov_numvfs']
|
|
|
|
get_sriov_vf_config(context, iface, port, vf_config)
|
|
|
|
config = {
|
|
'ifname': iface['ifname'],
|
|
'addr': quoted_str(port['pciaddr'].strip()),
|
|
'num_vfs': num_vfs,
|
|
'device_id': interface.get_sriov_interface_device_id(context, iface),
|
|
'port_name': port['name'],
|
|
'up_requirement': get_sriov_interface_up_requirement(context, iface),
|
|
'vf_config': vf_config
|
|
}
|
|
return config
|
|
|
|
|
|
def get_n3000_config(context, iface):
|
|
config = {}
|
|
if is_an_n3000_i40_device(context, iface):
|
|
port = get_interface_port(context, iface)
|
|
if not port:
|
|
return {}
|
|
|
|
device_id = interface.get_pci_device_id(port)
|
|
if not device_id:
|
|
return {}
|
|
|
|
vlans = []
|
|
for ifname in iface.get('used_by', []):
|
|
upper = context['interfaces'][ifname]
|
|
if upper['iftype'] == constants.INTERFACE_TYPE_VLAN:
|
|
vlans.append(get_interface_os_ifname(context, upper))
|
|
|
|
config = {
|
|
'ifname': port['name'],
|
|
'device_id': device_id,
|
|
'used_by': vlans
|
|
}
|
|
return config
|
|
|
|
|
|
def get_fpga_config(context, iface):
|
|
"""
|
|
Returns an FPGA interface config dictionary.
|
|
"""
|
|
config = {}
|
|
config.update(get_n3000_config(context, iface))
|
|
return config
|
|
|
|
|
|
def get_common_network_config(context, iface, config, network=None, address=None):
|
|
"""
|
|
Augments a basic config dictionary with the attributes specific to an upper
|
|
layer interface (i.e., an interface that is used to terminate IP traffic).
|
|
"""
|
|
|
|
os_ifname = get_interface_os_ifname(context, iface)
|
|
if os_ifname == config['ifname']:
|
|
# post-up scripts do not work for aliases.
|
|
traffic_classifier = get_interface_traffic_classifier(context, iface)
|
|
if traffic_classifier:
|
|
fill_interface_config_option_operation(config['options'], IFACE_POST_UP_OP,
|
|
traffic_classifier)
|
|
|
|
method = get_interface_address_method(context, iface, network, address)
|
|
if method == STATIC_METHOD:
|
|
if address:
|
|
_set_address_netmask(address)
|
|
|
|
config['ipaddress'] = address['address']
|
|
config['netmask'] = address['netmask']
|
|
|
|
gateway = get_gateway_address(context, network, address)
|
|
if gateway:
|
|
config['options']['gateway'] = gateway
|
|
return config
|
|
|
|
|
|
def get_final_network_config(context, iface, config, network=None):
|
|
"""
|
|
Augments a basic config dictionary with the attribute that must be
|
|
appended to an attribute that is already configured (e.g. pre_up)
|
|
"""
|
|
# add duplex_direct specific network config
|
|
if context['system_mode'] == constants.SYSTEM_MODE_DUPLEX_DIRECT:
|
|
if is_disable_dad_required(iface, network):
|
|
config = get_duplex_direct_network_config(context, iface, config, network)
|
|
return config
|
|
|
|
|
|
def get_interface_network_config(context, iface, network=None, address=None):
|
|
"""
|
|
Builds a network_config resource dictionary for a given interface
|
|
"""
|
|
|
|
method = get_interface_address_method(context, iface, network, address)
|
|
|
|
# if and address is present but the address mode is not static, there's
|
|
# no need to generate a labeled config for the interface
|
|
if address is not None and method == MANUAL_METHOD:
|
|
return {}
|
|
|
|
os_ifname = get_interface_os_ifname(context, iface)
|
|
family = get_address_family(address)
|
|
|
|
# for now label all interfaces that have network_id, later we will
|
|
# set the definitive values
|
|
if network or address:
|
|
net_num = network.id if network else 0
|
|
addr_num = address.id if address else 0
|
|
ifname = "%s:%d-%d" % (os_ifname, net_num, addr_num)
|
|
else:
|
|
ifname = os_ifname
|
|
|
|
mtu = get_interface_mtu(context, iface)
|
|
config = get_basic_network_config(
|
|
ifname, method=method, family=family, mtu=mtu)
|
|
|
|
# Add options common to all top level interfaces
|
|
config = get_common_network_config(context, iface, config, network, address)
|
|
|
|
# ensure addresses have host scope when configured against the loopback
|
|
if os_ifname == LOOPBACK_IFNAME:
|
|
options = {'scope': 'host'}
|
|
config['options'].update(options)
|
|
|
|
# Add type specific options
|
|
if iface['iftype'] == constants.INTERFACE_TYPE_VLAN:
|
|
config = get_vlan_network_config(context, iface, config)
|
|
elif iface['iftype'] == constants.INTERFACE_TYPE_AE:
|
|
config = get_bond_network_config(context, iface, config)
|
|
else:
|
|
config = get_ethernet_network_config(context, iface, config)
|
|
|
|
# Add final options
|
|
config = get_final_network_config(context, iface, config, network)
|
|
|
|
if iface['iftype'] == constants.INTERFACE_TYPE_VLAN:
|
|
# When configuring a static IPv6 interface, CentOS' ifup tool uses 'ip link'
|
|
# command to set both IPv4 and IPv6 MTU.
|
|
# Debian's ifup tool instead uses sysctl to set only the IPv6 MTU. But this
|
|
# value gets reset to the underlying device's MTU soon after ifup set sets the
|
|
# interface state to up. So we need to set the MTU again during post-up.
|
|
# Using 'ip link' command here instead of sysctl, will set both IPv4 and IPv6
|
|
# MTU like in CentOS.
|
|
set_mtu = '/usr/sbin/ip link set dev {} mtu {}'.format(os_ifname, mtu)
|
|
fill_interface_config_option_operation(config['options'], IFACE_POST_UP_OP, set_mtu)
|
|
|
|
# disable ipv6 autoconfig
|
|
interface_op = IFACE_POST_UP_OP
|
|
if is_slave_interface(context, iface):
|
|
# ifupdown's ifup only runs pre-up for slave interfaces
|
|
interface_op = IFACE_PRE_UP_OP
|
|
|
|
autoconf_off = 'echo 0 > /proc/sys/net/ipv6/conf/{}/autoconf'.format(os_ifname)
|
|
fill_interface_config_option_operation(config['options'], interface_op, autoconf_off)
|
|
accept_ra_off = 'echo 0 > /proc/sys/net/ipv6/conf/{}/accept_ra'.format(os_ifname)
|
|
fill_interface_config_option_operation(config['options'], interface_op, accept_ra_off)
|
|
accept_redir_off = 'echo 0 > /proc/sys/net/ipv6/conf/{}/accept_redirects'.format(os_ifname)
|
|
fill_interface_config_option_operation(config['options'], interface_op, accept_redir_off)
|
|
|
|
network_type = network.type if network else None
|
|
|
|
# add the description field with the database ifname and networktype if available
|
|
config['options']['stx-description'] = f"ifname:{iface['ifname']},net:{network_type}"
|
|
|
|
return config
|
|
|
|
|
|
def _append_interface_config(iface_configs, iface_config):
|
|
if len(iface_config) > 0:
|
|
iface_configs.append(iface_config)
|
|
|
|
|
|
def get_interface_network_configs(context, iface, network=None):
|
|
"""
|
|
Builds a list of network_config resource dictionaries for a given interface,
|
|
each corresponding to an associated address, plus the base unlabeled config
|
|
"""
|
|
|
|
if iface['iftype'] == constants.INTERFACE_TYPE_VF:
|
|
# Only the parent SR-IOV interface needs a network config
|
|
return []
|
|
|
|
iface_configs = []
|
|
|
|
network_id = network.id if network else None
|
|
network_dict = context['interface_networks'][iface.ifname][network_id]
|
|
|
|
if network:
|
|
if len(network_dict['addresses']) == 0:
|
|
iface_config = get_interface_network_config(context, iface, network)
|
|
_append_interface_config(iface_configs, iface_config)
|
|
else:
|
|
# Most basic interface config, no network no address
|
|
iface_config = get_interface_network_config(context, iface)
|
|
_append_interface_config(iface_configs, iface_config)
|
|
|
|
for address in network_dict['addresses']:
|
|
iface_config = get_interface_network_config(context, iface, network, address)
|
|
_append_interface_config(iface_configs, iface_config)
|
|
|
|
return iface_configs
|
|
|
|
|
|
def get_address_family(address):
|
|
if address and IPAddress(address['address']).version == 6:
|
|
return 'inet6'
|
|
return 'inet'
|
|
|
|
|
|
def generate_network_config(context, hiera_config, iface):
|
|
"""
|
|
Produce the puppet network config resources necessary to configure the
|
|
given interface. In some cases this will emit a single network_config
|
|
resource, while in other cases it will emit multiple resources to create a
|
|
bridge, or to add additional route resources.
|
|
"""
|
|
|
|
os_ifname = get_interface_os_ifname(context, iface)
|
|
|
|
# Setup the default device configuration for the interface. This will be
|
|
# overridden if there is a specific network type configuration, otherwise
|
|
# it will act as the parent device for the aliases
|
|
networks = context['interface_networks'][iface.ifname]
|
|
for network_dict in networks.values():
|
|
net_configs = get_interface_network_configs(context, iface, network_dict['network'])
|
|
for net_config in net_configs:
|
|
hiera_config[NETWORK_CONFIG_RESOURCE][net_config['ifname']] = \
|
|
format_network_config(net_config)
|
|
|
|
# Add complementary puppet resource definitions (if needed)
|
|
for route in get_interface_routes(context, iface):
|
|
route_config = get_route_config(route, os_ifname)
|
|
hiera_config[ROUTE_CONFIG_RESOURCE].update({
|
|
route_config['name']: route_config
|
|
})
|
|
|
|
interface_class = iface['ifclass']
|
|
if interface_class == constants.INTERFACE_CLASS_PCI_SRIOV:
|
|
sriov_config = get_sriov_config(context, iface)
|
|
if sriov_config:
|
|
hiera_config[SRIOV_CONFIG_RESOURCE].update({
|
|
sriov_config['ifname']: format_sriov_config(sriov_config)
|
|
})
|
|
|
|
fpga_config = get_fpga_config(context, iface)
|
|
if fpga_config:
|
|
hiera_config[FPGA_CONFIG_RESOURCE].update({
|
|
fpga_config['ifname']: format_fpga_config(fpga_config)
|
|
})
|
|
|
|
|
|
def find_network_id_by_networktype(context, networktype):
|
|
network = context['networks'].get(networktype, None)
|
|
if network:
|
|
return network.id
|
|
return None
|
|
|
|
|
|
def find_interface_by_type(context, networktype):
|
|
"""
|
|
Lookup an interface based on networktype. This is only intended for
|
|
platform interfaces that have only 1 such interface per node (i.e., oam,
|
|
mgmt, cluster-host, pxeboot, bmc).
|
|
"""
|
|
for ifname, iface in six.iteritems(context['interfaces']):
|
|
for net_type in iface.networktypelist:
|
|
if networktype == net_type:
|
|
return iface
|
|
|
|
|
|
def find_sriov_interfaces_by_driver(context, driver):
|
|
"""
|
|
Lookup all interfaces based on port driver.
|
|
To be noted that this is only used for IFTYPE_ETHERNET
|
|
"""
|
|
ifaces = []
|
|
for ifname, iface in six.iteritems(context['interfaces']):
|
|
if iface['iftype'] != constants.INTERFACE_TYPE_ETHERNET:
|
|
continue
|
|
port = get_interface_port(context, iface)
|
|
if (port['driver'] == driver and
|
|
iface['ifclass'] == constants.INTERFACE_CLASS_PCI_SRIOV):
|
|
ifaces.append(iface)
|
|
return ifaces
|
|
|
|
|
|
def get_ptp_interfaces(context):
|
|
"""
|
|
Lookup interfaces with a ptp_role specified
|
|
"""
|
|
ifaces = []
|
|
for ifname, iface in six.iteritems(context['interfaces']):
|
|
if iface['ptp_role'] != constants.INTERFACE_PTP_ROLE_NONE:
|
|
ifaces.append(iface)
|
|
return ifaces
|
|
|
|
|
|
def interface_sort_key(iface):
|
|
"""
|
|
Sort interfaces by interface type placing ethernet interfaces ahead of
|
|
aggregated ethernet and vlan interfaces, with pci interfaces last.
|
|
"""
|
|
if iface['iftype'] == constants.INTERFACE_TYPE_VIRTUAL:
|
|
return 0, iface['ifname']
|
|
elif iface['iftype'] == constants.INTERFACE_TYPE_ETHERNET and not is_pci_interface(iface):
|
|
return 1, iface['ifname']
|
|
elif iface['iftype'] == constants.INTERFACE_TYPE_AE:
|
|
return 2, iface['ifname']
|
|
elif iface['iftype'] == constants.INTERFACE_TYPE_VLAN:
|
|
return 3, iface['ifname']
|
|
elif is_pci_interface(iface):
|
|
return 4, iface['ifname']
|
|
else:
|
|
msg = _('Invalid iftype: %s') % iface['iftype']
|
|
raise exception.SysinvException(msg)
|
|
|
|
|
|
def generate_interface_configs(context, config, db_api):
|
|
"""
|
|
Generate the puppet resource for each of the interface and route config
|
|
resources.
|
|
"""
|
|
to_config_iface = list()
|
|
for iface in sorted(context['interfaces'].values(),
|
|
key=interface_sort_key):
|
|
if needs_interface_config(context, iface):
|
|
generate_network_config(context, config, iface)
|
|
to_config_iface.append(iface)
|
|
|
|
generate_unassigned_pxeboot_intf_config(context, config, db_api,
|
|
to_config_iface)
|
|
|
|
# all interfaces were generated with label. Now run a logic to adjust
|
|
# the interfaces that actually require labeling
|
|
process_interface_labels(config, context)
|
|
|
|
|
|
def process_interface_labels(config, context):
|
|
"""
|
|
Adjust interface labeling according to ifupdown package rules and StarlingX
|
|
requirements
|
|
"""
|
|
# This rules are a result of using Debian's ifupdown package
|
|
#
|
|
# Rules for label adjustment:
|
|
# 1) if the interface have just one label:
|
|
# - move the content of label to interface
|
|
# 2) if the interface have more that one label
|
|
# - if the family is inet
|
|
# - just keep the labeling
|
|
# - DHCPv4 can use a labeled interface (pxebbot for now is only IPv4),
|
|
# that was not the case when using CentOS
|
|
# - if the family is inet6
|
|
# - interface needs to contain the static address that will be used as
|
|
# source address (non-deprecated)
|
|
# - in inet6 labeled interfaces mark the static address as deprecated
|
|
# - a post-up operation will be added to remove this flag in one
|
|
# of the interfaces
|
|
# - the selected label interface will follow the precedence
|
|
# - mgmt
|
|
# - admin
|
|
# - cluster-host
|
|
# - pxeboot
|
|
# - storage
|
|
# - DHCPv6 cannot use label (pxebbot for now is only IPv4, so we don't
|
|
# have a use case for now for platform interfaces), move the label
|
|
# to interface
|
|
|
|
label_map = dict()
|
|
for net_cfg_key in config[NETWORK_CONFIG_RESOURCE].keys():
|
|
base_interface = net_cfg_key.split(':')[0]
|
|
if base_interface not in label_map.keys():
|
|
label_map.update({base_interface: dict()})
|
|
if ":" in net_cfg_key:
|
|
label_map[base_interface].update({
|
|
net_cfg_key: config[NETWORK_CONFIG_RESOURCE][net_cfg_key]})
|
|
|
|
for intf in label_map.keys():
|
|
if intf == 'lo':
|
|
# no need to change the loopback
|
|
continue
|
|
|
|
if len(label_map[intf]) == 1:
|
|
label = next(iter(label_map[intf]))
|
|
merge_interface_operations(config, intf, label)
|
|
# replace the base interface with the labeled one
|
|
config[NETWORK_CONFIG_RESOURCE][intf] = config[NETWORK_CONFIG_RESOURCE][label]
|
|
del config[NETWORK_CONFIG_RESOURCE][label]
|
|
|
|
elif len(label_map[intf]) > 1:
|
|
|
|
# process main ipv6 address
|
|
for label in label_map[intf].keys():
|
|
intf_data = label_map[intf][label]
|
|
if (intf_data['family'] == 'inet6') and (intf_data['method'] == 'static'):
|
|
name_net = intf_data['options']['stx-description'].split(',')
|
|
ifname = (name_net[0].split(":"))[1]
|
|
networktypelist = context['interfaces'][ifname].networktypelist
|
|
undeprecate = "ip -6 addr replace" + \
|
|
f" {intf_data['ipaddress']}/{intf_data['netmask']}" + \
|
|
f" dev {intf} preferred_lft forever"
|
|
if constants.NETWORK_TYPE_MGMT in networktypelist:
|
|
fill_interface_config_option_operation(intf_data['options'],
|
|
IFACE_POST_UP_OP, undeprecate)
|
|
break
|
|
elif constants.NETWORK_TYPE_ADMIN in networktypelist:
|
|
fill_interface_config_option_operation(intf_data['options'],
|
|
IFACE_POST_UP_OP, undeprecate)
|
|
break
|
|
elif constants.NETWORK_TYPE_CLUSTER_HOST in networktypelist:
|
|
fill_interface_config_option_operation(intf_data['options'],
|
|
IFACE_POST_UP_OP, undeprecate)
|
|
break
|
|
elif constants.NETWORK_TYPE_PXEBOOT in networktypelist:
|
|
fill_interface_config_option_operation(intf_data['options'],
|
|
IFACE_POST_UP_OP, undeprecate)
|
|
break
|
|
elif constants.NETWORK_TYPE_STORAGE in networktypelist:
|
|
fill_interface_config_option_operation(intf_data['options'],
|
|
IFACE_POST_UP_OP, undeprecate)
|
|
break
|
|
|
|
# process DHCPv6, needs to be in the base interface
|
|
for label in label_map[intf].keys():
|
|
intf_data = label_map[intf][label]
|
|
if (intf_data['family'] == 'inet6') and (intf_data['method'] == 'dhcp'):
|
|
merge_interface_operations(config, intf, label)
|
|
config[NETWORK_CONFIG_RESOURCE][intf] = config[NETWORK_CONFIG_RESOURCE][label]
|
|
del config[NETWORK_CONFIG_RESOURCE][label]
|
|
break
|
|
|
|
|
|
def merge_interface_operations(config, intf, label):
|
|
"""
|
|
Collect the operations in the labeled interface and merge with the existing operations
|
|
in the base interface, update the result in the labeled interface config
|
|
"""
|
|
label_intf = config[NETWORK_CONFIG_RESOURCE][label]
|
|
base_intf = config[NETWORK_CONFIG_RESOURCE][intf]
|
|
# merge operations from base and label
|
|
for oper in [IFACE_PRE_UP_OP, IFACE_UP_OP, IFACE_POST_UP_OP,
|
|
IFACE_PRE_DOWN_OP, IFACE_DOWN_OP, IFACE_POST_DOWN_OP]:
|
|
opername = get_intf_op_name(oper)
|
|
|
|
if opername in base_intf['options']:
|
|
base_oper = base_intf['options'][opername].split("; ")
|
|
if opername in label_intf['options']:
|
|
label_oper = label_intf['options'][opername].split("; ")
|
|
for cmd in base_oper:
|
|
if cmd not in label_oper:
|
|
fill_interface_config_option_operation(label_intf['options'], oper, cmd)
|
|
else:
|
|
for cmd in base_oper:
|
|
fill_interface_config_option_operation(label_intf['options'], oper, cmd)
|
|
|
|
|
|
def generate_unassigned_pxeboot_intf_config(context, config, db_api,
|
|
to_config_iface):
|
|
|
|
"""
|
|
If the pxeboot network isn't explicitly assigned to an interface, it is necessary
|
|
to add the network config in the same interface used by the management network
|
|
"""
|
|
platform_untag_networks = list()
|
|
mgmt_intf = None
|
|
for iface in to_config_iface:
|
|
# get list of platform networks are untagged (no vlan)
|
|
if (iface['ifclass'] == constants.INTERFACE_CLASS_PLATFORM
|
|
and (iface['iftype'] == constants.INTERFACE_TYPE_ETHERNET
|
|
or iface['iftype'] == constants.INTERFACE_TYPE_AE)) \
|
|
and iface['iftype'] != constants.INTERFACE_TYPE_VIRTUAL \
|
|
and iface['iftype'] != constants.INTERFACE_TYPE_VF:
|
|
try:
|
|
intf_networks = db_api.interface_network_get_by_interface(iface['id'])
|
|
for intf_net in intf_networks:
|
|
network = db_api.network_get(intf_net.network_uuid)
|
|
if network:
|
|
platform_untag_networks.append(network)
|
|
if intf_net['network_type'] == constants.NETWORK_TYPE_MGMT:
|
|
mgmt_intf = iface
|
|
except Exception as ex:
|
|
LOG.info(f"DB query failed with {ex}")
|
|
LOG.exception(ex)
|
|
|
|
# assigning pxeboot network to an interface is not mandatory if the management
|
|
# interface is untagged. If that is the case prepare a configuration for pxeboot
|
|
# in the same interface used by the management interface.
|
|
is_pxeboot_present = [network.type for network in platform_untag_networks
|
|
if network.type == constants.NETWORK_TYPE_PXEBOOT]
|
|
is_mgmt_present = [network.type for network in platform_untag_networks
|
|
if network.type == constants.NETWORK_TYPE_MGMT]
|
|
if not is_pxeboot_present and is_mgmt_present:
|
|
if mgmt_intf:
|
|
LOG.info(f"add pxeboot network config in {mgmt_intf.ifname} ")
|
|
network = context['networks'][constants.NETWORK_TYPE_PXEBOOT]
|
|
# Setup the default device configuration for the interface. This will be
|
|
# overridden if there is a specific network type configuration, otherwise
|
|
# it will act as the parent device for the aliases
|
|
mgmt_intf.networktypelist.append(constants.NETWORK_TYPE_PXEBOOT)
|
|
address_name = None
|
|
if context['hostname'] == constants.CONTROLLER_0_HOSTNAME:
|
|
address_name = utils.format_address_name(constants.CONTROLLER_0_HOSTNAME,
|
|
constants.NETWORK_TYPE_PXEBOOT)
|
|
elif context['hostname'] == constants.CONTROLLER_1_HOSTNAME:
|
|
address_name = utils.format_address_name(constants.CONTROLLER_1_HOSTNAME,
|
|
constants.NETWORK_TYPE_PXEBOOT)
|
|
address = None
|
|
if address_name:
|
|
address = utils.get_primary_address_by_name(db_api, address_name,
|
|
constants.NETWORK_TYPE_PXEBOOT)
|
|
|
|
net_config = get_interface_network_config(context, mgmt_intf, network, address)
|
|
|
|
if net_config:
|
|
config[NETWORK_CONFIG_RESOURCE].update({
|
|
net_config['ifname']: format_network_config(net_config)
|
|
})
|
|
|
|
|
|
def get_address_config(context, iface, addresses):
|
|
ifname = get_interface_os_ifname(context, iface)
|
|
|
|
if constants.DUAL_STACK_COMPATIBILITY_MODE:
|
|
address = addresses[0]
|
|
return {
|
|
'ifname': ifname,
|
|
'address': str(address.address) + '/' + str(address.prefix)
|
|
}
|
|
else:
|
|
address_list = []
|
|
for address in addresses:
|
|
address_list.append(str(address.address) + '/' + str(address.prefix))
|
|
return {
|
|
'ifname': ifname,
|
|
'addresses': address_list,
|
|
}
|
|
|
|
|
|
def generate_address_configs(context, config):
|
|
"""
|
|
Generate the puppet resource for each of the floating IP addresses
|
|
"""
|
|
for networktype, addresses in six.iteritems(context['floatingips']):
|
|
iface = find_interface_by_type(context, networktype)
|
|
if iface:
|
|
address_config = get_address_config(context, iface, addresses)
|
|
config[ADDRESS_CONFIG_RESOURCE].update({
|
|
networktype: address_config
|
|
})
|
|
elif networktype == constants.NETWORK_TYPE_PXEBOOT:
|
|
# Fallback PXE boot address against management interface
|
|
iface = find_interface_by_type(context,
|
|
constants.NETWORK_TYPE_MGMT)
|
|
if iface:
|
|
address_config = get_address_config(context, iface, addresses)
|
|
config[ADDRESS_CONFIG_RESOURCE].update({
|
|
networktype: address_config
|
|
})
|
|
elif networktype == constants.NETWORK_TYPE_CLUSTER_HOST:
|
|
# Fallback cluster host address against management interface
|
|
iface = find_interface_by_type(context,
|
|
constants.NETWORK_TYPE_CLUSTER_HOST)
|
|
if iface:
|
|
address_config = get_address_config(context, iface, addresses)
|
|
config[ADDRESS_CONFIG_RESOURCE].update({
|
|
networktype: address_config
|
|
})
|
|
|
|
|
|
def generate_data_iface_list_config(context, config):
|
|
"""
|
|
Generate the puppet resource for data-network iface name.
|
|
"""
|
|
for iface in context['interfaces'].values():
|
|
if is_data_interface(context, iface):
|
|
ifname = get_interface_os_ifname(context, iface)
|
|
config[DATA_IFACE_LIST_RESOURCE].append(ifname)
|
|
|
|
|
|
def generate_loopback_config(config):
|
|
"""
|
|
Generate the loopback network config resource so that the loopback
|
|
interface is automatically enabled on reboots.
|
|
"""
|
|
network_config = get_basic_network_config(LOOPBACK_IFNAME,
|
|
method=LOOPBACK_METHOD)
|
|
config[NETWORK_CONFIG_RESOURCE].update({
|
|
LOOPBACK_IFNAME: format_network_config(network_config)
|
|
})
|
|
|
|
|
|
def format_network_config(config):
|
|
"""
|
|
Converts a network_config resource dictionary to the equivalent puppet
|
|
resource definition parameters.
|
|
"""
|
|
network_config = copy.copy(config)
|
|
del network_config['ifname']
|
|
return network_config
|
|
|
|
|
|
def format_sriov_config(config):
|
|
"""
|
|
Converts a sriov_config resource dictionary to the equivalent puppet
|
|
resource definition parameters.
|
|
"""
|
|
sriov_config = copy.copy(config)
|
|
del sriov_config['ifname']
|
|
return sriov_config
|
|
|
|
|
|
def format_fpga_config(config):
|
|
"""
|
|
Converts a fpga_config resource dictionary to the equivalent puppet
|
|
resource definition parameters.
|
|
"""
|
|
fpga_config = copy.copy(config)
|
|
del fpga_config['ifname']
|
|
return fpga_config
|
|
|
|
|
|
def fill_interface_config_option_operation(options, operation, command):
|
|
"""
|
|
Join new command to previous commands on the same operation
|
|
"""
|
|
if_op = {IFACE_UP_OP: 'up',
|
|
IFACE_PRE_UP_OP: 'pre-up',
|
|
IFACE_POST_UP_OP: 'post-up',
|
|
IFACE_DOWN_OP: 'down',
|
|
IFACE_PRE_DOWN_OP: 'pre-down',
|
|
IFACE_POST_DOWN_OP: 'post-down'}
|
|
|
|
if operation in if_op.keys():
|
|
if if_op[operation] in options.keys():
|
|
previous_command = options[if_op[operation]]
|
|
options[if_op[operation]] = "{}; {}".format(previous_command,
|
|
command)
|
|
else:
|
|
options[if_op[operation]] = command
|
|
|
|
|
|
def get_intf_op_name(operation):
|
|
if_op = {IFACE_UP_OP: 'up',
|
|
IFACE_PRE_UP_OP: 'pre-up',
|
|
IFACE_POST_UP_OP: 'post-up',
|
|
IFACE_DOWN_OP: 'down',
|
|
IFACE_PRE_DOWN_OP: 'pre-down',
|
|
IFACE_POST_DOWN_OP: 'post-down'}
|
|
|
|
return if_op[operation]
|