1925 lines
85 KiB
Python
1925 lines
85 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
# Copyright 2014-2015 Red Hat, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import glob
|
|
import itertools
|
|
import logging
|
|
import netaddr
|
|
import os
|
|
import re
|
|
|
|
import os_net_config
|
|
from os_net_config import common
|
|
from os_net_config import objects
|
|
from os_net_config import utils
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Import the raw NetConfig object so we can call its methods
|
|
netconfig = os_net_config.NetConfig()
|
|
|
|
_ROUTE_TABLE_DEFAULT = """# reserved values
|
|
#
|
|
255\tlocal
|
|
254\tmain
|
|
253\tdefault
|
|
0\tunspec
|
|
#
|
|
# local
|
|
#
|
|
#1\tinr.ruhep\n"""
|
|
|
|
|
|
def ifcfg_config_path(name):
|
|
return "/etc/sysconfig/network-scripts/ifcfg-%s" % name
|
|
|
|
|
|
def remove_ifcfg_config(ifname):
|
|
if re.match(r'[\w-]+$', ifname):
|
|
ifcfg_file = ifcfg_config_path(ifname)
|
|
if os.path.exists(ifcfg_file):
|
|
os.remove(ifcfg_file)
|
|
|
|
|
|
# NOTE(dprince): added here for testability
|
|
def bridge_config_path(name):
|
|
return ifcfg_config_path(name)
|
|
|
|
|
|
def ivs_config_path():
|
|
return "/etc/sysconfig/ivs"
|
|
|
|
|
|
def nfvswitch_config_path():
|
|
return "/etc/sysconfig/nfvswitch"
|
|
|
|
|
|
def vpp_config_path():
|
|
return "/etc/vpp/startup.conf"
|
|
|
|
|
|
def route_config_path(name):
|
|
return "/etc/sysconfig/network-scripts/route-%s" % name
|
|
|
|
|
|
def route6_config_path(name):
|
|
return "/etc/sysconfig/network-scripts/route6-%s" % name
|
|
|
|
|
|
def route_rule_config_path(name):
|
|
return "/etc/sysconfig/network-scripts/rule-%s" % name
|
|
|
|
|
|
def route_table_config_path():
|
|
return "/etc/iproute2/rt_tables"
|
|
|
|
|
|
def cleanup_pattern():
|
|
return "/etc/sysconfig/network-scripts/ifcfg-*"
|
|
|
|
|
|
def dhclient_path():
|
|
if os.path.exists("/usr/sbin/dhclient"):
|
|
return "/usr/sbin/dhclient"
|
|
elif os.path.exists("/sbin/dhclient"):
|
|
return "/sbin/dhclient"
|
|
else:
|
|
raise RuntimeError("Could not find dhclient")
|
|
|
|
|
|
def stop_dhclient_process(interface):
|
|
"""Stop a DHCP process when no longer needed.
|
|
|
|
This method exists so that it may be stubbed out for unit tests.
|
|
:param interface: The interface on which to stop dhclient.
|
|
"""
|
|
pid_file = '/var/run/dhclient-%s.pid' % (interface)
|
|
try:
|
|
dhclient = dhclient_path()
|
|
except RuntimeError as err:
|
|
logger.info('Exception when stopping dhclient: %s' % err)
|
|
return
|
|
|
|
if os.path.exists(pid_file):
|
|
msg = 'Stopping %s on interface %s' % (dhclient, interface)
|
|
netconfig.execute(msg, dhclient, '-r', '-pf',
|
|
pid_file, interface)
|
|
try:
|
|
os.unlink(pid_file)
|
|
except OSError as err:
|
|
logger.error('Could not remove dhclient pid file \'%s\': %s' %
|
|
(pid_file, err))
|
|
|
|
|
|
class IfcfgNetConfig(os_net_config.NetConfig):
|
|
"""Configure network interfaces using the ifcfg format."""
|
|
|
|
def __init__(self, noop=False, root_dir=''):
|
|
super(IfcfgNetConfig, self).__init__(noop, root_dir)
|
|
self.interface_data = {}
|
|
self.ivsinterface_data = {}
|
|
self.nfvswitch_intiface_data = {}
|
|
self.nfvswitch_options = None
|
|
self.vlan_data = {}
|
|
self.ib_childs_data = {}
|
|
self.route_data = {}
|
|
self.route6_data = {}
|
|
self.route_table_data = {}
|
|
self.rule_data = {}
|
|
self.bridge_data = {}
|
|
self.linuxbridge_data = {}
|
|
self.linuxbond_data = {}
|
|
self.ib_interface_data = {}
|
|
self.linuxteam_data = {}
|
|
self.vpp_interface_data = {}
|
|
self.vpp_bond_data = {}
|
|
self.member_names = {}
|
|
self.renamed_interfaces = {}
|
|
self.bond_primary_ifaces = {}
|
|
logger.info('Ifcfg net config provider created.')
|
|
|
|
def parse_ifcfg(self, ifcfg_data):
|
|
"""Break out the key/value pairs from ifcfg_data
|
|
|
|
Return the keys and values without quotes.
|
|
"""
|
|
ifcfg_values = {}
|
|
for line in ifcfg_data.split("\n"):
|
|
if not line.startswith("#") and line.find("=") > 0:
|
|
k, v = line.split("=", 1)
|
|
ifcfg_values[k] = v.strip("\"'")
|
|
return ifcfg_values
|
|
|
|
def parse_ifcfg_routes(self, ifcfg_data):
|
|
"""Break out the individual routes from an ifcfg route file."""
|
|
routes = []
|
|
for line in ifcfg_data.split("\n"):
|
|
if not line.startswith("#"):
|
|
routes.append(line)
|
|
return routes
|
|
|
|
def parse_ifcfg_rules(self, ifcfg_data):
|
|
"""Break out the individual rules from an ifcfg rule file."""
|
|
rules = []
|
|
for line in ifcfg_data.split("\n"):
|
|
if not line.startswith("#"):
|
|
rules.append(line)
|
|
return rules
|
|
|
|
def enumerate_ifcfg_changes(self, ifcfg_data_old, ifcfg_data_new):
|
|
"""Determine which values are added/modified/removed
|
|
|
|
:param ifcfg_data_old: content of existing ifcfg file
|
|
:param ifcfg_data_new: content of replacement ifcfg file
|
|
:return: dict of changed values and states (added, removed, modified)
|
|
"""
|
|
|
|
changed_values = {}
|
|
for key in ifcfg_data_old:
|
|
if key in ifcfg_data_new:
|
|
if ifcfg_data_old[key].upper() != ifcfg_data_new[key].upper():
|
|
changed_values[key] = "modified"
|
|
else:
|
|
changed_values[key] = "removed"
|
|
for key in ifcfg_data_new:
|
|
if key not in ifcfg_data_old:
|
|
changed_values[key] = "added"
|
|
return changed_values
|
|
|
|
def enumerate_ifcfg_route_changes(self, old_routes, new_routes):
|
|
"""Determine which routes are added or removed.
|
|
|
|
:param file_values: contents of existing interface route file
|
|
:param data_values: contents of replacement interface route file
|
|
:return: list of tuples representing changes (route, state), where
|
|
state is one of added or removed
|
|
"""
|
|
|
|
route_changes = []
|
|
for route in old_routes:
|
|
if route not in new_routes:
|
|
route_changes.append((route, 'removed'))
|
|
for route in new_routes:
|
|
if route not in old_routes:
|
|
route_changes.append((route, 'added'))
|
|
return route_changes
|
|
|
|
def enumerate_ifcfg_rule_changes(self, old_rules, new_rules):
|
|
"""Determine which routes are added or removed.
|
|
|
|
:param file_values: contents of existing interface route rule file
|
|
:param data_values: contents of replacement interface route rule file
|
|
:return: list of tuples representing changes (rule, state), where
|
|
state is one of added or removed
|
|
"""
|
|
|
|
rule_changes = []
|
|
for rule in old_rules:
|
|
if rule not in new_rules:
|
|
rule_changes.append((rule, 'removed'))
|
|
for rule in new_rules:
|
|
if rule not in old_rules:
|
|
rule_changes.append((rule, 'added'))
|
|
return rule_changes
|
|
|
|
def ifcfg_requires_restart(self, filename, new_data):
|
|
"""Determine if changes to the ifcfg file require a restart to apply.
|
|
|
|
Simple changes like IP, MTU, and routes can be directly applied
|
|
without restarting the interface.
|
|
|
|
:param filename: The ifcfg-<int> filename.
|
|
:type filename: string
|
|
:param new_data: The data for the new ifcfg-<int> file.
|
|
:type new_data: string
|
|
:returns: boolean value for whether a restart is required
|
|
"""
|
|
|
|
file_data = common.get_file_data(filename)
|
|
logger.debug("Original ifcfg file:\n%s" % file_data)
|
|
logger.debug("New ifcfg file:\n%s" % new_data)
|
|
file_values = self.parse_ifcfg(file_data)
|
|
new_values = self.parse_ifcfg(new_data)
|
|
restart_required = False
|
|
# Certain changes can be applied without restarting the interface
|
|
permitted_changes = [
|
|
"IPADDR",
|
|
"NETMASK",
|
|
"MTU",
|
|
"ONBOOT"
|
|
]
|
|
# Check whether any of the changes require restart
|
|
for change in self.enumerate_ifcfg_changes(file_values, new_values):
|
|
if change not in permitted_changes:
|
|
# Moving to DHCP requires restarting interface
|
|
if change in ["BOOTPROTO", "OVSBOOTPROTO"]:
|
|
if change in new_values:
|
|
if (new_values[change].upper() == "DHCP"):
|
|
restart_required = True
|
|
logger.debug(
|
|
"DHCP on %s requires restart" % change)
|
|
else:
|
|
restart_required = True
|
|
if not restart_required:
|
|
logger.debug("Changes do not require restart")
|
|
return restart_required
|
|
|
|
def iproute2_apply_commands(self, device_name, filename, data):
|
|
"""Return list of commands needed to implement changes.
|
|
|
|
Given ifcfg data for an interface, return commands required to
|
|
apply the configuration using 'ip' commands.
|
|
|
|
:param device_name: The name of the int, bridge, or bond
|
|
:type device_name: string
|
|
:param filename: The ifcfg-<int> filename.
|
|
:type filename: string
|
|
:param data: The data for the new ifcfg-<int> file.
|
|
:type data: string
|
|
:returns: commands (commands to be run)
|
|
"""
|
|
|
|
previous_cfg = common.get_file_data(filename)
|
|
file_values = self.parse_ifcfg(previous_cfg)
|
|
data_values = self.parse_ifcfg(data)
|
|
logger.debug("File values:\n%s" % file_values)
|
|
logger.debug("Data values:\n%s" % data_values)
|
|
changes = self.enumerate_ifcfg_changes(file_values, data_values)
|
|
commands = []
|
|
new_cidr = 0
|
|
old_cidr = 0
|
|
# Convert dot notation netmask to CIDR length notation
|
|
if "NETMASK" in file_values:
|
|
netmask = file_values["NETMASK"]
|
|
old_cidr = netaddr.IPAddress(netmask).netmask_bits()
|
|
if "NETMASK" in data_values:
|
|
netmask = data_values["NETMASK"]
|
|
new_cidr = netaddr.IPAddress(netmask).netmask_bits()
|
|
if "IPADDR" in changes:
|
|
if changes["IPADDR"] == "removed" or changes[
|
|
"IPADDR"] == "modified":
|
|
if old_cidr:
|
|
commands.append("addr del %s/%s dev %s" %
|
|
(file_values["IPADDR"], old_cidr,
|
|
device_name))
|
|
else:
|
|
# Cannot remove old IP specifically if netmask not known
|
|
commands.append("addr flush dev %s" % device_name)
|
|
if changes["IPADDR"] == "added" or changes["IPADDR"] == "modified":
|
|
commands.insert(0, "addr add %s/%s dev %s" %
|
|
(data_values["IPADDR"], new_cidr, device_name))
|
|
if "MTU" in changes:
|
|
if changes["MTU"] == "added" or changes["MTU"] == "modified":
|
|
commands.append("link set dev %s mtu %s" %
|
|
(device_name, data_values["MTU"]))
|
|
elif changes["MTU"] == "removed":
|
|
commands.append("link set dev %s mtu 1500" % device_name)
|
|
return commands
|
|
|
|
def iproute2_route_commands(self, filename, data):
|
|
"""Return a list of commands for 'ip route' to modify routing table.
|
|
|
|
The list of commands is generated by comparing the old and new
|
|
configs, and calculating which routes need to be added and which
|
|
need to be removed.
|
|
|
|
:param filename: path to the original interface route file
|
|
:param data: data that is to be written to new route file
|
|
:return: list of commands to feed to 'ip' to reconfigure routes
|
|
"""
|
|
|
|
file_values = self.parse_ifcfg_routes(common.get_file_data(filename))
|
|
data_values = self.parse_ifcfg_routes(data)
|
|
route_changes = self.enumerate_ifcfg_route_changes(file_values,
|
|
data_values)
|
|
commands = []
|
|
|
|
for route in route_changes:
|
|
if route[1] == 'removed':
|
|
commands.append('route del ' + route[0])
|
|
elif route[1] == 'added':
|
|
commands.append('route add ' + route[0])
|
|
return commands
|
|
|
|
def iproute2_rule_commands(self, filename, data):
|
|
"""Return a list of commands for 'ip route' to modify routing rules.
|
|
|
|
The list of commands is generated by comparing the old and new
|
|
configs, and calculating which rules need to be added and which
|
|
need to be removed.
|
|
|
|
:param filename: path to the original interface route rule file
|
|
:param data: data that is to be written to new route rule file
|
|
:return: list of commands to feed to 'ip' to reconfigure route rules
|
|
"""
|
|
|
|
file_values = self.parse_ifcfg_rules(common.get_file_data(filename))
|
|
data_values = self.parse_ifcfg_rules(data)
|
|
rule_changes = self.enumerate_ifcfg_rule_changes(file_values,
|
|
data_values)
|
|
commands = []
|
|
|
|
for rule in rule_changes:
|
|
if rule[1] == 'removed':
|
|
commands.append('rule del ' + rule[0])
|
|
elif rule[1] == 'added':
|
|
commands.append('rule add ' + rule[0])
|
|
return commands
|
|
|
|
def child_members(self, name):
|
|
children = set()
|
|
try:
|
|
for member in self.member_names[name]:
|
|
children.add(member)
|
|
children.update(self.child_members(member))
|
|
except KeyError:
|
|
pass
|
|
return children
|
|
|
|
def _add_common(self, base_opt):
|
|
|
|
ovs_extra = []
|
|
data = "# This file is autogenerated by os-net-config\n"
|
|
data += "DEVICE=%s\n" % base_opt.name
|
|
if base_opt.onboot:
|
|
data += "ONBOOT=yes\n"
|
|
else:
|
|
data += "ONBOOT=no\n"
|
|
if isinstance(base_opt, objects.Interface) and base_opt.hotplug:
|
|
data += "HOTPLUG=yes\n"
|
|
else:
|
|
data += "HOTPLUG=no\n"
|
|
if base_opt.nm_controlled:
|
|
data += "NM_CONTROLLED=yes\n"
|
|
else:
|
|
data += "NM_CONTROLLED=no\n"
|
|
if not base_opt.dns_servers and not base_opt.use_dhcp:
|
|
data += "PEERDNS=no\n"
|
|
if isinstance(base_opt, objects.Vlan):
|
|
if not base_opt.ovs_port:
|
|
# vlans on OVS bridges are internal ports (no device, etc)
|
|
data += "VLAN=yes\n"
|
|
if base_opt.device:
|
|
data += "PHYSDEV=%s\n" % base_opt.device
|
|
elif base_opt.linux_bond_name:
|
|
data += "PHYSDEV=%s\n" % base_opt.linux_bond_name
|
|
else:
|
|
if base_opt.ovs_options:
|
|
data += "OVS_OPTIONS=\"%s\"\n" % base_opt.ovs_options
|
|
ovs_extra.extend(base_opt.ovs_extra)
|
|
elif isinstance(base_opt, objects.IvsInterface):
|
|
data += "TYPE=IVSIntPort\n"
|
|
elif isinstance(base_opt, objects.NfvswitchInternal):
|
|
data += "TYPE=NFVSWITCHIntPort\n"
|
|
elif isinstance(base_opt, objects.IbInterface):
|
|
data += "TYPE=Infiniband\n"
|
|
elif isinstance(base_opt, objects.IbChildInterface):
|
|
data += "TYPE=Infiniband\n"
|
|
data += "PKEY=yes\n"
|
|
data += "PHYSDEV=%s\n" % base_opt.parent
|
|
data += "PKEY_ID=%d\n" % base_opt.pkey_id
|
|
elif re.match(r'\w+\.\d+$', base_opt.name):
|
|
data += "VLAN=yes\n"
|
|
elif isinstance(base_opt, objects.Interface):
|
|
if base_opt.linkdelay:
|
|
data += "LINKDELAY=%s\n" % base_opt.linkdelay
|
|
if base_opt.linux_bond_name:
|
|
data += "MASTER=%s\n" % base_opt.linux_bond_name
|
|
data += "SLAVE=yes\n"
|
|
if base_opt.linux_team_name:
|
|
data += "TEAM_MASTER=%s\n" % base_opt.linux_team_name
|
|
if base_opt.primary:
|
|
data += "TEAM_PORT_CONFIG='{\"prio\": 100}'\n"
|
|
if base_opt.ivs_bridge_name:
|
|
data += "DEVICETYPE=ivs\n"
|
|
data += "IVS_BRIDGE=%s\n" % base_opt.ivs_bridge_name
|
|
if base_opt.nfvswitch_bridge_name:
|
|
data += "DEVICETYPE=nfvswitch\n"
|
|
data += "NFVSWITCH_BRIDGE=%s\n" % base_opt.nfvswitch_bridge_name
|
|
if base_opt.ovs_port:
|
|
if not isinstance(base_opt, objects.LinuxTeam):
|
|
data += "DEVICETYPE=ovs\n"
|
|
if base_opt.bridge_name:
|
|
if isinstance(base_opt, objects.Vlan):
|
|
data += "TYPE=OVSIntPort\n"
|
|
data += "OVS_BRIDGE=%s\n" % base_opt.bridge_name
|
|
data += "OVS_OPTIONS=\"tag=%s\"\n" % base_opt.vlan_id
|
|
else:
|
|
data += "TYPE=OVSPort\n"
|
|
data += "OVS_BRIDGE=%s\n" % base_opt.bridge_name
|
|
if base_opt.linux_bridge_name:
|
|
data += "BRIDGE=%s\n" % base_opt.linux_bridge_name
|
|
if isinstance(base_opt, objects.OvsBridge):
|
|
data += "DEVICETYPE=ovs\n"
|
|
data += "TYPE=OVSBridge\n"
|
|
if base_opt.use_dhcp or base_opt.use_dhcpv6:
|
|
data += "OVSBOOTPROTO=dhcp\n"
|
|
if base_opt.members:
|
|
members = [member.name for member in base_opt.members]
|
|
self.member_names[base_opt.name] = members
|
|
if base_opt.use_dhcp:
|
|
data += ("OVSDHCPINTERFACES=\"%s\"\n" % " ".join(members))
|
|
if base_opt.primary_interface_name:
|
|
mac = common.interface_mac(base_opt.primary_interface_name)
|
|
ovs_extra.append("set bridge %s other-config:hwaddr=%s" %
|
|
(base_opt.name, mac))
|
|
if base_opt.ovs_options:
|
|
data += "OVS_OPTIONS=\"%s\"\n" % base_opt.ovs_options
|
|
ovs_extra.extend(base_opt.ovs_extra)
|
|
elif isinstance(base_opt, objects.OvsUserBridge):
|
|
data += "DEVICETYPE=ovs\n"
|
|
data += "TYPE=OVSUserBridge\n"
|
|
if base_opt.use_dhcp or base_opt.use_dhcpv6:
|
|
data += "OVSBOOTPROTO=dhcp\n"
|
|
if base_opt.members:
|
|
members = [member.name for member in base_opt.members]
|
|
self.member_names[base_opt.name] = members
|
|
if base_opt.use_dhcp:
|
|
data += ("OVSDHCPINTERFACES=\"%s\"\n" % " ".join(members))
|
|
if base_opt.ovs_options:
|
|
data += "OVS_OPTIONS=\"%s\"\n" % base_opt.ovs_options
|
|
ovs_extra.extend(base_opt.ovs_extra)
|
|
elif isinstance(base_opt, objects.OvsBond):
|
|
if base_opt.primary_interface_name:
|
|
primary_name = base_opt.primary_interface_name
|
|
self.bond_primary_ifaces[base_opt.name] = primary_name
|
|
data += "DEVICETYPE=ovs\n"
|
|
data += "TYPE=OVSBond\n"
|
|
if base_opt.use_dhcp or base_opt.use_dhcpv6:
|
|
data += "OVSBOOTPROTO=dhcp\n"
|
|
if base_opt.members:
|
|
members = [member.name for member in base_opt.members]
|
|
self.member_names[base_opt.name] = members
|
|
data += ("BOND_IFACES=\"%s\"\n" % " ".join(members))
|
|
if base_opt.ovs_options:
|
|
data += "OVS_OPTIONS=\"%s\"\n" % base_opt.ovs_options
|
|
ovs_extra.extend(base_opt.ovs_extra)
|
|
elif isinstance(base_opt, objects.LinuxBridge):
|
|
data += "TYPE=Bridge\n"
|
|
data += "DELAY=0\n"
|
|
if base_opt.use_dhcp:
|
|
data += "BOOTPROTO=dhcp\n"
|
|
if base_opt.members:
|
|
members = [member.name for member in base_opt.members]
|
|
self.member_names[base_opt.name] = members
|
|
if base_opt.primary_interface_name:
|
|
primary_name = base_opt.primary_interface_name
|
|
primary_mac = common.interface_mac(primary_name)
|
|
data += "MACADDR=\"%s\"\n" % primary_mac
|
|
elif isinstance(base_opt, objects.LinuxBond):
|
|
if base_opt.primary_interface_name:
|
|
primary_name = base_opt.primary_interface_name
|
|
primary_mac = common.interface_mac(primary_name)
|
|
data += "MACADDR=\"%s\"\n" % primary_mac
|
|
if base_opt.use_dhcp:
|
|
data += "BOOTPROTO=dhcp\n"
|
|
if base_opt.members:
|
|
members = [member.name for member in base_opt.members]
|
|
self.member_names[base_opt.name] = members
|
|
if base_opt.bonding_options:
|
|
data += "BONDING_OPTS=\"%s\"\n" % base_opt.bonding_options
|
|
elif isinstance(base_opt, objects.LinuxTeam):
|
|
if base_opt.primary_interface_name:
|
|
primary_name = base_opt.primary_interface_name
|
|
primary_mac = common.interface_mac(primary_name)
|
|
data += "MACADDR=\"%s\"\n" % primary_mac
|
|
if base_opt.use_dhcp:
|
|
data += "BOOTPROTO=dhcp\n"
|
|
if base_opt.members:
|
|
members = [member.name for member in base_opt.members]
|
|
self.member_names[base_opt.name] = members
|
|
data += "DEVICETYPE=Team\n"
|
|
if base_opt.bonding_options:
|
|
data += "TEAM_CONFIG='%s'\n" % base_opt.bonding_options
|
|
elif isinstance(base_opt, objects.OvsTunnel):
|
|
ovs_extra.extend(base_opt.ovs_extra)
|
|
data += "DEVICETYPE=ovs\n"
|
|
data += "TYPE=OVSTunnel\n"
|
|
data += "OVS_BRIDGE=%s\n" % base_opt.bridge_name
|
|
data += "OVS_TUNNEL_TYPE=%s\n" % base_opt.tunnel_type
|
|
data += "OVS_TUNNEL_OPTIONS=\"%s\"\n" % \
|
|
' '.join(base_opt.ovs_options)
|
|
elif isinstance(base_opt, objects.OvsPatchPort):
|
|
ovs_extra.extend(base_opt.ovs_extra)
|
|
data += "DEVICETYPE=ovs\n"
|
|
data += "TYPE=OVSPatchPort\n"
|
|
data += "OVS_BRIDGE=%s\n" % base_opt.bridge_name
|
|
data += "OVS_PATCH_PEER=%s\n" % base_opt.peer
|
|
elif isinstance(base_opt, objects.OvsDpdkPort):
|
|
ovs_extra.extend(base_opt.ovs_extra)
|
|
data += "DEVICETYPE=ovs\n"
|
|
data += "TYPE=OVSDPDKPort\n"
|
|
data += "OVS_BRIDGE=%s\n" % base_opt.bridge_name
|
|
# Validation of DPDK port having only one interface is done prior
|
|
# to this. So accesing the interface name statically.
|
|
# Also dpdk_devargs would be valid here, since
|
|
# bind_dpdk_interfaces() is invoked before this.
|
|
dpdk_devargs = utils.get_dpdk_devargs(
|
|
base_opt.members[0].name, self.noop)
|
|
|
|
ovs_extra.append("set Interface $DEVICE options:dpdk-devargs="
|
|
"%s" % dpdk_devargs)
|
|
if base_opt.mtu:
|
|
ovs_extra.append("set Interface $DEVICE mtu_request=$MTU")
|
|
if base_opt.rx_queue:
|
|
data += "RX_QUEUE=%i\n" % base_opt.rx_queue
|
|
ovs_extra.append("set Interface $DEVICE " +
|
|
"options:n_rxq=$RX_QUEUE")
|
|
elif isinstance(base_opt, objects.OvsDpdkBond):
|
|
ovs_extra.extend(base_opt.ovs_extra)
|
|
# Referring to bug:1643026, the below commenting of the interfaces,
|
|
# is to workaround the error, but is not the long term solution.
|
|
# The long term solution is to run DPDK options before
|
|
# os-net-config, which is being tracked at BUG:1654975
|
|
# if base_opt.primary_interface_name:
|
|
# primary_name = base_opt.primary_interface_name
|
|
# self.bond_primary_ifaces[base_opt.name] = primary_name
|
|
data += "DEVICETYPE=ovs\n"
|
|
data += "TYPE=OVSDPDKBond\n"
|
|
data += "OVS_BRIDGE=%s\n" % base_opt.bridge_name
|
|
if base_opt.members:
|
|
for bond_member in base_opt.members:
|
|
# Validation of DPDK port having only one interface is done
|
|
# prior to this. So accesing the interface name statically.
|
|
# Also dpdk_devargs would be valid here, since
|
|
# bind_dpdk_interfaces () is invoked before this.
|
|
dpdk_devargs = utils.get_dpdk_devargs(
|
|
bond_member.members[0].name, self.noop)
|
|
ovs_extra.append("set Interface %s options:"
|
|
"dpdk-devargs=%s"
|
|
% (bond_member.name, dpdk_devargs))
|
|
members = [member.name for member in base_opt.members]
|
|
data += ("BOND_IFACES=\"%s\"\n" % " ".join(members))
|
|
# MTU configuration given for the OvsDpdkbond shall be applied
|
|
# to each of the members of the OvsDpdkbond
|
|
if base_opt.mtu:
|
|
for member in base_opt.members:
|
|
ovs_extra.append("set Interface %s mtu_request=$MTU" %
|
|
member.name)
|
|
if base_opt.rx_queue:
|
|
data += "RX_QUEUE=%i\n" % base_opt.rx_queue
|
|
for member in base_opt.members:
|
|
ovs_extra.append("set Interface %s options:n_rxq="
|
|
"$RX_QUEUE" % member.name)
|
|
if base_opt.ovs_options:
|
|
data += "OVS_OPTIONS=\"%s\"\n" % base_opt.ovs_options
|
|
ovs_extra.extend(base_opt.ovs_extra)
|
|
else:
|
|
if base_opt.use_dhcp:
|
|
data += "BOOTPROTO=dhcp\n"
|
|
elif not base_opt.addresses:
|
|
data += "BOOTPROTO=none\n"
|
|
if hasattr(base_opt, 'ethtool_opts') and base_opt.ethtool_opts:
|
|
data += "ETHTOOL_OPTS=\"%s\"\n" % base_opt.ethtool_opts
|
|
|
|
if base_opt.mtu:
|
|
data += "MTU=%i\n" % base_opt.mtu
|
|
if base_opt.use_dhcpv6 or base_opt.v6_addresses():
|
|
data += "IPV6INIT=yes\n"
|
|
if base_opt.mtu:
|
|
data += "IPV6_MTU=%i\n" % base_opt.mtu
|
|
if base_opt.use_dhcpv6:
|
|
data += "DHCPV6C=yes\n"
|
|
elif base_opt.addresses:
|
|
v4_addresses = base_opt.v4_addresses()
|
|
if v4_addresses:
|
|
data += "BOOTPROTO=static\n"
|
|
for i, address in enumerate(v4_addresses):
|
|
num = '%s' % i if i else ''
|
|
data += "IPADDR%s=%s\n" % (num, address.ip)
|
|
data += "NETMASK%s=%s\n" % (num, address.netmask)
|
|
|
|
v6_addresses = base_opt.v6_addresses()
|
|
if v6_addresses:
|
|
first_v6 = v6_addresses[0]
|
|
data += "IPV6_AUTOCONF=no\n"
|
|
data += "IPV6ADDR=%s\n" % first_v6.ip_netmask
|
|
if len(v6_addresses) > 1:
|
|
secondaries_v6 = " ".join(map(lambda a: a.ip_netmask,
|
|
v6_addresses[1:]))
|
|
data += "IPV6ADDR_SECONDARIES=\"%s\"\n" % secondaries_v6
|
|
|
|
if base_opt.hwaddr:
|
|
data += "HWADDR=%s\n" % base_opt.hwaddr
|
|
if ovs_extra:
|
|
data += "OVS_EXTRA=\"%s\"\n" % " -- ".join(ovs_extra)
|
|
if not base_opt.defroute:
|
|
data += "DEFROUTE=no\n"
|
|
if base_opt.dhclient_args:
|
|
data += "DHCLIENTARGS=%s\n" % base_opt.dhclient_args
|
|
if base_opt.dns_servers:
|
|
data += "DNS1=%s\n" % base_opt.dns_servers[0]
|
|
if len(base_opt.dns_servers) >= 2:
|
|
data += "DNS2=%s\n" % base_opt.dns_servers[1]
|
|
if len(base_opt.dns_servers) > 2:
|
|
logger.warning('ifcfg format supports max 2 resolvers.')
|
|
if base_opt.domain:
|
|
if type(base_opt.domain) == list:
|
|
data += "DOMAIN=\"%s\"\n" % ' '.join(base_opt.domain)
|
|
else:
|
|
data += "DOMAIN=%s\n" % base_opt.domain
|
|
return data
|
|
|
|
def _add_routes(self, interface_name, routes=[]):
|
|
logger.info('adding custom route for interface: %s' % interface_name)
|
|
data = ""
|
|
first_line = ""
|
|
data6 = ""
|
|
first_line6 = ""
|
|
for route in routes:
|
|
options = ""
|
|
table = ""
|
|
if route.route_options:
|
|
options = " %s" % route.route_options
|
|
if route.route_table:
|
|
if route.route_options.find('table ') == -1:
|
|
table = " table %s" % route.route_table
|
|
if ":" not in route.next_hop:
|
|
# Route is an IPv4 route
|
|
if route.default:
|
|
first_line = "default via %s dev %s%s%s\n" % (
|
|
route.next_hop, interface_name,
|
|
table, options)
|
|
else:
|
|
data += "%s via %s dev %s%s%s\n" % (
|
|
route.ip_netmask, route.next_hop,
|
|
interface_name, table, options)
|
|
else:
|
|
# Route is an IPv6 route
|
|
if route.default:
|
|
first_line6 = "default via %s dev %s%s%s\n" % (
|
|
route.next_hop, interface_name,
|
|
table, options)
|
|
else:
|
|
data6 += "%s via %s dev %s%s%s\n" % (
|
|
route.ip_netmask, route.next_hop,
|
|
interface_name, table, options)
|
|
self.route_data[interface_name] = first_line + data
|
|
self.route6_data[interface_name] = first_line6 + data6
|
|
logger.debug('route data: %s' % self.route_data[interface_name])
|
|
logger.debug('ipv6 route data: %s' % self.route6_data[interface_name])
|
|
|
|
def _add_rules(self, interface, rules):
|
|
"""Add RouteRule objects to an interface.
|
|
|
|
:param interface: the name of the interface to apply rules.
|
|
:param rules: the list of rules to apply to the interface.
|
|
"""
|
|
logger.info('adding route rules for interface: %s' % interface)
|
|
data = ""
|
|
first_line = "# This file is autogenerated by os-net-config\n"
|
|
for rule in rules:
|
|
if rule.comment:
|
|
data += "# %s\n" % rule.comment
|
|
data += "%s\n" % rule.rule
|
|
self.rule_data[interface] = first_line + data
|
|
logger.debug('rules for interface: %s' % self.rule_data[interface])
|
|
|
|
def add_route_table(self, route_table):
|
|
"""Add a RouteTable object to the net config object.
|
|
|
|
:param route_table: the RouteTable object to add.
|
|
"""
|
|
logger.info('adding route table: %s %s' % (route_table.table_id,
|
|
route_table.name))
|
|
self.route_table_data[int(route_table.table_id)] = route_table.name
|
|
|
|
def add_interface(self, interface):
|
|
"""Add an Interface object to the net config object.
|
|
|
|
:param interface: The Interface object to add.
|
|
"""
|
|
logger.info('adding interface: %s' % interface.name)
|
|
data = self._add_common(interface)
|
|
logger.debug('interface data: %s' % data)
|
|
self.interface_data[interface.name] = data
|
|
if interface.routes:
|
|
self._add_routes(interface.name, interface.routes)
|
|
if interface.rules:
|
|
self._add_rules(interface.name, interface.rules)
|
|
|
|
if interface.renamed:
|
|
logger.info("Interface %s being renamed to %s"
|
|
% (interface.hwname, interface.name))
|
|
self.renamed_interfaces[interface.hwname] = interface.name
|
|
|
|
def add_vlan(self, vlan):
|
|
"""Add a Vlan object to the net config object.
|
|
|
|
:param vlan: The vlan object to add.
|
|
"""
|
|
logger.info('adding vlan: %s' % vlan.name)
|
|
data = self._add_common(vlan)
|
|
logger.debug('vlan data: %s' % data)
|
|
self.vlan_data[vlan.name] = data
|
|
if vlan.routes:
|
|
self._add_routes(vlan.name, vlan.routes)
|
|
if vlan.rules:
|
|
self._add_rules(vlan.name, vlan.rules)
|
|
|
|
def add_ivs_interface(self, ivs_interface):
|
|
"""Add a ivs_interface object to the net config object.
|
|
|
|
:param ivs_interface: The ivs_interface object to add.
|
|
"""
|
|
logger.info('adding ivs_interface: %s' % ivs_interface.name)
|
|
data = self._add_common(ivs_interface)
|
|
logger.debug('ivs_interface data: %s' % data)
|
|
self.ivsinterface_data[ivs_interface.name] = data
|
|
if ivs_interface.routes:
|
|
self._add_routes(ivs_interface.name, ivs_interface.routes)
|
|
if ivs_interface.rules:
|
|
self._add_rules(ivs_interface.name, ivs_interface.rules)
|
|
|
|
def add_nfvswitch_internal(self, nfvswitch_internal):
|
|
"""Add a nfvswitch_internal interface object to the net config object.
|
|
|
|
:param nfvswitch_internal: The nfvswitch_internal object to add.
|
|
"""
|
|
iface_name = nfvswitch_internal.name
|
|
logger.info('adding nfvswitch_internal interface: %s' % iface_name)
|
|
data = self._add_common(nfvswitch_internal)
|
|
logger.debug('nfvswitch_internal interface data: %s' % data)
|
|
self.nfvswitch_intiface_data[iface_name] = data
|
|
if nfvswitch_internal.routes:
|
|
self._add_routes(iface_name, nfvswitch_internal.routes)
|
|
if nfvswitch_internal.rules:
|
|
self._add_rules(iface_name, nfvswitch_internal.rules)
|
|
|
|
def add_bridge(self, bridge):
|
|
"""Add an OvsBridge object to the net config object.
|
|
|
|
:param bridge: The OvsBridge object to add.
|
|
"""
|
|
logger.info('adding bridge: %s' % bridge.name)
|
|
data = self._add_common(bridge)
|
|
logger.debug('bridge data: %s' % data)
|
|
self.bridge_data[bridge.name] = data
|
|
if bridge.routes:
|
|
self._add_routes(bridge.name, bridge.routes)
|
|
if bridge.rules:
|
|
self._add_rules(bridge.name, bridge.rules)
|
|
|
|
def add_ovs_user_bridge(self, bridge):
|
|
"""Add an OvsUserBridge object to the net config object.
|
|
|
|
:param bridge: The OvsUserBridge object to add.
|
|
"""
|
|
logger.info('adding ovs user bridge: %s' % bridge.name)
|
|
data = self._add_common(bridge)
|
|
logger.debug('ovs user bridge data: %s' % data)
|
|
self.bridge_data[bridge.name] = data
|
|
if bridge.routes:
|
|
self._add_routes(bridge.name, bridge.routes)
|
|
if bridge.rules:
|
|
self._add_rules(bridge.name, bridge.rules)
|
|
|
|
def add_linux_bridge(self, bridge):
|
|
"""Add a LinuxBridge object to the net config object.
|
|
|
|
:param bridge: The LinuxBridge object to add.
|
|
"""
|
|
logger.info('adding linux bridge: %s' % bridge.name)
|
|
data = self._add_common(bridge)
|
|
logger.debug('bridge data: %s' % data)
|
|
self.linuxbridge_data[bridge.name] = data
|
|
if bridge.routes:
|
|
self._add_routes(bridge.name, bridge.routes)
|
|
if bridge.rules:
|
|
self._add_rules(bridge.name, bridge.rules)
|
|
|
|
def add_ivs_bridge(self, bridge):
|
|
"""Add a IvsBridge object to the net config object.
|
|
|
|
IVS can only support one virtual switch per node,
|
|
using "ivs" as its name. As long as the ivs service
|
|
is running, the ivs virtual switch will be there.
|
|
It is impossible to add multiple ivs virtual switches
|
|
per node.
|
|
:param bridge: The IvsBridge object to add.
|
|
"""
|
|
pass
|
|
|
|
def add_nfvswitch_bridge(self, bridge):
|
|
"""Add a NFVSwitchBridge object to the net config object.
|
|
|
|
NFVSwitch can only support one virtual switch per node,
|
|
using "nfvswitch" as its name. As long as the nfvswitch service
|
|
is running, the nfvswitch virtual switch will be available.
|
|
:param bridge: The NfvswitchBridge object to add.
|
|
"""
|
|
self.nfvswitch_options = bridge.options
|
|
|
|
def add_bond(self, bond):
|
|
"""Add an OvsBond object to the net config object.
|
|
|
|
:param bond: The OvsBond object to add.
|
|
"""
|
|
logger.info('adding bond: %s' % bond.name)
|
|
data = self._add_common(bond)
|
|
logger.debug('bond data: %s' % data)
|
|
self.interface_data[bond.name] = data
|
|
if bond.routes:
|
|
self._add_routes(bond.name, bond.routes)
|
|
if bond.rules:
|
|
self._add_rules(bond.name, bond.rules)
|
|
|
|
def add_linux_bond(self, bond):
|
|
"""Add a LinuxBond object to the net config object.
|
|
|
|
:param bond: The LinuxBond object to add.
|
|
"""
|
|
logger.info('adding linux bond: %s' % bond.name)
|
|
data = self._add_common(bond)
|
|
logger.debug('bond data: %s' % data)
|
|
self.linuxbond_data[bond.name] = data
|
|
if bond.routes:
|
|
self._add_routes(bond.name, bond.routes)
|
|
if bond.rules:
|
|
self._add_rules(bond.name, bond.rules)
|
|
|
|
def add_linux_team(self, team):
|
|
"""Add a LinuxTeam object to the net config object.
|
|
|
|
:param team: The LinuxTeam object to add.
|
|
"""
|
|
logger.info('adding linux team: %s' % team.name)
|
|
data = self._add_common(team)
|
|
logger.debug('team data: %s' % data)
|
|
self.linuxteam_data[team.name] = data
|
|
if team.routes:
|
|
self._add_routes(team.name, team.routes)
|
|
if team.rules:
|
|
self._add_rules(team.name, team.rules)
|
|
|
|
def add_ovs_tunnel(self, tunnel):
|
|
"""Add a OvsTunnel object to the net config object.
|
|
|
|
:param tunnel: The OvsTunnel object to add.
|
|
"""
|
|
logger.info('adding ovs tunnel: %s' % tunnel.name)
|
|
data = self._add_common(tunnel)
|
|
logger.debug('ovs tunnel data: %s' % data)
|
|
self.interface_data[tunnel.name] = data
|
|
|
|
def add_ovs_patch_port(self, ovs_patch_port):
|
|
"""Add a OvsPatchPort object to the net config object.
|
|
|
|
:param ovs_patch_port: The OvsPatchPort object to add.
|
|
"""
|
|
logger.info('adding ovs patch port: %s' % ovs_patch_port.name)
|
|
data = self._add_common(ovs_patch_port)
|
|
logger.debug('ovs patch port data: %s' % data)
|
|
self.interface_data[ovs_patch_port.name] = data
|
|
|
|
def add_ib_interface(self, ib_interface):
|
|
"""Add an InfiniBand interface object to the net config object.
|
|
|
|
:param ib_interface: The InfiniBand interface object to add.
|
|
"""
|
|
logger.info('adding ib_interface: %s' % ib_interface.name)
|
|
data = self._add_common(ib_interface)
|
|
logger.debug('ib_interface data: %s' % data)
|
|
self.ib_interface_data[ib_interface.name] = data
|
|
if ib_interface.routes:
|
|
self._add_routes(ib_interface.name, ib_interface.routes)
|
|
if ib_interface.rules:
|
|
self._add_rules(ib_interface.name, ib_interface.rules)
|
|
|
|
if ib_interface.renamed:
|
|
logger.info("InfiniBand interface %s being renamed to %s"
|
|
% (ib_interface.hwname, ib_interface.name))
|
|
self.renamed_interfaces[ib_interface.hwname] = ib_interface.name
|
|
|
|
def add_ib_child_interface(self, ib_child_interface):
|
|
"""Add an InfiniBand child interface object to the net config object.
|
|
|
|
:param ib_child_interface: The InfiniBand child
|
|
interface object to add.
|
|
"""
|
|
logger.info('adding ib_child_interface: %s' % ib_child_interface.name)
|
|
data = self._add_common(ib_child_interface)
|
|
logger.debug('ib_child_interface data: %s' % data)
|
|
self.ib_childs_data[ib_child_interface.name] = data
|
|
if ib_child_interface.routes:
|
|
self._add_routes(ib_child_interface.name,
|
|
ib_child_interface.routes)
|
|
if ib_child_interface.rules:
|
|
self._add_rules(ib_child_interface.name, ib_child_interface.rules)
|
|
|
|
def add_ovs_dpdk_port(self, ovs_dpdk_port):
|
|
"""Add a OvsDpdkPort object to the net config object.
|
|
|
|
:param ovs_dpdk_port: The OvsDpdkPort object to add.
|
|
"""
|
|
logger.info('adding ovs dpdk port: %s' % ovs_dpdk_port.name)
|
|
|
|
# DPDK Port will have only one member of type Interface, validation
|
|
# checks are added at the object creation stage.
|
|
ifname = ovs_dpdk_port.members[0].name
|
|
|
|
# Bind the dpdk interface
|
|
utils.bind_dpdk_interfaces(ifname, ovs_dpdk_port.driver, self.noop)
|
|
if not self.noop:
|
|
remove_ifcfg_config(ifname)
|
|
|
|
data = self._add_common(ovs_dpdk_port)
|
|
logger.debug('ovs dpdk port data: %s' % data)
|
|
self.interface_data[ovs_dpdk_port.name] = data
|
|
|
|
"""Add an extra ifcfg entry for mellanox NICs,
|
|
for the interface which is a member of ovs_dpdk_port
|
|
with NM and DHCP disabled
|
|
"""
|
|
if common.is_mellanox_interface(ifname):
|
|
self.nm_controlled = False
|
|
self.use_dhcp = False
|
|
dpdk_if_name = objects.Interface(ifname)
|
|
self.add_interface(dpdk_if_name)
|
|
|
|
def add_ovs_dpdk_bond(self, ovs_dpdk_bond):
|
|
"""Add an OvsDPDKBond object to the net config object.
|
|
|
|
:param ovs_dpdk_bond: The OvsBond object to add.
|
|
"""
|
|
logger.info('adding ovs dpdk bond: %s' % ovs_dpdk_bond.name)
|
|
|
|
# Bind the dpdk interface
|
|
for dpdk_port in ovs_dpdk_bond.members:
|
|
# DPDK Port will have only one member of type Interface, validation
|
|
# checks are added at the object creation stage.
|
|
ifname = dpdk_port.members[0].name
|
|
utils.bind_dpdk_interfaces(ifname, dpdk_port.driver, self.noop)
|
|
if not self.noop:
|
|
remove_ifcfg_config(ifname)
|
|
|
|
data = self._add_common(ovs_dpdk_bond)
|
|
logger.debug('ovs dpdk bond data: %s' % data)
|
|
self.interface_data[ovs_dpdk_bond.name] = data
|
|
if ovs_dpdk_bond.routes:
|
|
self._add_routes(ovs_dpdk_bond.name, ovs_dpdk_bond.routes)
|
|
if ovs_dpdk_bond.rules:
|
|
self._add_rules(ovs_dpdk_bond.name, ovs_dpdk_bond.rules)
|
|
|
|
"""Add an extra ifcfg entry for mellanox NICs,
|
|
for all interfaces which is a member of ovs_dpdk_bond
|
|
with NM and DHCP disabled
|
|
"""
|
|
for dpdk_port in ovs_dpdk_bond.members:
|
|
ifname = dpdk_port.members[0].name
|
|
if common.is_mellanox_interface(ifname):
|
|
self.nm_controlled = False
|
|
self.use_dhcp = False
|
|
dpdk_if_name = objects.Interface(ifname)
|
|
self.add_interface(dpdk_if_name)
|
|
|
|
def add_sriov_pf(self, sriov_pf):
|
|
"""Add a SriovPF object to the net config object
|
|
|
|
:param sriov_pf: The SriovPF object to add
|
|
"""
|
|
logger.info('adding sriov pf: %s' % sriov_pf.name)
|
|
data = self._add_common(sriov_pf)
|
|
logger.debug('sriov pf data: %s' % data)
|
|
utils.update_sriov_pf_map(sriov_pf.name, sriov_pf.numvfs,
|
|
self.noop, promisc=sriov_pf.promisc,
|
|
link_mode=sriov_pf.link_mode,
|
|
vdpa=sriov_pf.vdpa,
|
|
steering_mode=sriov_pf.steering_mode)
|
|
self.interface_data[sriov_pf.name] = data
|
|
if sriov_pf.routes:
|
|
self._add_routes(sriov_pf.name, sriov_pf.routes)
|
|
if sriov_pf.rules:
|
|
self._add_rules(sriov_pf.name, sriov_pf.rules)
|
|
|
|
def add_sriov_vf(self, sriov_vf):
|
|
"""Add a SriovVF object to the net config object
|
|
|
|
:param sriov_vf: The SriovVF object to add
|
|
"""
|
|
logger.info('adding sriov vf: %s for pf: %s, vfid: %d'
|
|
% (sriov_vf.name, sriov_vf.device, sriov_vf.vfid))
|
|
data = self._add_common(sriov_vf)
|
|
logger.debug('sriov vf data: %s' % data)
|
|
self.interface_data[sriov_vf.name] = data
|
|
if sriov_vf.routes:
|
|
self._add_routes(sriov_vf.name, sriov_vf.routes)
|
|
if sriov_vf.rules:
|
|
self._add_rules(sriov_vf.name, sriov_vf.rules)
|
|
|
|
def add_vpp_interface(self, vpp_interface):
|
|
"""Add a VppInterface object to the net config object
|
|
|
|
:param vpp_interface: The VppInterface object to add
|
|
"""
|
|
vpp_interface.pci_dev = utils.get_pci_address(vpp_interface.name,
|
|
False)
|
|
if not vpp_interface.pci_dev:
|
|
vpp_interface.pci_dev = utils.get_stored_pci_address(
|
|
vpp_interface.name, False)
|
|
vpp_interface.hwaddr = common.interface_mac(vpp_interface.name)
|
|
if not self.noop:
|
|
self.ifdown(vpp_interface.name)
|
|
remove_ifcfg_config(vpp_interface.name)
|
|
logger.info('adding vpp interface: %s %s'
|
|
% (vpp_interface.name, vpp_interface.pci_dev))
|
|
self.vpp_interface_data[vpp_interface.name] = vpp_interface
|
|
|
|
def add_vpp_bond(self, vpp_bond):
|
|
"""Add a VppInterface object to the net config object
|
|
|
|
:param vpp_bond: The VPPBond object to add
|
|
"""
|
|
logger.info('adding vpp bond: %s' % vpp_bond.name)
|
|
self.vpp_bond_data[vpp_bond.name] = vpp_bond
|
|
|
|
def add_contrail_vrouter(self, contrail_vrouter):
|
|
"""Add a ContraiVrouter object to the net config object
|
|
|
|
:param contrail_vrouter:
|
|
The ContrailVrouter object to add
|
|
"""
|
|
logger.info('adding contrail_vrouter interface: %s'
|
|
% contrail_vrouter.name)
|
|
ifnames = ",".join([m.name for m in contrail_vrouter.members])
|
|
data = self._add_common(contrail_vrouter)
|
|
data += "DEVICETYPE=vhost\n"
|
|
data += "TYPE=kernel_mode\n"
|
|
data += "BIND_INT=%s\n" % ifnames
|
|
logger.debug('contrail data: %s' % data)
|
|
self.interface_data[contrail_vrouter.name] = data
|
|
if contrail_vrouter.routes:
|
|
self._add_routes(contrail_vrouter.name, contrail_vrouter.routes)
|
|
if contrail_vrouter.rules:
|
|
self._add_rules(contrail_vrouter.name, contrail_vrouter.rules)
|
|
|
|
def add_contrail_vrouter_dpdk(self, contrail_vrouter_dpdk):
|
|
"""Add a ContraiVrouterDpdk object to the net config object
|
|
|
|
:param contrail_vrouter_dpdk:
|
|
The ContrailVrouterDpdk object to add
|
|
"""
|
|
logger.info('adding contrail vrouter dpdk interface: %s'
|
|
% contrail_vrouter_dpdk.name)
|
|
pci_string = ",".join(
|
|
utils.translate_ifname_to_pci_address(bind_int.name, self.noop)
|
|
for bind_int in contrail_vrouter_dpdk.members)
|
|
data = self._add_common(contrail_vrouter_dpdk)
|
|
data += "DEVICETYPE=vhost\n"
|
|
data += "TYPE=dpdk\n"
|
|
data += "BIND_INT=%s\n" % pci_string
|
|
if len(contrail_vrouter_dpdk.members) > 1:
|
|
data += "BOND_MODE=%s\n" % contrail_vrouter_dpdk.bond_mode
|
|
data += "BOND_POLICY=%s\n" % contrail_vrouter_dpdk.bond_policy
|
|
data += "DRIVER=%s\n" % contrail_vrouter_dpdk.driver
|
|
data += "CPU_LIST=%s\n" % contrail_vrouter_dpdk.cpu_list
|
|
if contrail_vrouter_dpdk.vlan_id:
|
|
data += "VLAN_ID=%s\n" % contrail_vrouter_dpdk.vlan_id
|
|
logger.debug('contrail dpdk data: %s' % data)
|
|
self.interface_data[contrail_vrouter_dpdk.name] = data
|
|
if contrail_vrouter_dpdk.routes:
|
|
self._add_routes(contrail_vrouter_dpdk.name,
|
|
contrail_vrouter_dpdk.routes)
|
|
if contrail_vrouter_dpdk.rules:
|
|
self._add_rules(contrail_vrouter_dpdk.name,
|
|
contrail_vrouter_dpdk.rules)
|
|
|
|
def add_linux_tap(self, linux_tap):
|
|
"""Add a LinuxTap object to the net config object
|
|
|
|
:param linux_tap:
|
|
The LinuxTap object to add
|
|
"""
|
|
logger.info('adding Linux TAP interface: %s'
|
|
% linux_tap.name)
|
|
data = self._add_common(linux_tap)
|
|
data += "TYPE=Tap\n"
|
|
self.interface_data[linux_tap.name] = data
|
|
if linux_tap.routes:
|
|
self._add_routes(linux_tap.name,
|
|
linux_tap.routes)
|
|
if linux_tap.rules:
|
|
self._add_rules(linux_tap.name,
|
|
linux_tap.rules)
|
|
|
|
def generate_ivs_config(self, ivs_uplinks, ivs_interfaces):
|
|
"""Generate configuration content for ivs."""
|
|
|
|
intfs = []
|
|
for intf in ivs_uplinks:
|
|
intfs.append(' -u ')
|
|
intfs.append(intf)
|
|
uplink_str = ''.join(intfs)
|
|
|
|
intfs = []
|
|
for intf in ivs_interfaces:
|
|
intfs.append(' --internal-port=')
|
|
intfs.append(intf)
|
|
intf_str = ''.join(intfs)
|
|
|
|
data = ("DAEMON_ARGS=\"--hitless --certificate /etc/ivs "
|
|
"--inband-vlan 4092%s%s\""
|
|
% (uplink_str, intf_str))
|
|
return data
|
|
|
|
def generate_nfvswitch_config(self, nfvswitch_ifaces,
|
|
nfvswitch_internal_ifaces):
|
|
"""Generate configuration content for nfvswitch."""
|
|
|
|
options_str = ""
|
|
if self.nfvswitch_options:
|
|
options_str = self.nfvswitch_options
|
|
|
|
ifaces = []
|
|
for iface in nfvswitch_ifaces:
|
|
ifaces.append(' -u ')
|
|
ifaces.append(iface)
|
|
iface_str = ''.join(ifaces)
|
|
|
|
ifaces = []
|
|
for iface in nfvswitch_internal_ifaces:
|
|
ifaces.append(' -m ')
|
|
ifaces.append(iface)
|
|
internal_str = ''.join(ifaces)
|
|
|
|
data = "SETUP_ARGS=\"%s%s%s\"" % (options_str, iface_str, internal_str)
|
|
return data
|
|
|
|
def generate_route_table_config(self, route_tables):
|
|
"""Generate configuration content for routing tables.
|
|
|
|
This method first extracts the existing route table definitions. If
|
|
any non-default tables exist, they will be kept unless they conflict
|
|
with new tables defined in the route_tables dict.
|
|
|
|
:param route_tables: A dict of RouteTable objects
|
|
"""
|
|
|
|
custom_tables = {}
|
|
res_ids = ['0', '253', '254', '255']
|
|
res_names = ['unspec', 'default', 'main', 'local']
|
|
rt_config = common.get_file_data(route_table_config_path()).split('\n')
|
|
rt_defaults = _ROUTE_TABLE_DEFAULT.split("\n")
|
|
data = _ROUTE_TABLE_DEFAULT
|
|
for line in (line for line in rt_config if line not in rt_defaults):
|
|
# Leave non-standard comments intact in file
|
|
if line.startswith('#') and not line.strip() in rt_defaults:
|
|
data += "%s\n" % line
|
|
# Ignore old managed entries, will be added back if in new config.
|
|
elif line.find("# os-net-config managed table") == -1:
|
|
id_name = line.split()
|
|
# Keep custom tables if there is no conflict with new tables.
|
|
if id_name[0].isdigit() and len(id_name) > 1:
|
|
if not id_name[0] in res_ids:
|
|
if not id_name[1] in res_names:
|
|
if not int(id_name[0]) in route_tables:
|
|
if not id_name[1] in route_tables.values():
|
|
# Replicate line with any comments appended
|
|
custom_tables[id_name[0]] = id_name[1]
|
|
data += "%s\n" % line
|
|
if custom_tables:
|
|
logger.debug("Existing route tables: %s" % custom_tables)
|
|
for id in sorted(route_tables):
|
|
if str(id) in res_ids:
|
|
message = "Table %s(%s) conflicts with reserved table %s(%s)" \
|
|
% (route_tables[id], id,
|
|
res_names[res_ids.index(str(id))], id)
|
|
raise os_net_config.ConfigurationError(message)
|
|
elif route_tables[id] in res_names:
|
|
message = "Table %s(%s) conflicts with reserved table %s(%s)" \
|
|
% (route_tables[id], id, route_tables[id],
|
|
res_ids[res_names.index(route_tables[id])])
|
|
raise os_net_config.ConfigurationError(message)
|
|
else:
|
|
data += "%s\t%s # os-net-config managed table\n" \
|
|
% (id, route_tables[id])
|
|
return data
|
|
|
|
def apply(self, cleanup=False, activate=True):
|
|
"""Apply the network configuration.
|
|
|
|
:param cleanup: A boolean which indicates whether any undefined
|
|
(existing but not present in the object model) interface
|
|
should be disabled and deleted.
|
|
:param activate: A boolean which indicates if the config should
|
|
be activated by stopping/starting interfaces
|
|
NOTE: if cleanup is specified we will deactivate interfaces even
|
|
if activate is false
|
|
:returns: a dict of the format: filename/data which contains info
|
|
for each file that was changed (or would be changed if in --noop
|
|
mode).
|
|
Note the noop mode is set via the constructor noop boolean
|
|
"""
|
|
logger.info('applying network configs...')
|
|
restart_interfaces = []
|
|
restart_vlans = []
|
|
restart_ib_childs = []
|
|
restart_bridges = []
|
|
restart_linux_bonds = []
|
|
start_linux_bonds = []
|
|
restart_linux_teams = []
|
|
restart_vpp = False
|
|
apply_interfaces = []
|
|
apply_bridges = []
|
|
apply_routes = []
|
|
apply_rules = []
|
|
update_files = {}
|
|
all_file_names = []
|
|
linux_bond_children = {}
|
|
ivs_uplinks = [] # ivs physical uplinks
|
|
ivs_interfaces = [] # ivs internal ports
|
|
nfvswitch_interfaces = [] # nfvswitch physical interfaces
|
|
nfvswitch_internal_ifaces = [] # nfvswitch internal/management ports
|
|
stop_dhclient_interfaces = []
|
|
ovs_needs_restart = False
|
|
vpp_interfaces = self.vpp_interface_data.values()
|
|
vpp_bonds = self.vpp_bond_data.values()
|
|
ipcmd = utils.iproute2_path()
|
|
|
|
for interface_name, iface_data in self.interface_data.items():
|
|
route_data = self.route_data.get(interface_name, '')
|
|
route6_data = self.route6_data.get(interface_name, '')
|
|
rule_data = self.rule_data.get(interface_name, '')
|
|
interface_path = self.root_dir + ifcfg_config_path(interface_name)
|
|
route_path = self.root_dir + route_config_path(interface_name)
|
|
route6_path = self.root_dir + route6_config_path(interface_name)
|
|
rule_path = self.root_dir + route_rule_config_path(interface_name)
|
|
all_file_names.append(interface_path)
|
|
all_file_names.append(route_path)
|
|
all_file_names.append(route6_path)
|
|
if "IVS_BRIDGE" in iface_data:
|
|
ivs_uplinks.append(interface_name)
|
|
if "NFVSWITCH_BRIDGE" in iface_data:
|
|
nfvswitch_interfaces.append(interface_name)
|
|
if utils.diff(interface_path, iface_data):
|
|
if self.ifcfg_requires_restart(interface_path, iface_data):
|
|
restart_interfaces.append(interface_name)
|
|
# Openvswitch needs to be restarted when OVSDPDKPort or
|
|
# OVSDPDKBond is added
|
|
if "OVSDPDK" in iface_data:
|
|
ovs_needs_restart = True
|
|
else:
|
|
apply_interfaces.append(
|
|
(interface_name, interface_path, iface_data))
|
|
update_files[interface_path] = iface_data
|
|
if "BOOTPROTO=dhcp" not in iface_data:
|
|
stop_dhclient_interfaces.append(interface_name)
|
|
|
|
else:
|
|
logger.info('No changes required for interface: %s' %
|
|
interface_name)
|
|
if utils.diff(route_path, route_data):
|
|
update_files[route_path] = route_data
|
|
if interface_name not in restart_interfaces:
|
|
apply_routes.append((interface_name, route_data))
|
|
if utils.diff(route6_path, route6_data):
|
|
update_files[route6_path] = route6_data
|
|
if interface_name not in restart_interfaces:
|
|
apply_routes.append((interface_name, route6_data))
|
|
if utils.diff(rule_path, rule_data):
|
|
update_files[rule_path] = rule_data
|
|
if interface_name not in restart_interfaces:
|
|
apply_rules.append((interface_name, rule_data))
|
|
|
|
for interface_name, iface_data in self.ivsinterface_data.items():
|
|
route_data = self.route_data.get(interface_name, '')
|
|
route6_data = self.route6_data.get(interface_name, '')
|
|
rule_data = self.rule_data.get(interface_name, '')
|
|
interface_path = self.root_dir + ifcfg_config_path(interface_name)
|
|
route_path = self.root_dir + route_config_path(interface_name)
|
|
route6_path = self.root_dir + route6_config_path(interface_name)
|
|
rule_path = self.root_dir + route_rule_config_path(interface_name)
|
|
all_file_names.append(interface_path)
|
|
all_file_names.append(route_path)
|
|
all_file_names.append(route6_path)
|
|
all_file_names.append(rule_path)
|
|
ivs_interfaces.append(interface_name)
|
|
if utils.diff(interface_path, iface_data):
|
|
if self.ifcfg_requires_restart(interface_path, iface_data):
|
|
restart_interfaces.append(interface_name)
|
|
else:
|
|
apply_interfaces.append(
|
|
(interface_name, interface_path, iface_data))
|
|
update_files[interface_path] = iface_data
|
|
else:
|
|
logger.info('No changes required for ivs interface: %s' %
|
|
interface_name)
|
|
if utils.diff(route_path, route_data):
|
|
update_files[route_path] = route_data
|
|
if interface_name not in restart_interfaces:
|
|
apply_routes.append((interface_name, route_data))
|
|
if utils.diff(route6_path, route6_data):
|
|
update_files[route6_path] = route6_data
|
|
if interface_name not in restart_interfaces:
|
|
apply_routes.append((interface_name, route6_data))
|
|
if utils.diff(rule_path, rule_data):
|
|
update_files[rule_path] = rule_data
|
|
if interface_name not in restart_interfaces:
|
|
apply_rules.append((interface_name, rule_data))
|
|
|
|
for iface_name, iface_data in self.nfvswitch_intiface_data.items():
|
|
route_data = self.route_data.get(iface_name, '')
|
|
route6_data = self.route6_data.get(iface_name, '')
|
|
rule_data = self.rule_data.get(iface_name, '')
|
|
iface_path = self.root_dir + ifcfg_config_path(iface_name)
|
|
route_path = self.root_dir + route_config_path(iface_name)
|
|
route6_path = self.root_dir + route6_config_path(iface_name)
|
|
rule_path = self.root_dir + route_rule_config_path(iface_name)
|
|
all_file_names.append(iface_path)
|
|
all_file_names.append(route_path)
|
|
all_file_names.append(route6_path)
|
|
all_file_names.append(rule_path)
|
|
nfvswitch_internal_ifaces.append(iface_name)
|
|
if utils.diff(iface_path, iface_data):
|
|
if self.ifcfg_requires_restart(iface_path, iface_data):
|
|
restart_interfaces.append(iface_name)
|
|
else:
|
|
apply_interfaces.append(
|
|
(iface_name, iface_path, iface_data))
|
|
update_files[iface_path] = iface_data
|
|
else:
|
|
logger.info('No changes required for nfvswitch interface: %s' %
|
|
iface_name)
|
|
if utils.diff(route_path, route_data):
|
|
update_files[route_path] = route_data
|
|
if iface_name not in restart_interfaces:
|
|
apply_routes.append((iface_name, route_data))
|
|
if utils.diff(route6_path, route6_data):
|
|
update_files[route6_path] = route6_data
|
|
if iface_name not in restart_interfaces:
|
|
apply_routes.append((iface_name, route6_data))
|
|
if utils.diff(rule_path, rule_data):
|
|
update_files[rule_path] = rule_data
|
|
if iface_name not in restart_interfaces:
|
|
apply_rules.append((iface_name, rule_data))
|
|
|
|
for bridge_name, bridge_data in self.bridge_data.items():
|
|
route_data = self.route_data.get(bridge_name, '')
|
|
route6_data = self.route6_data.get(bridge_name, '')
|
|
rule_data = self.rule_data.get(bridge_name, '')
|
|
bridge_path = self.root_dir + bridge_config_path(bridge_name)
|
|
br_route_path = self.root_dir + route_config_path(bridge_name)
|
|
br_route6_path = self.root_dir + route6_config_path(bridge_name)
|
|
br_rule_path = self.root_dir + route_rule_config_path(bridge_name)
|
|
all_file_names.append(bridge_path)
|
|
all_file_names.append(br_route_path)
|
|
all_file_names.append(br_route6_path)
|
|
all_file_names.append(br_rule_path)
|
|
if utils.diff(bridge_path, bridge_data):
|
|
if self.ifcfg_requires_restart(bridge_path, bridge_data):
|
|
restart_bridges.append(bridge_name)
|
|
# Avoid duplicate interface being added to the restart list
|
|
children = self.child_members(bridge_name)
|
|
for child in children:
|
|
if child not in restart_interfaces:
|
|
restart_interfaces.append(child)
|
|
else:
|
|
apply_bridges.append((bridge_name, bridge_path,
|
|
bridge_data))
|
|
update_files[bridge_path] = bridge_data
|
|
else:
|
|
logger.info('No changes required for bridge: %s' % bridge_name)
|
|
if utils.diff(br_route_path, route_data):
|
|
update_files[br_route_path] = route_data
|
|
if bridge_name not in restart_interfaces:
|
|
apply_routes.append((bridge_name, route_data))
|
|
if utils.diff(br_route6_path, route6_data):
|
|
update_files[br_route6_path] = route6_data
|
|
if bridge_name not in restart_interfaces:
|
|
apply_routes.append((bridge_name, route6_data))
|
|
if utils.diff(br_rule_path, rule_data):
|
|
update_files[br_rule_path] = rule_data
|
|
if bridge_name not in restart_interfaces:
|
|
apply_rules.append((bridge_name, rule_data))
|
|
|
|
for bridge_name, bridge_data in self.linuxbridge_data.items():
|
|
route_data = self.route_data.get(bridge_name, '')
|
|
route6_data = self.route6_data.get(bridge_name, '')
|
|
rule_data = self.rule_data.get(bridge_name, '')
|
|
bridge_path = self.root_dir + bridge_config_path(bridge_name)
|
|
br_route_path = self.root_dir + route_config_path(bridge_name)
|
|
br_route6_path = self.root_dir + route6_config_path(bridge_name)
|
|
br_rule_path = self.root_dir + route_rule_config_path(bridge_name)
|
|
all_file_names.append(bridge_path)
|
|
all_file_names.append(br_route_path)
|
|
all_file_names.append(br_route6_path)
|
|
all_file_names.append(br_rule_path)
|
|
if utils.diff(bridge_path, bridge_data):
|
|
if self.ifcfg_requires_restart(bridge_path, bridge_data):
|
|
restart_bridges.append(bridge_name)
|
|
# Avoid duplicate interface being added to the restart list
|
|
children = self.child_members(bridge_name)
|
|
for child in children:
|
|
if child not in restart_interfaces:
|
|
restart_interfaces.append(child)
|
|
else:
|
|
apply_bridges.append((bridge_name, bridge_path,
|
|
bridge_data))
|
|
update_files[bridge_path] = bridge_data
|
|
else:
|
|
logger.info('No changes required for bridge: %s' % bridge_name)
|
|
if utils.diff(br_route_path, route_data):
|
|
update_files[br_route_path] = route_data
|
|
if bridge_name not in restart_bridges:
|
|
apply_routes.append((bridge_name, route_data))
|
|
if utils.diff(route6_path, route6_data):
|
|
update_files[route6_path] = route6_data
|
|
if bridge_name not in restart_bridges:
|
|
apply_routes.append((bridge_name, route6_data))
|
|
if utils.diff(br_rule_path, rule_data):
|
|
update_files[br_rule_path] = rule_data
|
|
if bridge_name not in restart_bridges:
|
|
apply_rules.append((bridge_name, rule_data))
|
|
|
|
for team_name, team_data in self.linuxteam_data.items():
|
|
route_data = self.route_data.get(team_name, '')
|
|
route6_data = self.route6_data.get(team_name, '')
|
|
rule_data = self.rule_data.get(team_name, '')
|
|
team_path = self.root_dir + bridge_config_path(team_name)
|
|
team_route_path = self.root_dir + route_config_path(team_name)
|
|
team_route6_path = self.root_dir + route6_config_path(team_name)
|
|
team_rule_path = self.root_dir + route_rule_config_path(team_name)
|
|
all_file_names.append(team_path)
|
|
all_file_names.append(team_route_path)
|
|
all_file_names.append(team_route6_path)
|
|
all_file_names.append(team_rule_path)
|
|
if utils.diff(team_path, team_data):
|
|
if self.ifcfg_requires_restart(team_path, team_data):
|
|
restart_linux_teams.append(team_name)
|
|
# Avoid duplicate interface being added to the restart list
|
|
children = self.child_members(team_name)
|
|
for child in children:
|
|
if child not in restart_interfaces:
|
|
restart_interfaces.append(child)
|
|
else:
|
|
apply_interfaces.append(
|
|
(team_name, team_path, team_data))
|
|
update_files[team_path] = team_data
|
|
else:
|
|
logger.info('No changes required for linux team: %s' %
|
|
team_name)
|
|
if utils.diff(team_route_path, route_data):
|
|
update_files[team_route_path] = route_data
|
|
if team_name not in restart_linux_teams:
|
|
apply_routes.append((team_name, route_data))
|
|
if utils.diff(team_route6_path, route6_data):
|
|
update_files[team_route6_path] = route6_data
|
|
if team_name not in restart_linux_teams:
|
|
apply_routes.append((team_name, route6_data))
|
|
if utils.diff(team_rule_path, rule_data):
|
|
update_files[team_rule_path] = rule_data
|
|
|
|
for bond_name, bond_data in self.linuxbond_data.items():
|
|
route_data = self.route_data.get(bond_name, '')
|
|
route6_data = self.route6_data.get(bond_name, '')
|
|
rule_data = self.rule_data.get(bond_name, '')
|
|
bond_path = self.root_dir + bridge_config_path(bond_name)
|
|
bond_route_path = self.root_dir + route_config_path(bond_name)
|
|
bond_route6_path = self.root_dir + route6_config_path(bond_name)
|
|
bond_rule_path = self.root_dir + route_rule_config_path(bond_name)
|
|
all_file_names.append(bond_path)
|
|
all_file_names.append(bond_route_path)
|
|
all_file_names.append(bond_route6_path)
|
|
all_file_names.append(bond_rule_path)
|
|
children = self.child_members(bond_name)
|
|
linux_bond_children[bond_name] = children
|
|
if utils.diff(bond_path, bond_data):
|
|
if self.ifcfg_requires_restart(bond_path, bond_data):
|
|
restart_linux_bonds.append(bond_name)
|
|
# Avoid duplicate interface being added to the restart list
|
|
for child in children:
|
|
if child not in restart_interfaces:
|
|
restart_interfaces.append(child)
|
|
else:
|
|
apply_interfaces.append(
|
|
(bond_name, bond_path, bond_data))
|
|
update_files[bond_path] = bond_data
|
|
else:
|
|
logger.info('No changes required for linux bond: %s' %
|
|
bond_name)
|
|
if utils.diff(bond_route_path, route_data):
|
|
update_files[bond_route_path] = route_data
|
|
if bond_name not in restart_linux_bonds:
|
|
apply_routes.append((bond_name, route_data))
|
|
if utils.diff(bond_route6_path, route6_data):
|
|
update_files[bond_route6_path] = route6_data
|
|
if bond_name not in restart_linux_bonds:
|
|
apply_routes.append((bond_name, route6_data))
|
|
if utils.diff(bond_rule_path, rule_data):
|
|
update_files[bond_rule_path] = rule_data
|
|
|
|
# Infiniband interfaces are handled similarly to Ethernet interfaces
|
|
for interface_name, iface_data in self.ib_interface_data.items():
|
|
route_data = self.route_data.get(interface_name, '')
|
|
route6_data = self.route6_data.get(interface_name, '')
|
|
rule_data = self.rule_data.get(interface_name, '')
|
|
interface_path = self.root_dir + ifcfg_config_path(interface_name)
|
|
route_path = self.root_dir + route_config_path(interface_name)
|
|
route6_path = self.root_dir + route6_config_path(interface_name)
|
|
rule_path = self.root_dir + route_rule_config_path(interface_name)
|
|
all_file_names.append(interface_path)
|
|
all_file_names.append(route_path)
|
|
all_file_names.append(route6_path)
|
|
all_file_names.append(rule_path)
|
|
# TODO(dsneddon) determine if InfiniBand can be used with IVS
|
|
if "IVS_BRIDGE" in iface_data:
|
|
ivs_uplinks.append(interface_name)
|
|
if utils.diff(interface_path, iface_data):
|
|
if self.ifcfg_requires_restart(interface_path, iface_data):
|
|
restart_interfaces.append(interface_name)
|
|
else:
|
|
apply_interfaces.append(
|
|
(interface_name, interface_path, iface_data))
|
|
update_files[interface_path] = iface_data
|
|
else:
|
|
logger.info('No changes required for InfiniBand iface: %s' %
|
|
interface_name)
|
|
if utils.diff(route_path, route_data):
|
|
update_files[route_path] = route_data
|
|
if interface_name not in restart_interfaces:
|
|
apply_routes.append((interface_name, route_data))
|
|
if utils.diff(route6_path, route6_data):
|
|
update_files[route6_path] = route6_data
|
|
if interface_name not in restart_interfaces:
|
|
apply_routes.append((interface_name, route6_data))
|
|
if utils.diff(rule_path, rule_data):
|
|
update_files[rule_path] = rule_data
|
|
if interface_name not in restart_interfaces:
|
|
apply_rules.append((interface_name, rule_data))
|
|
|
|
# NOTE(hjensas): Process the VLAN's last so that we know if the vlan's
|
|
# parent interface is being restarted.
|
|
for vlan_name, vlan_data in self.vlan_data.items():
|
|
route_data = self.route_data.get(vlan_name, '')
|
|
route6_data = self.route6_data.get(vlan_name, '')
|
|
rule_data = self.rule_data.get(vlan_name, '')
|
|
vlan_path = self.root_dir + ifcfg_config_path(vlan_name)
|
|
vlan_route_path = self.root_dir + route_config_path(vlan_name)
|
|
vlan_route6_path = self.root_dir + route6_config_path(vlan_name)
|
|
vlan_rule_path = self.root_dir + route_rule_config_path(vlan_name)
|
|
all_file_names.append(vlan_path)
|
|
all_file_names.append(vlan_route_path)
|
|
all_file_names.append(vlan_route6_path)
|
|
all_file_names.append(vlan_rule_path)
|
|
restarts_concatenated = itertools.chain(restart_interfaces,
|
|
restart_bridges,
|
|
restart_linux_bonds,
|
|
restart_linux_teams)
|
|
if (self.parse_ifcfg(vlan_data).get('PHYSDEV') in
|
|
restarts_concatenated):
|
|
if vlan_name not in restart_vlans:
|
|
restart_vlans.append(vlan_name)
|
|
update_files[vlan_path] = vlan_data
|
|
elif utils.diff(vlan_path, vlan_data):
|
|
if self.ifcfg_requires_restart(vlan_path, vlan_data):
|
|
restart_vlans.append(vlan_name)
|
|
else:
|
|
apply_interfaces.append(
|
|
(vlan_name, vlan_path, vlan_data))
|
|
update_files[vlan_path] = vlan_data
|
|
else:
|
|
logger.info('No changes required for vlan interface: %s' %
|
|
vlan_name)
|
|
if utils.diff(vlan_route_path, route_data):
|
|
update_files[vlan_route_path] = route_data
|
|
if vlan_name not in restart_vlans:
|
|
apply_routes.append((vlan_name, route_data))
|
|
if utils.diff(vlan_route6_path, route6_data):
|
|
update_files[vlan_route6_path] = route6_data
|
|
if vlan_name not in restart_vlans:
|
|
apply_routes.append((vlan_name, route6_data))
|
|
if utils.diff(vlan_rule_path, rule_data):
|
|
update_files[vlan_rule_path] = rule_data
|
|
if vlan_name not in restart_vlans:
|
|
apply_rules.append((vlan_name, rule_data))
|
|
|
|
for ib_child_name, ib_child_data in self.ib_childs_data.items():
|
|
route_data = self.route_data.get(ib_child_name, '')
|
|
route6_data = self.route6_data.get(ib_child_name, '')
|
|
rule_data = self.rule_data.get(ib_child_name, '')
|
|
ib_child_path = self.root_dir + ifcfg_config_path(ib_child_name)
|
|
ib_child_route_path = \
|
|
self.root_dir + route_config_path(ib_child_name)
|
|
ib_child_route6_path = \
|
|
self.root_dir + route6_config_path(ib_child_name)
|
|
ib_child_rule_path = \
|
|
self.root_dir + route_rule_config_path(ib_child_name)
|
|
all_file_names.append(ib_child_path)
|
|
all_file_names.append(ib_child_route_path)
|
|
all_file_names.append(ib_child_route6_path)
|
|
all_file_names.append(ib_child_rule_path)
|
|
restarts_concatenated = itertools.chain(restart_interfaces,
|
|
restart_bridges,
|
|
restart_linux_bonds,
|
|
restart_linux_teams)
|
|
if (self.parse_ifcfg(ib_child_data).get('PHYSDEV') in
|
|
restarts_concatenated):
|
|
if ib_child_name not in restart_ib_childs:
|
|
restart_ib_childs.append(ib_child_name)
|
|
update_files[ib_child_path] = ib_child_data
|
|
elif utils.diff(ib_child_path, ib_child_data):
|
|
if self.ifcfg_requires_restart(ib_child_path, ib_child_data):
|
|
restart_ib_childs.append(ib_child_name)
|
|
else:
|
|
apply_interfaces.append(
|
|
(ib_child_name, ib_child_path, ib_child_data))
|
|
update_files[ib_child_path] = ib_child_data
|
|
else:
|
|
logger.info('No changes required for the ib child interface: '
|
|
'%s' % ib_child_name)
|
|
if utils.diff(ib_child_route_path, route_data):
|
|
update_files[ib_child_route_path] = route_data
|
|
if ib_child_name not in restart_ib_childs:
|
|
apply_routes.append((ib_child_name, route_data))
|
|
if utils.diff(ib_child_route6_path, route6_data):
|
|
update_files[ib_child_route6_path] = route6_data
|
|
if ib_child_name not in restart_ib_childs:
|
|
apply_routes.append((ib_child_name, route6_data))
|
|
if utils.diff(ib_child_rule_path, rule_data):
|
|
update_files[ib_child_rule_path] = rule_data
|
|
if ib_child_name not in restart_ib_childs:
|
|
apply_rules.append((ib_child_name, rule_data))
|
|
|
|
if self.vpp_interface_data or self.vpp_bond_data:
|
|
vpp_path = self.root_dir + vpp_config_path()
|
|
vpp_config = utils.generate_vpp_config(vpp_path, vpp_interfaces,
|
|
vpp_bonds)
|
|
if utils.diff(vpp_path, vpp_config):
|
|
restart_vpp = True
|
|
update_files[vpp_path] = vpp_config
|
|
else:
|
|
logger.info('No changes required for VPP')
|
|
|
|
if cleanup:
|
|
for ifcfg_file in glob.iglob(cleanup_pattern()):
|
|
if ifcfg_file not in all_file_names:
|
|
interface_name = ifcfg_file[len(cleanup_pattern()) - 1:]
|
|
if interface_name != 'lo':
|
|
logger.info('cleaning up interface: %s'
|
|
% interface_name)
|
|
self.ifdown(interface_name)
|
|
self.remove_config(ifcfg_file)
|
|
|
|
if activate:
|
|
for interface in apply_interfaces:
|
|
logger.debug('Running ip commands on interface: %s' %
|
|
interface[0])
|
|
commands = self.iproute2_apply_commands(interface[0],
|
|
interface[1],
|
|
interface[2])
|
|
for command in commands:
|
|
try:
|
|
args = command.split()
|
|
self.execute('Running ip %s' % command, ipcmd, *args)
|
|
except Exception as e:
|
|
logger.warning("Error in 'ip %s', restarting %s:\n%s" %
|
|
(command, interface[0], str(e)))
|
|
restart_interfaces.append(interface[0])
|
|
restart_interfaces.extend(
|
|
self.child_members(interface[0]))
|
|
break
|
|
|
|
for bridge in apply_bridges:
|
|
logger.debug('Running ip commands on bridge: %s' %
|
|
bridge[0])
|
|
commands = self.iproute2_apply_commands(bridge[0],
|
|
bridge[1],
|
|
bridge[2])
|
|
for command in commands:
|
|
try:
|
|
args = command.split()
|
|
self.execute('Running ip %s' % command, ipcmd, *args)
|
|
except Exception as e:
|
|
logger.warning("Error in 'ip %s', restarting %s:\n%s" %
|
|
(command, bridge[0], str(e)))
|
|
restart_bridges.append(bridge[0])
|
|
restart_interfaces.extend(
|
|
self.child_members(bridge[0]))
|
|
break
|
|
|
|
for interface in apply_routes:
|
|
logger.debug('Applying routes for interface %s' % interface[0])
|
|
filename = self.root_dir + route_config_path(interface[0])
|
|
commands = self.iproute2_route_commands(filename, interface[1])
|
|
for command in commands:
|
|
args = command.split()
|
|
try:
|
|
if len(args) > 0:
|
|
self.execute('Running ip %s' % command, ipcmd,
|
|
*args)
|
|
except Exception as e:
|
|
logger.warning("Error in 'ip %s', restarting %s:\n%s" %
|
|
(command, interface[0], str(e)))
|
|
restart_interfaces.append(interface[0])
|
|
restart_interfaces.extend(
|
|
self.child_members(interface[0]))
|
|
break
|
|
|
|
for interface in apply_rules:
|
|
logger.debug('Applying rules for interface %s' % interface[0])
|
|
filename = self.root_dir + route_rule_config_path(interface[0])
|
|
commands = self.iproute2_rule_commands(filename, interface[1])
|
|
for command in commands:
|
|
args = command.split()
|
|
try:
|
|
if len(args) > 0:
|
|
self.execute('Running ip %s' % command, ipcmd,
|
|
*args)
|
|
except Exception as e:
|
|
logger.warning("Error in 'ip %s', restarting %s:\n%s" %
|
|
(command, interface[0], str(e)))
|
|
restart_interfaces.append(interface[0])
|
|
restart_interfaces.extend(
|
|
self.child_members(interface[0]))
|
|
break
|
|
|
|
for vlan in restart_vlans:
|
|
self.ifdown(vlan)
|
|
|
|
for ib_child in restart_ib_childs:
|
|
self.ifdown(ib_child)
|
|
|
|
for interface in restart_interfaces:
|
|
self.ifdown(interface)
|
|
for bond in linux_bond_children:
|
|
if interface in linux_bond_children[bond]:
|
|
if bond not in restart_linux_bonds:
|
|
start_linux_bonds.append(bond)
|
|
|
|
for linux_bond in restart_linux_bonds:
|
|
self.ifdown(linux_bond)
|
|
|
|
for linux_team in restart_linux_teams:
|
|
self.ifdown(linux_team)
|
|
|
|
for bridge in restart_bridges:
|
|
self.ifdown(bridge, iftype='bridge')
|
|
|
|
for vpp_interface in vpp_interfaces:
|
|
self.ifdown(vpp_interface.name)
|
|
|
|
for oldname, newname in self.renamed_interfaces.items():
|
|
self.ifrename(oldname, newname)
|
|
|
|
# DPDK initialization is done before running os-net-config, to make
|
|
# the DPDK ports available when enabled. DPDK Hotplug support is
|
|
# supported only in OvS 2.7 version. Until then, OvS needs to be
|
|
# restarted after adding a DPDK port. This change will be removed
|
|
# on migration to OvS 2.7 where DPDK Hotplug support is available.
|
|
if ovs_needs_restart:
|
|
msg = "Restart openvswitch"
|
|
self.execute(msg, '/usr/bin/systemctl',
|
|
'restart', 'openvswitch')
|
|
|
|
for location, data in update_files.items():
|
|
self.write_config(location, data)
|
|
|
|
if self.route_table_data:
|
|
location = route_table_config_path()
|
|
data = self.generate_route_table_config(self.route_table_data)
|
|
self.write_config(location, data)
|
|
|
|
if ivs_uplinks or ivs_interfaces:
|
|
location = ivs_config_path()
|
|
data = self.generate_ivs_config(ivs_uplinks, ivs_interfaces)
|
|
if (utils.diff(location, data)):
|
|
self.write_config(location, data)
|
|
msg = "Restart ivs"
|
|
self.execute(msg, '/usr/bin/systemctl',
|
|
'restart', 'ivs')
|
|
|
|
if nfvswitch_interfaces or nfvswitch_internal_ifaces:
|
|
location = nfvswitch_config_path()
|
|
data = self.generate_nfvswitch_config(nfvswitch_interfaces,
|
|
nfvswitch_internal_ifaces)
|
|
if (utils.diff(location, data)):
|
|
self.write_config(location, data)
|
|
msg = "Restart nfvswitch"
|
|
self.execute(msg, '/usr/bin/systemctl',
|
|
'restart', 'nfvswitch')
|
|
|
|
if activate:
|
|
for linux_team in restart_linux_teams:
|
|
self.ifup(linux_team)
|
|
|
|
for bridge in restart_bridges:
|
|
self.ifup(bridge, iftype='bridge')
|
|
|
|
# If dhclient is running and dhcp not set, stop dhclient
|
|
for interface in stop_dhclient_interfaces:
|
|
logger.debug("Calling stop_dhclient_interfaces() for %s" %
|
|
interface)
|
|
if not self.noop:
|
|
stop_dhclient_process(interface)
|
|
|
|
for interface in restart_interfaces:
|
|
self.ifup(interface)
|
|
|
|
for linux_bond in start_linux_bonds:
|
|
if linux_bond not in restart_linux_bonds:
|
|
restart_linux_bonds.append(linux_bond)
|
|
|
|
for linux_bond in restart_linux_bonds:
|
|
self.ifup(linux_bond)
|
|
|
|
for bond in self.bond_primary_ifaces:
|
|
self.ovs_appctl('bond/set-active-slave', bond,
|
|
self.bond_primary_ifaces[bond])
|
|
|
|
if ivs_uplinks or ivs_interfaces:
|
|
logger.info("Attach to ivs with "
|
|
"uplinks: %s, "
|
|
"interfaces: %s" %
|
|
(ivs_uplinks, ivs_interfaces))
|
|
for ivs_uplink in ivs_uplinks:
|
|
self.ifup(ivs_uplink)
|
|
for ivs_interface in ivs_interfaces:
|
|
self.ifup(ivs_interface)
|
|
|
|
if nfvswitch_interfaces or nfvswitch_internal_ifaces:
|
|
logger.info("Attach to nfvswitch with "
|
|
"interfaces: %s, "
|
|
"internal interfaces: %s" %
|
|
(nfvswitch_interfaces, nfvswitch_internal_ifaces))
|
|
for nfvswitch_interface in nfvswitch_interfaces:
|
|
self.ifup(nfvswitch_interface)
|
|
for nfvswitch_internal in nfvswitch_internal_ifaces:
|
|
self.ifup(nfvswitch_internal)
|
|
|
|
for ib_child in restart_ib_childs:
|
|
self.ifup(ib_child)
|
|
|
|
for vlan in restart_vlans:
|
|
self.ifup(vlan)
|
|
|
|
if not self.noop:
|
|
if restart_vpp:
|
|
logger.info('Restarting VPP')
|
|
utils.restart_vpp(vpp_interfaces)
|
|
|
|
if self.vpp_interface_data:
|
|
logger.info('Updating VPP mapping')
|
|
utils.update_vpp_mapping(vpp_interfaces, vpp_bonds)
|
|
|
|
if self.errors:
|
|
message = 'Failure(s) occurred when applying configuration'
|
|
logger.error(message)
|
|
for e in self.errors:
|
|
logger.error('stdout: %s, stderr: %s', e.stdout, e.stderr)
|
|
raise os_net_config.ConfigurationError(message)
|
|
|
|
return update_files
|