Add Policy Based Routing Support to os-net-config

Add support for routing tables and rules, as well as a
property to specify a routing table for each route to
the impl_ifcfg implementation.

This change adds a new top-level object in the
network_config with type "route_table" to specify
which route table IDs and names should be added to
the /etc/iproute2/rt_table file.

This change adds a "table" property to routes for each
interface type, that allows the selection of which
routing table to apply the route to.

This change also adds a "rules" property to each
interface type, which can be used to specify a list
of rules. Each rule contains a rule string and an
optional comment, both of which will be added to the
/etc/sysconfig/network-scripts/rule-<iface> file.

This change includes tests and changes to the schema
for validation.

Note that this change was based on the abandoned patch
https://review.openstack.org/#/c/575712
and was recreated when that patch diverged too far
from master branch.

Change-Id: I6906d3b6923845af177faba6579fa255a2195bec
Closes-bug: #1783297
This commit is contained in:
Dan Sneddon 2018-12-19 15:06:39 -08:00
parent 0607a2ac29
commit 35832347e1
10 changed files with 828 additions and 229 deletions

View File

@ -0,0 +1,52 @@
network_config:
-
type: route_table
name: custom
table_id: 200
-
type: route_table
name: alternate
table_id: 201
-
type: interface
name: em1
use_dhcp: false
addresses:
-
ip_netmask: 192.0.2.1/24
routes:
-
ip_netmask: 10.1.3.0/24
next_hop: 192.0.2.5
route_options: "metric 10"
table: 200 # Use table ID or table name
-
ip_netmask: 0.0.0.0/0
next_hop: 192.0.2.254
default: true
table: 200
rules:
- rule: "iif em1 table 200"
comment: "Route incoming traffic to em1 with table 200"
- rule: "from 192.0.2.0/24 table 200"
comment: "Route all traffic from 192.0.2.0/24 with table 200"
- rule: "add blackhole from 172.19.40.0/24 table 200"
- rule: "add unreachable iif em1 from 192.168.1.0/24"
-
type: interface
name: em2
use_dhcp: false
addresses:
- ip_netmask: 10.0.2.1/24
routes:
-
ip_netmask: 10.1.3.0/24
next_hop: 10.0.2.253
table: alternate # Use table ID or table name
-
default: true
next_hop: 10.0.3.254
route_options: "table alternate"
rules:
- rule: "iif em2 table alternate"
- rule: "from 10.0.2.0/24 table alternate"

View File

@ -50,6 +50,8 @@ class NetConfig(object):
:param obj: The object to add.
"""
if isinstance(obj, objects.RouteTable):
self.add_route_table(obj)
if isinstance(obj, objects.Interface):
self.add_interface(obj)
elif isinstance(obj, objects.Vlan):
@ -115,6 +117,13 @@ class NetConfig(object):
elif isinstance(obj, objects.ContrailVrouterDpdk):
self.add_contrail_vrouter_dpdk(obj)
def add_route_table(self, route_table):
"""Add a route table object to the net config object.
:param route_table: The RouteTable object to add.
"""
raise NotImplementedError("add_route_table is not implemented.")
def add_interface(self, interface):
"""Add an Interface object to the net config object.

View File

@ -235,8 +235,9 @@ def main(argv=sys.argv):
return 1
for iface_json in iface_array:
iface_json.update({'nic_mapping': iface_mapping})
iface_json.update({'persist_mapping': persist_mapping})
if iface_json.get('type') != 'route_table':
iface_json.update({'nic_mapping': iface_mapping})
iface_json.update({'persist_mapping': persist_mapping})
validation_errors = validator.validate_config(iface_array)
if validation_errors:

View File

@ -31,6 +31,17 @@ 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
@ -68,6 +79,14 @@ 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-*"
@ -117,6 +136,8 @@ class IfcfgNetConfig(os_net_config.NetConfig):
self.vlan_data = {}
self.route_data = {}
self.route6_data = {}
self.route_table_data = {}
self.rule_data = {}
self.bridge_data = {}
self.linuxbridge_data = {}
self.linuxbond_data = {}
@ -594,33 +615,62 @@ class IfcfgNetConfig(os_net_config.NetConfig):
first_line6 = ""
for route in routes:
options = ""
table = ""
if route.route_options:
options = " %s" % (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\n" % (
first_line = "default via %s dev %s%s%s\n" % (
route.next_hop, interface_name,
options)
table, options)
else:
data += "%s via %s dev %s%s\n" % (
data += "%s via %s dev %s%s%s\n" % (
route.ip_netmask, route.next_hop,
interface_name, options)
interface_name, table, options)
else:
# Route is an IPv6 route
if route.default:
first_line6 = "default via %s dev %s%s\n" % (
first_line6 = "default via %s dev %s%s%s\n" % (
route.next_hop, interface_name,
options)
table, options)
else:
data6 += "%s via %s dev %s%s\n" % (
data6 += "%s via %s dev %s%s%s\n" % (
route.ip_netmask, route.next_hop,
interface_name, options)
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.
@ -632,6 +682,8 @@ class IfcfgNetConfig(os_net_config.NetConfig):
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"
@ -649,6 +701,8 @@ class IfcfgNetConfig(os_net_config.NetConfig):
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.
@ -661,6 +715,8 @@ class IfcfgNetConfig(os_net_config.NetConfig):
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.
@ -674,6 +730,8 @@ class IfcfgNetConfig(os_net_config.NetConfig):
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.
@ -686,6 +744,8 @@ class IfcfgNetConfig(os_net_config.NetConfig):
self.bridge_data[bridge.name] = data
if bridge.routes:
self._add_routes(bridge.name, bridge.routes)
if bridge.routes:
self._add_rules(bridge.name, bridge.rules)
def add_ovs_user_bridge(self, bridge):
"""Add an OvsUserBridge object to the net config object.
@ -698,6 +758,8 @@ class IfcfgNetConfig(os_net_config.NetConfig):
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.
@ -710,6 +772,8 @@ class IfcfgNetConfig(os_net_config.NetConfig):
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.
@ -744,6 +808,8 @@ class IfcfgNetConfig(os_net_config.NetConfig):
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.
@ -756,6 +822,8 @@ class IfcfgNetConfig(os_net_config.NetConfig):
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.
@ -768,6 +836,8 @@ class IfcfgNetConfig(os_net_config.NetConfig):
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.
@ -800,6 +870,8 @@ class IfcfgNetConfig(os_net_config.NetConfig):
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"
@ -847,6 +919,8 @@ class IfcfgNetConfig(os_net_config.NetConfig):
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)
def add_sriov_pf(self, sriov_pf):
"""Add a SriovPF object to the net config object
@ -872,6 +946,8 @@ class IfcfgNetConfig(os_net_config.NetConfig):
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
@ -917,8 +993,9 @@ class IfcfgNetConfig(os_net_config.NetConfig):
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)
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
@ -947,6 +1024,9 @@ class IfcfgNetConfig(os_net_config.NetConfig):
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 generate_ivs_config(self, ivs_uplinks, ivs_interfaces):
"""Generate configuration content for ivs."""
@ -991,6 +1071,56 @@ class IfcfgNetConfig(os_net_config.NetConfig):
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 = utils.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.
@ -1031,9 +1161,11 @@ class IfcfgNetConfig(os_net_config.NetConfig):
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)
@ -1066,16 +1198,21 @@ class IfcfgNetConfig(os_net_config.NetConfig):
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
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):
@ -1095,16 +1232,21 @@ class IfcfgNetConfig(os_net_config.NetConfig):
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
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):
@ -1124,16 +1266,21 @@ class IfcfgNetConfig(os_net_config.NetConfig):
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
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)
@ -1156,16 +1303,21 @@ class IfcfgNetConfig(os_net_config.NetConfig):
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
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)
@ -1188,16 +1340,21 @@ class IfcfgNetConfig(os_net_config.NetConfig):
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
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)
@ -1221,16 +1378,21 @@ class IfcfgNetConfig(os_net_config.NetConfig):
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)
if utils.diff(bond_path, bond_data):
if self.ifcfg_requires_restart(bond_path, bond_data):
restart_linux_bonds.append(bond_name)
@ -1254,17 +1416,22 @@ class IfcfgNetConfig(os_net_config.NetConfig):
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)
@ -1286,18 +1453,23 @@ class IfcfgNetConfig(os_net_config.NetConfig):
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
# 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,
@ -1325,6 +1497,8 @@ class IfcfgNetConfig(os_net_config.NetConfig):
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 self.vpp_interface_data or self.vpp_bond_data:
vpp_path = self.root_dir + vpp_config_path()
@ -1432,6 +1606,11 @@ class IfcfgNetConfig(os_net_config.NetConfig):
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)

View File

@ -39,6 +39,10 @@ class InvalidConfigException(ValueError):
def object_from_json(json):
obj_type = json.get("type")
if obj_type == "route_table":
return RouteTable.from_json(json)
if obj_type == "route_rule":
return RouteRule.from_json(json)
if obj_type == "interface":
return Interface.from_json(json)
elif obj_type == "vlan":
@ -222,11 +226,12 @@ class Route(object):
"""Base class for network routes."""
def __init__(self, next_hop, ip_netmask="", default=False,
route_options=""):
route_options="", route_table=None):
self.next_hop = next_hop
self.ip_netmask = ip_netmask
self.default = default
self.route_options = route_options
self.route_table = route_table
@staticmethod
def from_json(json):
@ -249,7 +254,9 @@ class Route(object):
ip_netmask = json.get('ip_netmask', json.get('destination', ""))
route_options = json.get('route_options', "")
default = strutils.bool_from_string(str(json.get('default', False)))
return Route(next_hop, ip_netmask, default, route_options)
route_options = json.get('route_options', "")
route_table = json.get('table', "")
return Route(next_hop, ip_netmask, default, route_options, route_table)
class Address(object):
@ -269,15 +276,56 @@ class Address(object):
return Address(ip_netmask)
class RouteRule(object):
"""Base class for route rules."""
def __init__(self, rule, comment=""):
self.rule = rule
self.comment = comment
@staticmethod
def from_json(json):
rule = _get_required_field(json, 'rule', 'RouteRule')
comment = json.get('comment', "")
return RouteRule(rule, comment)
class RouteTable(object):
"""Base class for route tables for policy-based routing."""
def __init__(self, name, table_id):
self.name = name
self.table_id = table_id
@staticmethod
def from_json(json):
name = _get_required_field(json, 'name', 'RouteTable')
table_id = _get_required_field(json, 'table_id', 'RouteTable')
reserved_ids = [0, 253, 254, 255]
reserved_names = ['unspec', 'default', 'main', 'local']
if table_id in reserved_ids:
msg = 'Route table "%s" conflicts with reserved table "%s %s"'\
% (table_id, table_id,
reserved_names[reserved_ids.index(table_id)])
raise InvalidConfigException(msg)
elif name in reserved_names:
msg = 'Route table "%s" conflicts with reserved table "%s %s"'\
% (name, reserved_ids[reserved_names.index(name)], name)
raise InvalidConfigException(msg)
return RouteTable(name, table_id)
class _BaseOpts(object):
"""Base abstraction for logical port options."""
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
routes=None, mtu=None, primary=False, nic_mapping=None,
persist_mapping=False, defroute=True, dhclient_args=None,
dns_servers=None, nm_controlled=False, onboot=True):
routes=None, rules=None, mtu=None, primary=False,
nic_mapping=None, persist_mapping=False, defroute=True,
dhclient_args=None, dns_servers=None, nm_controlled=False,
onboot=True):
addresses = addresses or []
routes = routes or []
rules = rules or []
dns_servers = dns_servers or []
mapped_nic_names = mapped_nics(nic_mapping)
self.hwaddr = None
@ -307,6 +355,7 @@ class _BaseOpts(object):
self.use_dhcpv6 = use_dhcpv6
self.addresses = addresses
self.routes = routes
self.rules = rules
self.primary = primary
self.defroute = defroute
self.dhclient_args = dhclient_args
@ -355,6 +404,7 @@ class _BaseOpts(object):
primary = strutils.bool_from_string(str(json.get('primary', False)))
addresses = []
routes = []
rules = []
# addresses
addresses_json = json.get('addresses')
@ -376,15 +426,25 @@ class _BaseOpts(object):
msg = 'Routes must be a list.'
raise InvalidConfigException(msg)
# rules
rules_json = json.get('rules')
if rules_json:
if isinstance(rules_json, list):
for rule in rules_json:
rules.append(RouteRule.from_json(rule))
else:
msg = 'Routes must be a list.'
raise InvalidConfigException(msg)
nic_mapping = json.get('nic_mapping')
persist_mapping = json.get('persist_mapping')
if include_primary:
return (use_dhcp, use_dhcpv6, addresses, routes, mtu, primary,
nic_mapping, persist_mapping, defroute, dhclient_args,
dns_servers, nm_controlled, onboot)
return (use_dhcp, use_dhcpv6, addresses, routes, rules, mtu,
primary, nic_mapping, persist_mapping, defroute,
dhclient_args, dns_servers, nm_controlled, onboot)
else:
return (use_dhcp, use_dhcpv6, addresses, routes, mtu,
return (use_dhcp, use_dhcpv6, addresses, routes, rules, mtu,
nic_mapping, persist_mapping, defroute, dhclient_args,
dns_servers, nm_controlled, onboot)
@ -393,16 +453,17 @@ class Interface(_BaseOpts):
"""Base class for network interfaces."""
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
routes=None, mtu=None, primary=False, nic_mapping=None,
persist_mapping=False, defroute=True, dhclient_args=None,
dns_servers=None, nm_controlled=False, onboot=True,
ethtool_opts=None, hotplug=False):
routes=None, rules=None, mtu=None, primary=False,
nic_mapping=None, persist_mapping=False, defroute=True,
dhclient_args=None, dns_servers=None, nm_controlled=False,
onboot=True, ethtool_opts=None, hotplug=False):
addresses = addresses or []
routes = routes or []
rules = rules or []
dns_servers = dns_servers or []
super(Interface, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
routes, mtu, primary, nic_mapping,
persist_mapping, defroute,
routes, rules, mtu, primary,
nic_mapping, persist_mapping, defroute,
dhclient_args, dns_servers,
nm_controlled, onboot)
self.ethtool_opts = ethtool_opts
@ -426,16 +487,17 @@ class Vlan(_BaseOpts):
"""
def __init__(self, device, vlan_id, use_dhcp=False, use_dhcpv6=False,
addresses=None, routes=None, mtu=None, primary=False,
nic_mapping=None, persist_mapping=False, defroute=True,
dhclient_args=None, dns_servers=None, nm_controlled=False,
onboot=True):
addresses=None, routes=None, rules=None, mtu=None,
primary=False, nic_mapping=None, persist_mapping=False,
defroute=True, dhclient_args=None, dns_servers=None,
nm_controlled=False, onboot=True):
addresses = addresses or []
routes = routes or []
rules = rules or []
dns_servers = dns_servers or []
name = 'vlan%i' % vlan_id
super(Vlan, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
routes, mtu, primary, nic_mapping,
routes, rules, mtu, primary, nic_mapping,
persist_mapping, defroute, dhclient_args,
dns_servers, nm_controlled, onboot)
self.vlan_id = int(vlan_id)
@ -458,20 +520,21 @@ class IvsInterface(_BaseOpts):
"""Base class for ivs interfaces."""
def __init__(self, vlan_id, name='ivs', use_dhcp=False, use_dhcpv6=False,
addresses=None, routes=None, mtu=1500, primary=False,
nic_mapping=None, persist_mapping=False, defroute=True,
dhclient_args=None, dns_servers=None, nm_controlled=False,
onboot=True):
addresses=None, routes=None, rules=None, mtu=1500,
primary=False, nic_mapping=None, persist_mapping=False,
defroute=True, dhclient_args=None, dns_servers=None,
nm_controlled=False, onboot=True):
addresses = addresses or []
routes = routes or []
rules = rules or []
dns_servers = dns_servers or []
name_vlan = '%s%i' % (name, vlan_id)
super(IvsInterface, self).__init__(name_vlan, use_dhcp, use_dhcpv6,
addresses, routes, mtu, primary,
nic_mapping, persist_mapping,
defroute, dhclient_args,
dns_servers, nm_controlled,
onboot)
addresses, routes, rules, mtu,
primary, nic_mapping,
persist_mapping, defroute,
dhclient_args, dns_servers,
nm_controlled, onboot)
self.vlan_id = int(vlan_id)
@staticmethod
@ -486,20 +549,22 @@ class NfvswitchInternal(_BaseOpts):
"""Base class for nfvswitch internal interfaces."""
def __init__(self, vlan_id, name='nfvswitch', use_dhcp=False,
use_dhcpv6=False, addresses=None, routes=None, mtu=1500,
primary=False, nic_mapping=None, persist_mapping=False,
defroute=True, dhclient_args=None, dns_servers=None,
nm_controlled=False, onboot=True):
use_dhcpv6=False, addresses=None, routes=None, rules=None,
mtu=1500, primary=False, nic_mapping=None,
persist_mapping=False, defroute=True, dhclient_args=None,
dns_servers=None, nm_controlled=False, onboot=True):
addresses = addresses or []
routes = routes or []
rules = rules or []
dns_servers = dns_servers or []
name_vlan = '%s%i' % (name, vlan_id)
super(NfvswitchInternal, self).__init__(name_vlan, use_dhcp,
use_dhcpv6, addresses, routes,
mtu, primary, nic_mapping,
persist_mapping, defroute,
dhclient_args, dns_servers,
nm_controlled, onboot)
rules, mtu, primary,
nic_mapping, persist_mapping,
defroute, dhclient_args,
dns_servers, nm_controlled,
onboot)
self.vlan_id = int(vlan_id)
@staticmethod
@ -514,19 +579,21 @@ class OvsBridge(_BaseOpts):
"""Base class for OVS bridges."""
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
routes=None, mtu=None, members=None, ovs_options=None,
ovs_extra=None, nic_mapping=None, persist_mapping=False,
defroute=True, dhclient_args=None, dns_servers=None,
nm_controlled=False, onboot=True, fail_mode=None):
routes=None, rules=None, mtu=None, members=None,
ovs_options=None, ovs_extra=None, nic_mapping=None,
persist_mapping=False, defroute=True, dhclient_args=None,
dns_servers=None, nm_controlled=False, onboot=True,
fail_mode=None):
check_ovs_installed(self.__class__.__name__)
addresses = addresses or []
routes = routes or []
rules = rules or []
members = members or []
dns_servers = dns_servers or []
super(OvsBridge, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
routes, mtu, False, nic_mapping,
routes, rules, mtu, False, nic_mapping,
persist_mapping, defroute,
dhclient_args, dns_servers,
nm_controlled, onboot)
@ -574,9 +641,8 @@ class OvsBridge(_BaseOpts):
@staticmethod
def from_json(json):
name = _get_required_field(json, 'name', 'OvsBridge')
(use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping,
persist_mapping, defroute,
dhclient_args, dns_servers,
(use_dhcp, use_dhcpv6, addresses, routes, rules, mtu, nic_mapping,
persist_mapping, defroute, dhclient_args, dns_servers,
nm_controlled, onboot) = _BaseOpts.base_opts_from_json(
json, include_primary=False)
ovs_options = json.get('ovs_options')
@ -588,8 +654,8 @@ class OvsBridge(_BaseOpts):
members = _update_members(json, nic_mapping, persist_mapping)
return OvsBridge(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6,
addresses=addresses, routes=routes, mtu=mtu,
members=members, ovs_options=ovs_options,
addresses=addresses, routes=routes, rules=rules,
mtu=mtu, members=members, ovs_options=ovs_options,
ovs_extra=ovs_extra, nic_mapping=nic_mapping,
persist_mapping=persist_mapping, defroute=defroute,
dhclient_args=dhclient_args, dns_servers=dns_servers,
@ -601,19 +667,20 @@ class OvsUserBridge(_BaseOpts):
"""Base class for OVS User bridges."""
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
routes=None, mtu=None, members=None, ovs_options=None,
ovs_extra=None, nic_mapping=None, persist_mapping=False,
defroute=True, dhclient_args=None, dns_servers=None,
nm_controlled=False, onboot=True, fail_mode=None):
routes=None, rules=None, mtu=None, members=None,
ovs_options=None, ovs_extra=None, nic_mapping=None,
persist_mapping=False, defroute=True, dhclient_args=None,
dns_servers=None, nm_controlled=False, onboot=True,
fail_mode=None):
check_ovs_installed(self.__class__.__name__)
super(OvsUserBridge, self).__init__(name, use_dhcp, use_dhcpv6,
addresses, routes, mtu, False,
nic_mapping, persist_mapping,
defroute, dhclient_args,
dns_servers, nm_controlled,
onboot)
addresses, routes, rules, mtu,
False, nic_mapping,
persist_mapping, defroute,
dhclient_args, dns_servers,
nm_controlled, onboot)
self.members = members or []
self.ovs_options = ovs_options
ovs_extra = ovs_extra or []
@ -638,9 +705,8 @@ class OvsUserBridge(_BaseOpts):
@staticmethod
def from_json(json):
name = _get_required_field(json, 'name', 'OvsUserBridge')
(use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping,
persist_mapping, defroute,
dhclient_args, dns_servers,
(use_dhcp, use_dhcpv6, addresses, routes, rules, mtu, nic_mapping,
persist_mapping, defroute, dhclient_args, dns_servers,
nm_controlled, onboot) = _BaseOpts.base_opts_from_json(
json, include_primary=False)
ovs_options = json.get('ovs_options')
@ -652,8 +718,8 @@ class OvsUserBridge(_BaseOpts):
members = _update_members(json, nic_mapping, persist_mapping)
return OvsUserBridge(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6,
addresses=addresses, routes=routes, mtu=mtu,
members=members, ovs_options=ovs_options,
addresses=addresses, routes=routes, rules=rules,
mtu=mtu, members=members, ovs_options=ovs_options,
ovs_extra=ovs_extra, nic_mapping=nic_mapping,
persist_mapping=persist_mapping,
defroute=defroute, dhclient_args=dhclient_args,
@ -666,15 +732,17 @@ class LinuxBridge(_BaseOpts):
"""Base class for Linux bridges."""
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
routes=None, mtu=None, members=None, nic_mapping=None,
persist_mapping=False, defroute=True, dhclient_args=None,
dns_servers=None, nm_controlled=False, onboot=True):
routes=None, rules=None, mtu=None, members=None,
nic_mapping=None, persist_mapping=False, defroute=True,
dhclient_args=None, dns_servers=None, nm_controlled=False,
onboot=True):
addresses = addresses or []
routes = routes or []
rules = rules or []
members = members or []
dns_servers = dns_servers or []
super(LinuxBridge, self).__init__(name, use_dhcp, use_dhcpv6,
addresses, routes, mtu, False,
addresses, routes, rules, mtu, False,
nic_mapping, persist_mapping,
defroute, dhclient_args, dns_servers,
nm_controlled, onboot)
@ -694,16 +762,15 @@ class LinuxBridge(_BaseOpts):
@staticmethod
def from_json(json):
name = _get_required_field(json, 'name', 'LinuxBridge')
(use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping,
persist_mapping, defroute, dhclient_args,
dns_servers, nm_controlled, onboot) = _BaseOpts.base_opts_from_json(
json, include_primary=False)
(use_dhcp, use_dhcpv6, addresses, routes, rules, mtu, nic_mapping,
persist_mapping, defroute, dhclient_args, dns_servers, nm_controlled,
onboot) = _BaseOpts.base_opts_from_json(json, include_primary=False)
members = _update_members(json, nic_mapping, persist_mapping)
return LinuxBridge(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6,
addresses=addresses, routes=routes, mtu=mtu,
members=members, nic_mapping=nic_mapping,
addresses=addresses, routes=routes, rules=rules,
mtu=mtu, members=members, nic_mapping=nic_mapping,
persist_mapping=persist_mapping, defroute=defroute,
dhclient_args=dhclient_args,
dns_servers=dns_servers,
@ -724,16 +791,17 @@ class IvsBridge(_BaseOpts):
"""
def __init__(self, name='ivs', use_dhcp=False, use_dhcpv6=False,
addresses=None, routes=None, mtu=1500, members=None,
nic_mapping=None, persist_mapping=False, defroute=True,
dhclient_args=None, dns_servers=None, nm_controlled=False,
onboot=True):
addresses=None, rules=None, routes=None,
mtu=1500, members=None, nic_mapping=None,
persist_mapping=False, defroute=True, dhclient_args=None,
dns_servers=None, nm_controlled=False, onboot=True):
addresses = addresses or []
routes = routes or []
rules = rules or []
members = members or []
dns_servers = dns_servers or []
super(IvsBridge, self).__init__(name, use_dhcp, use_dhcpv6,
addresses, routes, mtu, False,
addresses, routes, rules, mtu, False,
nic_mapping, persist_mapping,
defroute, dhclient_args, dns_servers,
nm_controlled, onboot)
@ -749,16 +817,15 @@ class IvsBridge(_BaseOpts):
@staticmethod
def from_json(json):
name = 'ivs'
(use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping,
persist_mapping, defroute, dhclient_args,
dns_servers, nm_controlled, onboot) = _BaseOpts.base_opts_from_json(
json, include_primary=False)
(use_dhcp, use_dhcpv6, addresses, routes, rules, mtu, nic_mapping,
persist_mapping, defroute, dhclient_args, dns_servers, nm_controlled,
onboot) = _BaseOpts.base_opts_from_json(json, include_primary=False)
members = _update_members(json, nic_mapping, persist_mapping)
return IvsBridge(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6,
addresses=addresses, routes=routes, mtu=mtu,
members=members, nic_mapping=nic_mapping,
addresses=addresses, routes=routes, rules=rules,
mtu=mtu, members=members, nic_mapping=nic_mapping,
persist_mapping=persist_mapping, defroute=defroute,
dhclient_args=dhclient_args,
dns_servers=dns_servers, nm_controlled=nm_controlled,
@ -774,20 +841,21 @@ class NfvswitchBridge(_BaseOpts):
"""
def __init__(self, name='nfvswitch', use_dhcp=False, use_dhcpv6=False,
addresses=None, routes=None, mtu=1500, members=None,
nic_mapping=None, persist_mapping=False, defroute=True,
dhclient_args=None, dns_servers=None, nm_controlled=False,
onboot=True, options=""):
addresses=None, routes=None, rules=None, mtu=1500,
members=None, nic_mapping=None, persist_mapping=False,
defroute=True, dhclient_args=None, dns_servers=None,
nm_controlled=False, onboot=True, options=""):
addresses = addresses or []
routes = routes or []
rules = rules or []
members = members or []
dns_servers = dns_servers or []
super(NfvswitchBridge, self).__init__(name, use_dhcp, use_dhcpv6,
addresses, routes, mtu, False,
nic_mapping, persist_mapping,
defroute, dhclient_args,
dns_servers, nm_controlled,
onboot)
addresses, routes, rules, mtu,
False, nic_mapping,
persist_mapping, defroute,
dhclient_args, dns_servers,
nm_controlled, onboot)
self.options = options
self.members = members
for member in self.members:
@ -801,10 +869,9 @@ class NfvswitchBridge(_BaseOpts):
@staticmethod
def from_json(json):
name = 'nfvswitch'
(use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping,
persist_mapping, defroute, dhclient_args,
dns_servers, nm_controlled, onboot) = _BaseOpts.base_opts_from_json(
json, include_primary=False)
(use_dhcp, use_dhcpv6, addresses, routes, rules, mtu, nic_mapping,
persist_mapping, defroute, dhclient_args, dns_servers, nm_controlled,
onboot) = _BaseOpts.base_opts_from_json(json, include_primary=False)
members = _update_members(json, nic_mapping, persist_mapping)
@ -814,8 +881,9 @@ class NfvswitchBridge(_BaseOpts):
raise InvalidConfigException(msg)
return NfvswitchBridge(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6,
addresses=addresses, routes=routes, mtu=mtu,
members=members, nic_mapping=nic_mapping,
addresses=addresses, routes=routes, rules=rules,
mtu=mtu, members=members,
nic_mapping=nic_mapping,
persist_mapping=persist_mapping,
defroute=defroute, dhclient_args=dhclient_args,
dns_servers=dns_servers,
@ -827,17 +895,18 @@ class LinuxTeam(_BaseOpts):
"""Base class for Linux bonds using teamd."""
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
routes=None, mtu=None, primary=False, members=None,
bonding_options=None, nic_mapping=None, persist_mapping=False,
defroute=True, dhclient_args=None, dns_servers=None,
nm_controlled=False, onboot=True):
routes=None, rules=None, mtu=None, primary=False,
members=None, bonding_options=None, nic_mapping=None,
persist_mapping=False, defroute=True, dhclient_args=None,
dns_servers=None, nm_controlled=False, onboot=True):
addresses = addresses or []
routes = routes or []
rules = rules or []
members = members or []
dns_servers = dns_servers or []
super(LinuxTeam, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
routes, mtu, primary, nic_mapping,
persist_mapping, defroute,
routes, rules, mtu, primary,
nic_mapping, persist_mapping, defroute,
dhclient_args, dns_servers,
nm_controlled, onboot)
self.members = members
@ -856,17 +925,17 @@ class LinuxTeam(_BaseOpts):
@staticmethod
def from_json(json):
name = _get_required_field(json, 'name', 'LinuxTeam')
(use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping,
persist_mapping, defroute, dhclient_args,
dns_servers, nm_controlled, onboot) = _BaseOpts.base_opts_from_json(
json, include_primary=False)
bonding_options = json.get('bonding_options')
(use_dhcp, use_dhcpv6, addresses, routes, rules, mtu, nic_mapping,
persist_mapping, defroute, dhclient_args, dns_servers, nm_controlled,
onboot) = _BaseOpts.base_opts_from_json(json, include_primary=False)
bonding_options = json.get('bonding_options')
members = _update_members(json, nic_mapping, persist_mapping)
return LinuxTeam(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6,
addresses=addresses, routes=routes, mtu=mtu,
members=members, bonding_options=bonding_options,
addresses=addresses, routes=routes, rules=rules,
mtu=mtu, members=members,
bonding_options=bonding_options,
nic_mapping=nic_mapping,
persist_mapping=persist_mapping, defroute=defroute,
dhclient_args=dhclient_args, dns_servers=dns_servers,
@ -877,17 +946,18 @@ class LinuxBond(_BaseOpts):
"""Base class for Linux bonds."""
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
routes=None, mtu=None, primary=False, members=None,
bonding_options=None, nic_mapping=None, persist_mapping=False,
defroute=True, dhclient_args=None, dns_servers=None,
nm_controlled=False, onboot=True):
routes=None, rules=None, mtu=None, primary=False,
members=None, bonding_options=None, nic_mapping=None,
persist_mapping=False, defroute=True, dhclient_args=None,
dns_servers=None, nm_controlled=False, onboot=True):
addresses = addresses or []
routes = routes or []
rules = rules or []
members = members or []
dns_servers = dns_servers or []
super(LinuxBond, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
routes, mtu, primary, nic_mapping,
persist_mapping, defroute,
routes, rules, mtu, primary,
nic_mapping, persist_mapping, defroute,
dhclient_args, dns_servers,
nm_controlled, onboot)
self.members = members
@ -928,7 +998,7 @@ class LinuxBond(_BaseOpts):
@staticmethod
def from_json(json):
name = _get_required_field(json, 'name', 'LinuxBond')
(use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping,
(use_dhcp, use_dhcpv6, addresses, routes, rules, mtu, nic_mapping,
persist_mapping, defroute, dhclient_args,
dns_servers, nm_controlled, onboot) = _BaseOpts.base_opts_from_json(
json, include_primary=False)
@ -937,8 +1007,9 @@ class LinuxBond(_BaseOpts):
members = _update_members(json, nic_mapping, persist_mapping)
return LinuxBond(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6,
addresses=addresses, routes=routes, mtu=mtu,
members=members, bonding_options=bonding_options,
addresses=addresses, routes=routes, rules=rules,
mtu=mtu, members=members,
bonding_options=bonding_options,
nic_mapping=nic_mapping,
persist_mapping=persist_mapping, defroute=defroute,
dhclient_args=dhclient_args, dns_servers=dns_servers,
@ -949,19 +1020,21 @@ class OvsBond(_BaseOpts):
"""Base class for OVS bonds."""
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
routes=None, mtu=None, primary=False, members=None,
ovs_options=None, ovs_extra=None, nic_mapping=None,
persist_mapping=False, defroute=True, dhclient_args=None,
dns_servers=None, nm_controlled=False, onboot=True):
routes=None, rules=None, mtu=None, primary=False,
members=None, ovs_options=None, ovs_extra=None,
nic_mapping=None, persist_mapping=False, defroute=True,
dhclient_args=None, dns_servers=None, nm_controlled=False,
onboot=True):
check_ovs_installed(self.__class__.__name__)
addresses = addresses or []
routes = routes or []
rules = rules or []
members = members or []
dns_servers = dns_servers or []
super(OvsBond, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
routes, mtu, primary, nic_mapping,
routes, rules, mtu, primary, nic_mapping,
persist_mapping, defroute, dhclient_args,
dns_servers, nm_controlled, onboot)
self.members = members
@ -1006,7 +1079,7 @@ class OvsBond(_BaseOpts):
@staticmethod
def from_json(json):
name = _get_required_field(json, 'name', 'OvsBond')
(use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping,
(use_dhcp, use_dhcpv6, addresses, routes, rules, mtu, nic_mapping,
persist_mapping, defroute, dhclient_args,
dns_servers, nm_controlled, onboot) = _BaseOpts.base_opts_from_json(
json, include_primary=False)
@ -1018,8 +1091,8 @@ class OvsBond(_BaseOpts):
members = _update_members(json, nic_mapping, persist_mapping)
return OvsBond(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6,
addresses=addresses, routes=routes, mtu=mtu,
members=members, ovs_options=ovs_options,
addresses=addresses, routes=routes, rules=rules,
mtu=mtu, members=members, ovs_options=ovs_options,
ovs_extra=ovs_extra, nic_mapping=nic_mapping,
persist_mapping=persist_mapping, defroute=defroute,
dhclient_args=dhclient_args, dns_servers=dns_servers,
@ -1030,19 +1103,21 @@ class OvsTunnel(_BaseOpts):
"""Base class for OVS Tunnels."""
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
routes=None, mtu=None, primary=False, nic_mapping=None,
persist_mapping=False, defroute=True, dhclient_args=None,
dns_servers=None, nm_controlled=False, onboot=True,
tunnel_type=None, ovs_options=None, ovs_extra=None):
routes=None, rules=None, mtu=None, primary=False,
nic_mapping=None, persist_mapping=False, defroute=True,
dhclient_args=None, dns_servers=None, nm_controlled=False,
onboot=True, tunnel_type=None, ovs_options=None,
ovs_extra=None):
check_ovs_installed(self.__class__.__name__)
addresses = addresses or []
routes = routes or []
rules = rules or []
dns_servers = dns_servers or []
super(OvsTunnel, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
routes, mtu, primary, nic_mapping,
persist_mapping, defroute,
routes, rules, mtu, primary,
nic_mapping, persist_mapping, defroute,
dhclient_args, dns_servers,
nm_controlled, onboot)
self.tunnel_type = tunnel_type
@ -1067,22 +1142,24 @@ class OvsPatchPort(_BaseOpts):
"""Base class for OVS Patch Ports."""
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
routes=None, mtu=None, primary=False, nic_mapping=None,
persist_mapping=False, defroute=True, dhclient_args=None,
dns_servers=None, nm_controlled=False, onboot=True,
bridge_name=None, peer=None, ovs_options=None,
routes=None, rules=None, mtu=None, primary=False,
nic_mapping=None, persist_mapping=False, defroute=True,
dhclient_args=None, dns_servers=None, nm_controlled=False,
onboot=True, bridge_name=None, peer=None, ovs_options=None,
ovs_extra=None):
check_ovs_installed(self.__class__.__name__)
addresses = addresses or []
routes = routes or []
rules = rules or []
dns_servers = dns_servers or []
super(OvsPatchPort, self).__init__(name, use_dhcp, use_dhcpv6,
addresses, routes, mtu, primary,
nic_mapping, persist_mapping,
defroute, dhclient_args,
dns_servers, nm_controlled, onboot)
addresses, routes, rules, mtu,
primary, nic_mapping,
persist_mapping, defroute,
dhclient_args, dns_servers,
nm_controlled, onboot)
self.bridge_name = bridge_name
self.peer = peer
self.ovs_options = ovs_options or []
@ -1107,17 +1184,19 @@ class IbInterface(_BaseOpts):
"""Base class for InfiniBand network interfaces."""
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
routes=None, mtu=None, primary=False, nic_mapping=None,
persist_mapping=False, defroute=True, dhclient_args=None,
dns_servers=None, nm_controlled=False, onboot=True,
ethtool_opts=None):
routes=None, rules=None, mtu=None, primary=False,
nic_mapping=None, persist_mapping=False, defroute=True,
dhclient_args=None, dns_servers=None, nm_controlled=False,
onboot=True, ethtool_opts=None):
addresses = addresses or []
routes = routes or []
rules = rules or []
dns_servers = dns_servers or []
super(IbInterface, self).__init__(name, use_dhcp, use_dhcpv6,
addresses, routes, mtu, primary,
nic_mapping, persist_mapping,
defroute, dhclient_args, dns_servers,
addresses, routes, rules, mtu,
primary, nic_mapping,
persist_mapping, defroute,
dhclient_args, dns_servers,
nm_controlled, onboot)
self.ethtool_opts = ethtool_opts
@ -1133,19 +1212,20 @@ class OvsDpdkPort(_BaseOpts):
"""Base class for OVS Dpdk Ports."""
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
routes=None, mtu=None, primary=False, nic_mapping=None,
persist_mapping=False, defroute=True, dhclient_args=None,
dns_servers=None, nm_controlled=False, onboot=True,
members=None, driver='vfio-pci', ovs_options=None,
ovs_extra=None, rx_queue=None):
routes=None, rules=None, mtu=None, primary=False,
nic_mapping=None, persist_mapping=False, defroute=True,
dhclient_args=None, dns_servers=None, nm_controlled=False,
onboot=True, members=None, driver='vfio-pci',
ovs_options=None, ovs_extra=None, rx_queue=None):
check_ovs_installed(self.__class__.__name__)
super(OvsDpdkPort, self).__init__(name, use_dhcp, use_dhcpv6,
addresses, routes, mtu, primary,
nic_mapping, persist_mapping,
defroute, dhclient_args,
dns_servers, nm_controlled, onboot)
addresses, routes, rules, mtu,
primary, nic_mapping,
persist_mapping, defroute,
dhclient_args, dns_servers,
nm_controlled, onboot)
self.members = members or []
self.ovs_options = ovs_options or []
self.ovs_extra = format_ovs_extra(self, ovs_extra)
@ -1175,8 +1255,8 @@ class OvsDpdkPort(_BaseOpts):
def from_json(json):
name = _get_required_field(json, 'name', 'OvsDpdkPort')
# driver name by default will be 'vfio-pci' if not specified
(use_dhcp, use_dhcpv6, addresses, routes, mtu, primary, nic_mapping,
persist_mapping, defroute, dhclient_args,
(use_dhcp, use_dhcpv6, addresses, routes, rules, mtu, primary,
nic_mapping, persist_mapping, defroute, dhclient_args,
dns_servers, nm_controlled,
onboot) = _BaseOpts.base_opts_from_json(json)
@ -1223,8 +1303,8 @@ class OvsDpdkPort(_BaseOpts):
if not isinstance(ovs_extra, list):
ovs_extra = [ovs_extra]
return OvsDpdkPort(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6,
addresses=addresses, routes=routes, mtu=mtu,
primary=primary, nic_mapping=nic_mapping,
addresses=addresses, routes=routes, rules=rules,
mtu=mtu, primary=primary, nic_mapping=nic_mapping,
persist_mapping=persist_mapping, defroute=defroute,
dhclient_args=dhclient_args,
dns_servers=dns_servers,
@ -1238,13 +1318,15 @@ class SriovVF(_BaseOpts):
"""Base class for SR-IOV VF."""
def __init__(self, device, vfid, use_dhcp=False, use_dhcpv6=False,
addresses=None, routes=None, mtu=None, primary=False,
nic_mapping=None, persist_mapping=False, defroute=True,
dhclient_args=None, dns_servers=None, nm_controlled=False,
onboot=True, vlan_id=0, qos=0, spoofcheck=None,
trust=None, state=None, macaddr=None, promisc=None):
addresses=None, routes=None, rules=None, mtu=None,
primary=False, nic_mapping=None, persist_mapping=False,
defroute=True, dhclient_args=None, dns_servers=None,
nm_controlled=False, onboot=True, vlan_id=0, qos=0,
spoofcheck=None, trust=None, state=None, macaddr=None,
promisc=None):
addresses = addresses or []
routes = routes or []
rules = rules or []
dns_servers = dns_servers or []
mapped_nic_names = mapped_nics(nic_mapping)
if device in mapped_nic_names:
@ -1254,7 +1336,7 @@ class SriovVF(_BaseOpts):
# (device) and the VF id.
name = utils.get_vf_devname(device, vfid)
super(SriovVF, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
routes, mtu, primary, nic_mapping,
routes, rules, mtu, primary, nic_mapping,
persist_mapping, defroute,
dhclient_args, dns_servers,
nm_controlled, onboot)
@ -1314,15 +1396,16 @@ class SriovPF(_BaseOpts):
"""Base class for SR-IOV PF."""
def __init__(self, name, numvfs, use_dhcp=False, use_dhcpv6=False,
addresses=None, routes=None, mtu=None, primary=False,
nic_mapping=None, persist_mapping=False, defroute=True,
dhclient_args=None, dns_servers=None, nm_controlled=False,
onboot=True, members=None, promisc=None):
addresses=None, routes=None, rules=None, mtu=None,
primary=False, nic_mapping=None, persist_mapping=False,
defroute=True, dhclient_args=None, dns_servers=None,
nm_controlled=False, onboot=True, members=None, promisc=None):
addresses = addresses or []
routes = routes or []
rules = rules or []
dns_servers = dns_servers or []
super(SriovPF, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
routes, mtu, primary, nic_mapping,
routes, rules, mtu, primary, nic_mapping,
persist_mapping, defroute,
dhclient_args, dns_servers,
nm_controlled, onboot)
@ -1358,18 +1441,19 @@ class OvsDpdkBond(_BaseOpts):
"""Base class for OVS DPDK bonds."""
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
routes=None, mtu=None, primary=False, members=None,
ovs_options=None, ovs_extra=None, nic_mapping=None,
persist_mapping=False, defroute=True, dhclient_args=None,
dns_servers=None, nm_controlled=False, onboot=True,
rx_queue=None):
routes=None, rules=None, mtu=None, primary=False,
members=None, ovs_options=None, ovs_extra=None,
nic_mapping=None, persist_mapping=False, defroute=True,
dhclient_args=None, dns_servers=None, nm_controlled=False,
onboot=True, rx_queue=None):
check_ovs_installed(self.__class__.__name__)
super(OvsDpdkBond, self).__init__(name, use_dhcp, use_dhcpv6,
addresses, routes, mtu, primary,
nic_mapping, persist_mapping,
defroute, dhclient_args, dns_servers,
addresses, routes, rules, mtu,
primary, nic_mapping,
persist_mapping, defroute,
dhclient_args, dns_servers,
nm_controlled, onboot)
self.members = members or []
self.ovs_options = ovs_options
@ -1393,7 +1477,7 @@ class OvsDpdkBond(_BaseOpts):
@staticmethod
def from_json(json):
name = _get_required_field(json, 'name', 'OvsDpdkBond')
(use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping,
(use_dhcp, use_dhcpv6, addresses, routes, rules, mtu, nic_mapping,
persist_mapping, defroute, dhclient_args,
dns_servers, nm_controlled, onboot) = _BaseOpts.base_opts_from_json(
json, include_primary=False)
@ -1423,8 +1507,8 @@ class OvsDpdkBond(_BaseOpts):
raise InvalidConfigException(msg)
return OvsDpdkBond(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6,
addresses=addresses, routes=routes, mtu=mtu,
members=members, ovs_options=ovs_options,
addresses=addresses, routes=routes, rules=rules,
mtu=mtu, members=members, ovs_options=ovs_options,
ovs_extra=ovs_extra, nic_mapping=nic_mapping,
persist_mapping=persist_mapping,
defroute=defroute, dhclient_args=dhclient_args,
@ -1458,17 +1542,20 @@ class VppInterface(_BaseOpts):
subsequent runs of os-net-config.
"""
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
routes=None, mtu=None, primary=False, nic_mapping=None,
persist_mapping=False, defroute=True, dhclient_args=None,
dns_servers=None, nm_controlled=False, onboot=True,
uio_driver='vfio-pci', options=None):
routes=None, rules=None, mtu=None, primary=False,
nic_mapping=None, persist_mapping=False, defroute=True,
dhclient_args=None, dns_servers=None, nm_controlled=False,
onboot=True, uio_driver='vfio-pci', options=None):
addresses = addresses or []
routes = routes or []
rules = rules or []
super(VppInterface, self).__init__(name, use_dhcp, use_dhcpv6,
addresses, routes, mtu, primary,
nic_mapping, persist_mapping,
defroute, dhclient_args,
dns_servers, nm_controlled, onboot)
addresses, routes, rules, mtu,
primary, nic_mapping,
persist_mapping, defroute,
dhclient_args, dns_servers,
nm_controlled, onboot)
self.uio_driver = uio_driver
self.options = options
# pci_dev contains pci address for the interface, it will be populated
@ -1490,15 +1577,17 @@ class VppInterface(_BaseOpts):
class VppBond(_BaseOpts):
"""Base class for VPP Bond."""
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
routes=None, mtu=None, primary=False, nic_mapping=None,
persist_mapping=False, defroute=True, dhclient_args=None,
dns_servers=None, nm_controlled=False, onboot=True,
members=None, bonding_options=None):
routes=None, rules=None, mtu=None, primary=False,
nic_mapping=None, persist_mapping=False, defroute=True,
dhclient_args=None, dns_servers=None, nm_controlled=False,
onboot=True, members=None, bonding_options=None):
addresses = addresses or []
members = members or []
routes = routes or []
rules = rules or []
super(VppBond, self).__init__(name, use_dhcp, use_dhcpv6,
addresses, routes, mtu, primary,
addresses, routes, rules, mtu, primary,
nic_mapping, persist_mapping,
defroute, dhclient_args,
dns_servers, nm_controlled, onboot)
@ -1510,10 +1599,9 @@ class VppBond(_BaseOpts):
name = _get_required_field(json, 'name', 'VppBond')
bonding_options = json.get('bonding_options', '')
(use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping,
persist_mapping, defroute, dhclient_args,
dns_servers, nm_controlled, onboot) = _BaseOpts.base_opts_from_json(
json, include_primary=False)
(use_dhcp, use_dhcpv6, addresses, routes, rules, mtu, nic_mapping,
persist_mapping, defroute, dhclient_args, dns_servers, nm_controlled,
onboot) = _BaseOpts.base_opts_from_json(json, include_primary=False)
members = []
members_json = json.get('members', None)
@ -1534,8 +1622,8 @@ class VppBond(_BaseOpts):
raise InvalidConfigException(msg)
return VppBond(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6,
addresses=addresses, routes=routes, mtu=mtu,
members=members, nic_mapping=nic_mapping,
addresses=addresses, routes=routes, rules=rules,
mtu=mtu, members=members, nic_mapping=nic_mapping,
persist_mapping=persist_mapping,
defroute=defroute, dhclient_args=dhclient_args,
dns_servers=dns_servers, nm_controlled=nm_controlled,
@ -1552,14 +1640,14 @@ class ContrailVrouter(_BaseOpts):
- members: List of sole interface to use by vhost0
"""
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
routes=None, mtu=None, primary=False, nic_mapping=None,
persist_mapping=False, defroute=True, dhclient_args=None,
dns_servers=None, nm_controlled=False, onboot=True,
members=None):
routes=None, rules=None, mtu=None, primary=False,
nic_mapping=None, persist_mapping=False, defroute=True,
dhclient_args=None, dns_servers=None, nm_controlled=False,
onboot=True, members=None):
addresses = addresses or []
super(ContrailVrouter, self).__init__(name, use_dhcp, use_dhcpv6,
addresses, routes, mtu,
addresses, routes, rules, mtu,
primary, nic_mapping,
persist_mapping, defroute,
dhclient_args, dns_servers,
@ -1570,7 +1658,7 @@ class ContrailVrouter(_BaseOpts):
def from_json(json):
name = _get_required_field(json, 'name', 'ContrailVrouter')
(_use_dhcp, _use_dhcpv6, _addresses, _routes, _mtu, _primary,
(_use_dhcp, _use_dhcpv6, _addresses, _routes, _rules, _mtu, _primary,
nic_mapping, persist_mapping, _defroute, _dhclient_args, _dns_servers,
_nm_controlled, _onboot) = opts = _BaseOpts.base_opts_from_json(json)
members = _update_members(json, nic_mapping, persist_mapping)
@ -1592,16 +1680,16 @@ class ContrailVrouterDpdk(_BaseOpts):
- vlan_id:
"""
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
routes=None, mtu=None, primary=False, nic_mapping=None,
persist_mapping=False, defroute=True, dhclient_args=None,
dns_servers=None, nm_controlled=False, onboot=True,
members=None, bond_mode=None, bond_policy=None,
routes=None, rules=None, mtu=None, primary=False,
nic_mapping=None, persist_mapping=False, defroute=True,
dhclient_args=None, dns_servers=None, nm_controlled=False,
onboot=True, members=None, bond_mode=None, bond_policy=None,
driver=None, cpu_list='0-31', vlan_id=None):
addresses = addresses or []
super(ContrailVrouterDpdk, self).__init__(name, use_dhcp, use_dhcpv6,
addresses, routes, mtu,
primary, nic_mapping,
addresses, routes, rules,
mtu, primary, nic_mapping,
persist_mapping, defroute,
dhclient_args, dns_servers,
nm_controlled, onboot)
@ -1622,7 +1710,7 @@ class ContrailVrouterDpdk(_BaseOpts):
cpu_list = json.get('cpu_list', '0-31')
vlan_id = json.get('vlan_id', '')
(_use_dhcp, _use_dhcpv6, _addresses, _routes, _mtu, _primary,
(_use_dhcp, _use_dhcpv6, _addresses, _routes, _rules, _mtu, _primary,
nic_mapping, persist_mapping, _defroute, _dhclient_args, _dns_servers,
_nm_controlled, _onboot) = opts = _BaseOpts.base_opts_from_json(json)
members = _update_members(json, nic_mapping, persist_mapping)

View File

@ -135,6 +135,10 @@ definitions:
$ref: "#/definitions/bool_or_param"
route_options:
$ref: "#/definitions/string_or_param"
table:
oneOf:
- $ref: "#/definitions/string_or_param"
- $ref: "#/definitions/int_or_param"
requires:
- next_hop
additionalProperties: False
@ -147,6 +151,10 @@ definitions:
$ref: "#/definitions/bool_or_param"
route_options:
$ref: "#/definitions/string_or_param"
table:
oneOf:
- $ref: "#/definitions/string_or_param"
- $ref: "#/definitions/int_or_param"
requires:
- nexthop
additionalProperties: False
@ -156,6 +164,22 @@ definitions:
$ref: "#/definitions/route"
minItems: 0
route_rule:
type: object
properties:
rule:
$ref: "#/definitions/string_or_param"
comment:
$ref: "#/definitions/string_or_param"
required:
- rule
additionalProperties: False
list_of_rule:
type: array
items:
$ref: "#/definitions/route_rule"
minItems: 1
nic_mapping:
type: ["object", "null"]
@ -230,6 +254,8 @@ definitions:
$ref: "#/definitions/list_of_address"
routes:
$ref: "#/definitions/list_of_route"
rules:
$ref: "#/definitions/list_of_rule"
mtu:
$ref: "#/definitions/int_or_param"
nic_mapping:
@ -251,6 +277,23 @@ definitions:
- name
additionalProperties: False
route_table:
type: object
properties:
type:
enum: ["route_table"]
name:
$ref: "#/definitions/string_or_param"
table_id:
oneOf:
- $ref: "#/definitions/int_or_param"
- $ref: "#/definitions/string_or_param"
required:
- type
- name
- table_id
additionalProperties: False
sriov_pf:
type: object
properties:
@ -275,6 +318,8 @@ definitions:
$ref: "#/definitions/list_of_address"
routes:
$ref: "#/definitions/list_of_route"
rules:
$ref: "#/definitions/list_of_rule"
mtu:
$ref: "#/definitions/int_or_param"
nic_mapping:
@ -333,6 +378,8 @@ definitions:
$ref: "#/definitions/list_of_address"
routes:
$ref: "#/definitions/list_of_route"
rules:
$ref: "#/definitions/list_of_rule"
mtu:
$ref: "#/definitions/int_or_param"
nic_mapping:
@ -375,6 +422,8 @@ definitions:
$ref: "#/definitions/list_of_address"
routes:
$ref: "#/definitions/list_of_route"
rules:
$ref: "#/definitions/list_of_rule"
mtu:
$ref: "#/definitions/int_or_param"
nic_mapping:
@ -429,6 +478,8 @@ definitions:
$ref: "#/definitions/list_of_address"
routes:
$ref: "#/definitions/list_of_route"
rules:
$ref: "#/definitions/list_of_rule"
mtu:
$ref: "#/definitions/int_or_param"
nic_mapping:
@ -484,6 +535,8 @@ definitions:
$ref: "#/definitions/list_of_address"
routes:
$ref: "#/definitions/list_of_route"
rules:
$ref: "#/definitions/list_of_rule"
mtu:
$ref: "#/definitions/int_or_param"
nic_mapping:
@ -534,6 +587,8 @@ definitions:
$ref: "#/definitions/list_of_address"
routes:
$ref: "#/definitions/list_of_route"
rules:
$ref: "#/definitions/list_of_rule"
mtu:
$ref: "#/definitions/int_or_param"
nic_mapping:
@ -582,6 +637,8 @@ definitions:
$ref: "#/definitions/list_of_address"
routes:
$ref: "#/definitions/list_of_route"
rules:
$ref: "#/definitions/list_of_rule"
mtu:
$ref: "#/definitions/int_or_param"
nic_mapping:
@ -629,6 +686,8 @@ definitions:
$ref: "#/definitions/list_of_address"
routes:
$ref: "#/definitions/list_of_route"
rules:
$ref: "#/definitions/list_of_rule"
mtu:
$ref: "#/definitions/int_or_param"
nic_mapping:
@ -677,6 +736,8 @@ definitions:
$ref: "#/definitions/list_of_address"
routes:
$ref: "#/definitions/list_of_route"
rules:
$ref: "#/definitions/list_of_rule"
mtu:
$ref: "#/definitions/int_or_param"
nic_mapping:
@ -733,6 +794,8 @@ definitions:
$ref: "#/definitions/list_of_address"
routes:
$ref: "#/definitions/list_of_route"
rules:
$ref: "#/definitions/list_of_rule"
mtu:
$ref: "#/definitions/int_or_param"
nic_mapping:
@ -775,6 +838,8 @@ definitions:
$ref: "#/definitions/list_of_address"
routes:
$ref: "#/definitions/list_of_route"
rules:
$ref: "#/definitions/list_of_rule"
mtu:
$ref: "#/definitions/int_or_param"
nic_mapping:
@ -816,6 +881,8 @@ definitions:
$ref: "#/definitions/list_of_address"
routes:
$ref: "#/definitions/list_of_route"
rules:
$ref: "#/definitions/list_of_rule"
mtu:
$ref: "#/definitions/int_or_param"
nic_mapping:
@ -869,6 +936,8 @@ definitions:
$ref: "#/definitions/list_of_address"
routes:
$ref: "#/definitions/list_of_route"
rules:
$ref: "#/definitions/list_of_rule"
mtu:
$ref: "#/definitions/int_or_param"
nic_mapping:
@ -926,6 +995,8 @@ definitions:
$ref: "#/definitions/list_of_address"
routes:
$ref: "#/definitions/list_of_route"
rules:
$ref: "#/definitions/list_of_rule"
mtu:
$ref: "#/definitions/int_or_param"
nic_mapping:
@ -969,6 +1040,8 @@ definitions:
$ref: "#/definitions/list_of_address"
routes:
$ref: "#/definitions/list_of_route"
rules:
$ref: "#/definitions/list_of_rule"
mtu:
$ref: "#/definitions/int_or_param"
nic_mapping:
@ -1016,6 +1089,8 @@ definitions:
$ref: "#/definitions/list_of_address"
routes:
$ref: "#/definitions/list_of_route"
rules:
$ref: "#/definitions/list_of_rule"
mtu:
$ref: "#/definitions/int_or_param"
nic_mapping:
@ -1062,6 +1137,8 @@ definitions:
$ref: "#/definitions/list_of_address"
routes:
$ref: "#/definitions/list_of_route"
rules:
$ref: "#/definitions/list_of_rule"
mtu:
$ref: "#/definitions/int_or_param"
nic_mapping:
@ -1104,6 +1181,8 @@ definitions:
$ref: "#/definitions/list_of_address"
routes:
$ref: "#/definitions/list_of_route"
rules:
$ref: "#/definitions/list_of_rule"
mtu:
$ref: "#/definitions/int_or_param"
nic_mapping:
@ -1145,6 +1224,8 @@ definitions:
$ref: "#/definitions/list_of_address"
routes:
$ref: "#/definitions/list_of_route"
rules:
$ref: "#/definitions/list_of_rule"
mtu:
$ref: "#/definitions/int_or_param"
nic_mapping:
@ -1189,6 +1270,8 @@ definitions:
$ref: "#/definitions/list_of_address"
routes:
$ref: "#/definitions/list_of_route"
rules:
$ref: "#/definitions/list_of_rule"
mtu:
$ref: "#/definitions/int_or_param"
nic_mapping:
@ -1231,6 +1314,8 @@ definitions:
$ref: "#/definitions/list_of_address"
routes:
$ref: "#/definitions/list_of_route"
rules:
$ref: "#/definitions/list_of_rule"
mtu:
$ref: "#/definitions/int_or_param"
nic_mapping:
@ -1272,6 +1357,8 @@ definitions:
$ref: "#/definitions/list_of_address"
routes:
$ref: "#/definitions/list_of_route"
rules:
$ref: "#/definitions/list_of_rule"
mtu:
$ref: "#/definitions/int_or_param"
nic_mapping:
@ -1297,6 +1384,7 @@ type: array
items:
oneOf:
- $ref: "#/definitions/interface"
- $ref: "#/definitions/route_table"
- $ref: "#/definitions/sriov_pf"
- $ref: "#/definitions/sriov_vf"
- $ref: "#/definitions/vlan"

View File

@ -194,6 +194,33 @@ _ROUTES = """default via 192.168.1.1 dev em1 metric 10
172.20.0.0/24 via 192.168.1.5 dev em1 metric 100
"""
_ROUTES_WITH_TABLES = """172.19.0.0/24 via 192.168.1.1 dev em1 table table1
172.20.0.0/24 via 192.168.1.1 dev em1 table 201
172.21.0.0/24 via 192.168.1.1 dev em1 table 200
"""
_ROUTE_RULES = """# This file is autogenerated by os-net-config
# test comment
from 192.0.2.0/24 table 200
"""
_RT_DEFAULT = """# reserved values
#
255\tlocal
254\tmain
253\tdefault
0\tunspec
#
# local
#
#1\tinr.ruhep\n"""
_RT_CUSTOM = _RT_DEFAULT + "# Custom\n10\tcustom # Custom table\n20\ttable1\n"
_RT_FULL = _RT_DEFAULT + """# Custom
10\tcustom # os-net-config managed table
200\ttable1 # os-net-config managed table\n"""
_ROUTES_V6 = """default via 2001:db8::1 dev em1
2001:db8:dead:beef:cafe::/56 via fd00:fd00:2000::1 dev em1
2001:db8:dead:beff::/64 via fd00:fd00:2000::1 dev em1 metric 100
@ -554,6 +581,12 @@ class TestIfcfgNetConfig(base.TestCase):
def get_route_config(self, name='em1'):
return self.provider.route_data.get(name, '')
def get_route_table_config(self, name='custom', table_id=200):
return self.provider.route_table_data.get(name, table_id)
def get_rule_config(self, name='em1'):
return self.provider.rule_data.get(name)
def get_route6_config(self, name='em1'):
return self.provider.route6_data.get(name, '')
@ -569,6 +602,35 @@ class TestIfcfgNetConfig(base.TestCase):
if 'em1' in ifname:
return "0000:00:01.0"
def test_add_route_table(self):
route_table1 = objects.RouteTable('table1', 200)
route_table2 = objects.RouteTable('table2', '201')
self.provider.add_route_table(route_table1)
self.provider.add_route_table(route_table2)
self.assertEqual("table1", self.get_route_table_config(200))
self.assertEqual("table2", self.get_route_table_config(201))
def test_add_route_with_table(self):
route_rule1 = objects.RouteRule('from 192.0.2.0/24 table 200',
'test comment')
# Test route table by name
route1 = objects.Route('192.168.1.1', '172.19.0.0/24', False,
route_table="table1")
# Test that table specified in route_options takes precedence
route2 = objects.Route('192.168.1.1', '172.20.0.0/24', False,
'table 201', route_table=200)
# Test route table specified by integer ID
route3 = objects.Route('192.168.1.1', '172.21.0.0/24', False,
route_table=200)
v4_addr = objects.Address('192.168.1.2/24')
interface = objects.Interface('em1', addresses=[v4_addr],
routes=[route1, route2, route3],
rules=[route_rule1])
self.provider.add_interface(interface)
self.assertEqual(_V4_IFCFG, self.get_interface_config())
self.assertEqual(_ROUTES_WITH_TABLES, self.get_route_config())
self.assertEqual(_ROUTE_RULES, self.get_rule_config())
def test_add_base_interface(self):
interface = objects.Interface('em1')
self.provider.add_interface(interface)
@ -1637,6 +1699,8 @@ class TestIfcfgNetConfigApply(base.TestCase):
self.temp_bond_file = tempfile.NamedTemporaryFile()
self.temp_route_file = tempfile.NamedTemporaryFile()
self.temp_route6_file = tempfile.NamedTemporaryFile()
self.temp_route_table_file = tempfile.NamedTemporaryFile()
self.temp_rule_file = tempfile.NamedTemporaryFile()
self.temp_bridge_file = tempfile.NamedTemporaryFile()
self.temp_cleanup_file = tempfile.NamedTemporaryFile(delete=False)
self.ifup_interface_names = []
@ -1666,6 +1730,18 @@ class TestIfcfgNetConfigApply(base.TestCase):
self.stub_out(
'os_net_config.impl_ifcfg.route6_config_path', test_routes6_path)
def test_rules_path(name):
return self.temp_rule_file.name
self.stub_out(
'os_net_config.impl_ifcfg.route_rule_config_path', test_rules_path)
def test_route_table_path():
return self.temp_route_table_file.name
self.stub_out(
'os_net_config.impl_ifcfg.route_table_config_path',
test_route_table_path)
utils.write_config(self.temp_route_table_file.name, _RT_CUSTOM)
def test_bridge_path(name):
return self.temp_bridge_file.name
self.stub_out(
@ -1707,6 +1783,40 @@ class TestIfcfgNetConfigApply(base.TestCase):
self.temp_cleanup_file.close()
super(TestIfcfgNetConfigApply, self).tearDown()
def test_route_table_apply(self):
# Test overriding an existing route table
route_table1 = objects.RouteTable('custom', 10)
self.provider.add_route_table(route_table1)
route_table2 = objects.RouteTable('table1', 200)
self.provider.add_route_table(route_table2)
def test_route_table_path():
return self.temp_route_table_file.name
self.stub_out(
'os_net_config.impl_ifcfg.route_table_config_path',
test_route_table_path)
utils.write_config(self.temp_route_table_file.name, _RT_CUSTOM)
self.provider.apply()
route_table_data = utils.get_file_data(
self.temp_route_table_file.name)
self.assertEqual(_RT_FULL, route_table_data)
def test_reserved_route_table_id(self):
route_table1 = objects.RouteTable('table1', 253)
self.provider.add_route_table(route_table1)
self.assertRaises(os_net_config.ConfigurationError,
self.provider.apply)
def test_reserved_route_table_name(self):
route_table2 = objects.RouteTable('default', 200)
self.provider.add_route_table(route_table2)
self.assertRaises(os_net_config.ConfigurationError,
self.provider.apply)
def test_network_apply(self):
route1 = objects.Route('192.168.1.1', default=True,
route_options="metric 10")

View File

@ -30,12 +30,13 @@ class TestRoute(base.TestCase):
def test_from_json(self):
data = '{"next_hop": "172.19.0.1", "ip_netmask": "172.19.0.0/24", ' \
'"route_options": "metric 10"}'
'"route_options": "metric 10", "table": "200"}'
route = objects.Route.from_json(json.loads(data))
self.assertEqual("172.19.0.1", route.next_hop)
self.assertEqual("172.19.0.0/24", route.ip_netmask)
self.assertFalse(route.default)
self.assertEqual("metric 10", route.route_options)
self.assertEqual("200", route.route_table)
def test_from_json_default_route(self):
data = '{"next_hop": "172.19.0.1", "ip_netmask": "172.19.0.0/24", ' \
@ -73,6 +74,45 @@ class TestRoute(base.TestCase):
objects.Route.from_json, data)
class TestRouteTable(base.TestCase):
def test_from_json(self):
data = '{"type": "route_table", "name": "custom", "table_id": 200}'
route_table = objects.RouteTable.from_json(json.loads(data))
self.assertEqual("custom", route_table.name)
self.assertEqual(200, route_table.table_id)
def test_from_json_invalid(self):
self.assertRaises(objects.InvalidConfigException,
objects.RouteTable.from_json,
{})
data = '{"type": "route_table", "table_id": 200}'
json_data = json.loads(data)
self.assertRaises(objects.InvalidConfigException,
objects.RouteTable.from_json,
json_data)
data = '{"type": "route_table", "name": "custom"}'
json_data = json.loads(data)
self.assertRaises(objects.InvalidConfigException,
objects.RouteTable.from_json,
json_data)
class TestRouteRule(base.TestCase):
def test_rule(self):
rule1 = objects.RouteRule('from 192.0.2.0/24 table 200 prio 1000')
self.assertEqual('from 192.0.2.0/24 table 200 prio 1000', rule1.rule)
def test_rule_from_json(self):
data = '{"rule":"from 172.19.0.0/24 table 200", "comment":"test"}'
route_rule = objects.RouteRule.from_json(json.loads(data))
self.assertEqual("from 172.19.0.0/24 table 200", route_rule.rule)
self.assertEqual("test", route_rule.comment)
class TestAddress(base.TestCase):
def test_ipv4_address(self):

View File

@ -200,6 +200,28 @@ class TestDerivedTypes(base.TestCase):
self.assertFalse(v.is_valid([]))
self.assertFalse(v.is_valid(None))
def test_route_table(self):
schema = validator.get_schema_for_defined_type("route_table")
v = jsonschema.Draft4Validator(schema)
data = {"type": "route_table", "name": "custom", "table_id": "20"}
self.assertTrue(v.is_valid(data))
data["unkown_property"] = "value"
self.assertFalse(v.is_valid(data))
self.assertFalse(v.is_valid({}))
self.assertFalse(v.is_valid([]))
self.assertFalse(v.is_valid(None))
def test_route_rule(self):
schema = validator.get_schema_for_defined_type("route_rule")
v = jsonschema.Draft4Validator(schema)
data = {"rule": "iif em2 table 20"}
self.assertTrue(v.is_valid(data))
data["unkown_property"] = "value"
self.assertFalse(v.is_valid(data))
self.assertFalse(v.is_valid({}))
self.assertFalse(v.is_valid([]))
self.assertFalse(v.is_valid(None))
class TestDeviceTypes(base.TestCase):

View File

@ -0,0 +1,10 @@
---
features:
- |
Support for configuring policy-based routing has been added. A new
top-level object "route_table" has been added, which allows the user to
add tables to the system route table at /etc/iproute2/rt_tables. Routes
have a new "table" property for specifying which table to apply the route.
Interfaces now have a "rules" property that allows the user to add
arbitrary rules for when the system should use a particular routing table,
such as input interface or source IP address.