|
Packit Service |
a04d08 |
# This file is part of cloud-init. See LICENSE file for license information.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
751c4a |
import copy
|
|
Packit Service |
751c4a |
import io
|
|
Packit Service |
a04d08 |
import os
|
|
Packit Service |
a04d08 |
import re
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
751c4a |
from configobj import ConfigObj
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
11b429 |
from cloudinit import log as logging
|
|
Packit Service |
11b429 |
from cloudinit import util
|
|
Packit Service |
751c4a |
from cloudinit import subp
|
|
Packit Service |
751c4a |
from cloudinit.distros.parsers import networkmanager_conf
|
|
Packit Service |
751c4a |
from cloudinit.distros.parsers import resolv_conf
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
from . import renderer
|
|
Packit Service |
a04d08 |
from .network_state import (
|
|
Packit Service |
a04d08 |
is_ipv6_addr, net_prefix_to_ipv4_mask, subnet_is_ipv6, IPV6_DYNAMIC_TYPES)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
LOG = logging.getLogger(__name__)
|
|
Packit Service |
a04d08 |
NM_CFG_FILE = "/etc/NetworkManager/NetworkManager.conf"
|
|
Packit Service |
a04d08 |
KNOWN_DISTROS = ['centos', 'fedora', 'rhel', 'suse']
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def _make_header(sep='#'):
|
|
Packit Service |
a04d08 |
lines = [
|
|
Packit Service |
a04d08 |
"Created by cloud-init on instance boot automatically, do not edit.",
|
|
Packit Service |
a04d08 |
"",
|
|
Packit Service |
a04d08 |
]
|
|
Packit Service |
a04d08 |
for i in range(0, len(lines)):
|
|
Packit Service |
a04d08 |
if lines[i]:
|
|
Packit Service |
a04d08 |
lines[i] = sep + " " + lines[i]
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
lines[i] = sep
|
|
Packit Service |
a04d08 |
return "\n".join(lines)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def _is_default_route(route):
|
|
Packit Service |
a04d08 |
default_nets = ('::', '0.0.0.0')
|
|
Packit Service |
a04d08 |
return route['prefix'] == 0 and route['network'] in default_nets
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def _quote_value(value):
|
|
Packit Service |
a04d08 |
if re.search(r"\s", value):
|
|
Packit Service |
a04d08 |
# This doesn't handle complex cases...
|
|
Packit Service |
a04d08 |
if value.startswith('"') and value.endswith('"'):
|
|
Packit Service |
a04d08 |
return value
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
return '"%s"' % value
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
return value
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def enable_ifcfg_rh(path):
|
|
Packit Service |
a04d08 |
"""Add ifcfg-rh to NetworkManager.cfg plugins if main section is present"""
|
|
Packit Service |
a04d08 |
config = ConfigObj(path)
|
|
Packit Service |
a04d08 |
if 'main' in config:
|
|
Packit Service |
a04d08 |
if 'plugins' in config['main']:
|
|
Packit Service |
a04d08 |
if 'ifcfg-rh' in config['main']['plugins']:
|
|
Packit Service |
a04d08 |
return
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
config['main']['plugins'] = []
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if isinstance(config['main']['plugins'], list):
|
|
Packit Service |
a04d08 |
config['main']['plugins'].append('ifcfg-rh')
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
config['main']['plugins'] = [config['main']['plugins'], 'ifcfg-rh']
|
|
Packit Service |
a04d08 |
config.write()
|
|
Packit Service |
a04d08 |
LOG.debug('Enabled ifcfg-rh NetworkManager plugins')
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
class ConfigMap(object):
|
|
Packit Service |
a04d08 |
"""Sysconfig like dictionary object."""
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# Why does redhat prefer yes/no to true/false??
|
|
Packit Service |
a04d08 |
_bool_map = {
|
|
Packit Service |
a04d08 |
True: 'yes',
|
|
Packit Service |
a04d08 |
False: 'no',
|
|
Packit Service |
a04d08 |
}
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def __init__(self):
|
|
Packit Service |
a04d08 |
self._conf = {}
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def __setitem__(self, key, value):
|
|
Packit Service |
a04d08 |
self._conf[key] = value
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def __getitem__(self, key):
|
|
Packit Service |
a04d08 |
return self._conf[key]
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
751c4a |
def get(self, key):
|
|
Packit Service |
751c4a |
return self._conf.get(key)
|
|
Packit Service |
751c4a |
|
|
Packit Service |
a04d08 |
def __contains__(self, key):
|
|
Packit Service |
a04d08 |
return key in self._conf
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def drop(self, key):
|
|
Packit Service |
a04d08 |
self._conf.pop(key, None)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def __len__(self):
|
|
Packit Service |
a04d08 |
return len(self._conf)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def to_string(self):
|
|
Packit Service |
751c4a |
buf = io.StringIO()
|
|
Packit Service |
a04d08 |
buf.write(_make_header())
|
|
Packit Service |
a04d08 |
if self._conf:
|
|
Packit Service |
a04d08 |
buf.write("\n")
|
|
Packit Service |
a04d08 |
for key in sorted(self._conf.keys()):
|
|
Packit Service |
a04d08 |
value = self._conf[key]
|
|
Packit Service |
a04d08 |
if isinstance(value, bool):
|
|
Packit Service |
a04d08 |
value = self._bool_map[value]
|
|
Packit Service |
751c4a |
if not isinstance(value, str):
|
|
Packit Service |
a04d08 |
value = str(value)
|
|
Packit Service |
a04d08 |
buf.write("%s=%s\n" % (key, _quote_value(value)))
|
|
Packit Service |
a04d08 |
return buf.getvalue()
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
751c4a |
def update(self, updates):
|
|
Packit Service |
751c4a |
self._conf.update(updates)
|
|
Packit Service |
751c4a |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
class Route(ConfigMap):
|
|
Packit Service |
a04d08 |
"""Represents a route configuration."""
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def __init__(self, route_name, base_sysconf_dir,
|
|
Packit Service |
a04d08 |
ipv4_tpl, ipv6_tpl):
|
|
Packit Service |
a04d08 |
super(Route, self).__init__()
|
|
Packit Service |
a04d08 |
self.last_idx = 1
|
|
Packit Service |
a04d08 |
self.has_set_default_ipv4 = False
|
|
Packit Service |
a04d08 |
self.has_set_default_ipv6 = False
|
|
Packit Service |
a04d08 |
self._route_name = route_name
|
|
Packit Service |
a04d08 |
self._base_sysconf_dir = base_sysconf_dir
|
|
Packit Service |
a04d08 |
self.route_fn_tpl_ipv4 = ipv4_tpl
|
|
Packit Service |
a04d08 |
self.route_fn_tpl_ipv6 = ipv6_tpl
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def copy(self):
|
|
Packit Service |
a04d08 |
r = Route(self._route_name, self._base_sysconf_dir,
|
|
Packit Service |
a04d08 |
self.route_fn_tpl_ipv4, self.route_fn_tpl_ipv6)
|
|
Packit Service |
a04d08 |
r._conf = self._conf.copy()
|
|
Packit Service |
a04d08 |
r.last_idx = self.last_idx
|
|
Packit Service |
a04d08 |
r.has_set_default_ipv4 = self.has_set_default_ipv4
|
|
Packit Service |
a04d08 |
r.has_set_default_ipv6 = self.has_set_default_ipv6
|
|
Packit Service |
a04d08 |
return r
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@property
|
|
Packit Service |
a04d08 |
def path_ipv4(self):
|
|
Packit Service |
a04d08 |
return self.route_fn_tpl_ipv4 % ({'base': self._base_sysconf_dir,
|
|
Packit Service |
a04d08 |
'name': self._route_name})
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@property
|
|
Packit Service |
a04d08 |
def path_ipv6(self):
|
|
Packit Service |
a04d08 |
return self.route_fn_tpl_ipv6 % ({'base': self._base_sysconf_dir,
|
|
Packit Service |
a04d08 |
'name': self._route_name})
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def is_ipv6_route(self, address):
|
|
Packit Service |
a04d08 |
return ':' in address
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def to_string(self, proto="ipv4"):
|
|
Packit Service |
a04d08 |
# only accept ipv4 and ipv6
|
|
Packit Service |
a04d08 |
if proto not in ['ipv4', 'ipv6']:
|
|
Packit Service |
a04d08 |
raise ValueError("Unknown protocol '%s'" % (str(proto)))
|
|
Packit Service |
751c4a |
buf = io.StringIO()
|
|
Packit Service |
a04d08 |
buf.write(_make_header())
|
|
Packit Service |
a04d08 |
if self._conf:
|
|
Packit Service |
a04d08 |
buf.write("\n")
|
|
Packit Service |
a04d08 |
# need to reindex IPv4 addresses
|
|
Packit Service |
a04d08 |
# (because Route can contain a mix of IPv4 and IPv6)
|
|
Packit Service |
a04d08 |
reindex = -1
|
|
Packit Service |
a04d08 |
for key in sorted(self._conf.keys()):
|
|
Packit Service |
a04d08 |
if 'ADDRESS' in key:
|
|
Packit Service |
a04d08 |
index = key.replace('ADDRESS', '')
|
|
Packit Service |
a04d08 |
address_value = str(self._conf[key])
|
|
Packit Service |
a04d08 |
# only accept combinations:
|
|
Packit Service |
a04d08 |
# if proto ipv6 only display ipv6 routes
|
|
Packit Service |
a04d08 |
# if proto ipv4 only display ipv4 routes
|
|
Packit Service |
a04d08 |
# do not add ipv6 routes if proto is ipv4
|
|
Packit Service |
a04d08 |
# do not add ipv4 routes if proto is ipv6
|
|
Packit Service |
a04d08 |
# (this array will contain a mix of ipv4 and ipv6)
|
|
Packit Service |
a04d08 |
if proto == "ipv4" and not self.is_ipv6_route(address_value):
|
|
Packit Service |
a04d08 |
netmask_value = str(self._conf['NETMASK' + index])
|
|
Packit Service |
a04d08 |
gateway_value = str(self._conf['GATEWAY' + index])
|
|
Packit Service |
a04d08 |
# increase IPv4 index
|
|
Packit Service |
a04d08 |
reindex = reindex + 1
|
|
Packit Service |
a04d08 |
buf.write("%s=%s\n" % ('ADDRESS' + str(reindex),
|
|
Packit Service |
a04d08 |
_quote_value(address_value)))
|
|
Packit Service |
a04d08 |
buf.write("%s=%s\n" % ('GATEWAY' + str(reindex),
|
|
Packit Service |
a04d08 |
_quote_value(gateway_value)))
|
|
Packit Service |
a04d08 |
buf.write("%s=%s\n" % ('NETMASK' + str(reindex),
|
|
Packit Service |
a04d08 |
_quote_value(netmask_value)))
|
|
Packit Service |
a04d08 |
metric_key = 'METRIC' + index
|
|
Packit Service |
a04d08 |
if metric_key in self._conf:
|
|
Packit Service |
a04d08 |
metric_value = str(self._conf['METRIC' + index])
|
|
Packit Service |
a04d08 |
buf.write("%s=%s\n" % ('METRIC' + str(reindex),
|
|
Packit Service |
a04d08 |
_quote_value(metric_value)))
|
|
Packit Service |
a04d08 |
elif proto == "ipv6" and self.is_ipv6_route(address_value):
|
|
Packit Service |
a04d08 |
netmask_value = str(self._conf['NETMASK' + index])
|
|
Packit Service |
a04d08 |
gateway_value = str(self._conf['GATEWAY' + index])
|
|
Packit Service |
a04d08 |
metric_value = (
|
|
Packit Service |
a04d08 |
'metric ' + str(self._conf['METRIC' + index])
|
|
Packit Service |
a04d08 |
if 'METRIC' + index in self._conf else '')
|
|
Packit Service |
a04d08 |
buf.write(
|
|
Packit Service |
a04d08 |
"%s/%s via %s %s dev %s\n" % (address_value,
|
|
Packit Service |
a04d08 |
netmask_value,
|
|
Packit Service |
a04d08 |
gateway_value,
|
|
Packit Service |
a04d08 |
metric_value,
|
|
Packit Service |
a04d08 |
self._route_name))
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
return buf.getvalue()
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
class NetInterface(ConfigMap):
|
|
Packit Service |
a04d08 |
"""Represents a sysconfig/networking-script (and its config + children)."""
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
iface_types = {
|
|
Packit Service |
a04d08 |
'ethernet': 'Ethernet',
|
|
Packit Service |
a04d08 |
'bond': 'Bond',
|
|
Packit Service |
a04d08 |
'bridge': 'Bridge',
|
|
Packit Service |
a04d08 |
'infiniband': 'InfiniBand',
|
|
Packit Service |
a04d08 |
}
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def __init__(self, iface_name, base_sysconf_dir, templates,
|
|
Packit Service |
a04d08 |
kind='ethernet'):
|
|
Packit Service |
a04d08 |
super(NetInterface, self).__init__()
|
|
Packit Service |
a04d08 |
self.children = []
|
|
Packit Service |
a04d08 |
self.templates = templates
|
|
Packit Service |
a04d08 |
route_tpl = self.templates.get('route_templates')
|
|
Packit Service |
a04d08 |
self.routes = Route(iface_name, base_sysconf_dir,
|
|
Packit Service |
a04d08 |
ipv4_tpl=route_tpl.get('ipv4'),
|
|
Packit Service |
a04d08 |
ipv6_tpl=route_tpl.get('ipv6'))
|
|
Packit Service |
a04d08 |
self.iface_fn_tpl = self.templates.get('iface_templates')
|
|
Packit Service |
a04d08 |
self.kind = kind
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
self._iface_name = iface_name
|
|
Packit Service |
a04d08 |
self._conf['DEVICE'] = iface_name
|
|
Packit Service |
a04d08 |
self._base_sysconf_dir = base_sysconf_dir
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@property
|
|
Packit Service |
a04d08 |
def name(self):
|
|
Packit Service |
a04d08 |
return self._iface_name
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@name.setter
|
|
Packit Service |
a04d08 |
def name(self, iface_name):
|
|
Packit Service |
a04d08 |
self._iface_name = iface_name
|
|
Packit Service |
a04d08 |
self._conf['DEVICE'] = iface_name
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@property
|
|
Packit Service |
a04d08 |
def kind(self):
|
|
Packit Service |
a04d08 |
return self._kind
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@kind.setter
|
|
Packit Service |
a04d08 |
def kind(self, kind):
|
|
Packit Service |
a04d08 |
if kind not in self.iface_types:
|
|
Packit Service |
a04d08 |
raise ValueError(kind)
|
|
Packit Service |
a04d08 |
self._kind = kind
|
|
Packit Service |
a04d08 |
self._conf['TYPE'] = self.iface_types[kind]
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@property
|
|
Packit Service |
a04d08 |
def path(self):
|
|
Packit Service |
a04d08 |
return self.iface_fn_tpl % ({'base': self._base_sysconf_dir,
|
|
Packit Service |
a04d08 |
'name': self.name})
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def copy(self, copy_children=False, copy_routes=False):
|
|
Packit Service |
a04d08 |
c = NetInterface(self.name, self._base_sysconf_dir,
|
|
Packit Service |
a04d08 |
self.templates, kind=self._kind)
|
|
Packit Service |
a04d08 |
c._conf = self._conf.copy()
|
|
Packit Service |
a04d08 |
if copy_children:
|
|
Packit Service |
a04d08 |
c.children = list(self.children)
|
|
Packit Service |
a04d08 |
if copy_routes:
|
|
Packit Service |
a04d08 |
c.routes = self.routes.copy()
|
|
Packit Service |
a04d08 |
return c
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
class Renderer(renderer.Renderer):
|
|
Packit Service |
a04d08 |
"""Renders network information in a /etc/sysconfig format."""
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# See: https://access.redhat.com/documentation/en-US/\
|
|
Packit Service |
a04d08 |
# Red_Hat_Enterprise_Linux/6/html/Deployment_Guide/\
|
|
Packit Service |
a04d08 |
# s1-networkscripts-interfaces.html (or other docs for
|
|
Packit Service |
a04d08 |
# details about this)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
751c4a |
iface_defaults = {
|
|
Packit Service |
b35483 |
'rhel': {'ONBOOT': True, 'USERCTL': False,
|
|
Packit Service |
751c4a |
'BOOTPROTO': 'none'},
|
|
Packit Service |
751c4a |
'suse': {'BOOTPROTO': 'static', 'STARTMODE': 'auto'},
|
|
Packit Service |
751c4a |
}
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
cfg_key_maps = {
|
|
Packit Service |
751c4a |
'rhel': {
|
|
Packit Service |
751c4a |
'accept-ra': 'IPV6_FORCE_ACCEPT_RA',
|
|
Packit Service |
751c4a |
'bridge_stp': 'STP',
|
|
Packit Service |
751c4a |
'bridge_ageing': 'AGEING',
|
|
Packit Service |
751c4a |
'bridge_bridgeprio': 'PRIO',
|
|
Packit Service |
751c4a |
'mac_address': 'HWADDR',
|
|
Packit Service |
751c4a |
'mtu': 'MTU',
|
|
Packit Service |
751c4a |
},
|
|
Packit Service |
751c4a |
'suse': {
|
|
Packit Service |
751c4a |
'bridge_stp': 'BRIDGE_STP',
|
|
Packit Service |
751c4a |
'bridge_ageing': 'BRIDGE_AGEINGTIME',
|
|
Packit Service |
751c4a |
'bridge_bridgeprio': 'BRIDGE_PRIORITY',
|
|
Packit Service |
751c4a |
'mac_address': 'LLADDR',
|
|
Packit Service |
751c4a |
'mtu': 'MTU',
|
|
Packit Service |
751c4a |
},
|
|
Packit Service |
751c4a |
}
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# If these keys exist, then their values will be used to form
|
|
Packit Service |
a04d08 |
# a BONDING_OPTS grouping; otherwise no grouping will be set.
|
|
Packit Service |
a04d08 |
bond_tpl_opts = tuple([
|
|
Packit Service |
a04d08 |
('bond_mode', "mode=%s"),
|
|
Packit Service |
a04d08 |
('bond_xmit_hash_policy', "xmit_hash_policy=%s"),
|
|
Packit Service |
a04d08 |
('bond_miimon', "miimon=%s"),
|
|
Packit Service |
a04d08 |
('bond_min_links', "min_links=%s"),
|
|
Packit Service |
a04d08 |
('bond_arp_interval', "arp_interval=%s"),
|
|
Packit Service |
a04d08 |
('bond_arp_ip_target', "arp_ip_target=%s"),
|
|
Packit Service |
a04d08 |
('bond_arp_validate', "arp_validate=%s"),
|
|
Packit Service |
a04d08 |
('bond_ad_select', "ad_select=%s"),
|
|
Packit Service |
a04d08 |
('bond_num_grat_arp', "num_grat_arp=%s"),
|
|
Packit Service |
a04d08 |
('bond_downdelay', "downdelay=%s"),
|
|
Packit Service |
a04d08 |
('bond_updelay', "updelay=%s"),
|
|
Packit Service |
a04d08 |
('bond_lacp_rate', "lacp_rate=%s"),
|
|
Packit Service |
a04d08 |
('bond_fail_over_mac', "fail_over_mac=%s"),
|
|
Packit Service |
a04d08 |
('bond_primary', "primary=%s"),
|
|
Packit Service |
a04d08 |
('bond_primary_reselect', "primary_reselect=%s"),
|
|
Packit Service |
a04d08 |
])
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
templates = {}
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def __init__(self, config=None):
|
|
Packit Service |
a04d08 |
if not config:
|
|
Packit Service |
a04d08 |
config = {}
|
|
Packit Service |
a04d08 |
self.sysconf_dir = config.get('sysconf_dir', 'etc/sysconfig')
|
|
Packit Service |
a04d08 |
self.netrules_path = config.get(
|
|
Packit Service |
a04d08 |
'netrules_path', 'etc/udev/rules.d/70-persistent-net.rules')
|
|
Packit Service |
a04d08 |
self.dns_path = config.get('dns_path', 'etc/resolv.conf')
|
|
Packit Service |
a04d08 |
nm_conf_path = 'etc/NetworkManager/conf.d/99-cloud-init.conf'
|
|
Packit Service |
a04d08 |
self.networkmanager_conf_path = config.get('networkmanager_conf_path',
|
|
Packit Service |
a04d08 |
nm_conf_path)
|
|
Packit Service |
a04d08 |
self.templates = {
|
|
Packit Service |
a04d08 |
'control': config.get('control'),
|
|
Packit Service |
a04d08 |
'iface_templates': config.get('iface_templates'),
|
|
Packit Service |
a04d08 |
'route_templates': config.get('route_templates'),
|
|
Packit Service |
a04d08 |
}
|
|
Packit Service |
751c4a |
self.flavor = config.get('flavor', 'rhel')
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@classmethod
|
|
Packit Service |
751c4a |
def _render_iface_shared(cls, iface, iface_cfg, flavor):
|
|
Packit Service |
751c4a |
flavor_defaults = copy.deepcopy(cls.iface_defaults.get(flavor, {}))
|
|
Packit Service |
751c4a |
iface_cfg.update(flavor_defaults)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
751c4a |
for old_key in ('mac_address', 'mtu', 'accept-ra'):
|
|
Packit Service |
a04d08 |
old_value = iface.get(old_key)
|
|
Packit Service |
a04d08 |
if old_value is not None:
|
|
Packit Service |
a04d08 |
# only set HWADDR on physical interfaces
|
|
Packit Service |
a04d08 |
if (old_key == 'mac_address' and
|
|
Packit Service |
a04d08 |
iface['type'] not in ['physical', 'infiniband']):
|
|
Packit Service |
a04d08 |
continue
|
|
Packit Service |
751c4a |
new_key = cls.cfg_key_maps[flavor].get(old_key)
|
|
Packit Service |
751c4a |
if new_key:
|
|
Packit Service |
751c4a |
iface_cfg[new_key] = old_value
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@classmethod
|
|
Packit Service |
751c4a |
def _render_subnets(cls, iface_cfg, subnets, has_default_route, flavor):
|
|
Packit Service |
a04d08 |
# setting base values
|
|
Packit Service |
751c4a |
if flavor == 'suse':
|
|
Packit Service |
751c4a |
iface_cfg['BOOTPROTO'] = 'static'
|
|
Packit Service |
751c4a |
if 'BRIDGE' in iface_cfg:
|
|
Packit Service |
751c4a |
iface_cfg['BOOTPROTO'] = 'dhcp'
|
|
Packit Service |
751c4a |
iface_cfg.drop('BRIDGE')
|
|
Packit Service |
751c4a |
else:
|
|
Packit Service |
751c4a |
iface_cfg['BOOTPROTO'] = 'none'
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# modifying base values according to subnets
|
|
Packit Service |
a04d08 |
for i, subnet in enumerate(subnets, start=len(iface_cfg.children)):
|
|
Packit Service |
a04d08 |
mtu_key = 'MTU'
|
|
Packit Service |
a04d08 |
subnet_type = subnet.get('type')
|
|
Packit Service |
a04d08 |
if subnet_type == 'dhcp6' or subnet_type == 'ipv6_dhcpv6-stateful':
|
|
Packit Service |
751c4a |
if flavor == 'suse':
|
|
Packit Service |
751c4a |
# User wants dhcp for both protocols
|
|
Packit Service |
751c4a |
if iface_cfg['BOOTPROTO'] == 'dhcp4':
|
|
Packit Service |
751c4a |
iface_cfg['BOOTPROTO'] = 'dhcp'
|
|
Packit Service |
751c4a |
else:
|
|
Packit Service |
751c4a |
# Only IPv6 is DHCP, IPv4 may be static
|
|
Packit Service |
751c4a |
iface_cfg['BOOTPROTO'] = 'dhcp6'
|
|
Packit Service |
751c4a |
iface_cfg['DHCLIENT6_MODE'] = 'managed'
|
|
Packit Service |
751c4a |
else:
|
|
Packit Service |
751c4a |
iface_cfg['IPV6INIT'] = True
|
|
Packit Service |
751c4a |
# Configure network settings using DHCPv6
|
|
Packit Service |
751c4a |
iface_cfg['DHCPV6C'] = True
|
|
Packit Service |
a04d08 |
elif subnet_type == 'ipv6_dhcpv6-stateless':
|
|
Packit Service |
751c4a |
if flavor == 'suse':
|
|
Packit Service |
751c4a |
# User wants dhcp for both protocols
|
|
Packit Service |
751c4a |
if iface_cfg['BOOTPROTO'] == 'dhcp4':
|
|
Packit Service |
751c4a |
iface_cfg['BOOTPROTO'] = 'dhcp'
|
|
Packit Service |
751c4a |
else:
|
|
Packit Service |
751c4a |
# Only IPv6 is DHCP, IPv4 may be static
|
|
Packit Service |
751c4a |
iface_cfg['BOOTPROTO'] = 'dhcp6'
|
|
Packit Service |
751c4a |
iface_cfg['DHCLIENT6_MODE'] = 'info'
|
|
Packit Service |
751c4a |
else:
|
|
Packit Service |
751c4a |
iface_cfg['IPV6INIT'] = True
|
|
Packit Service |
751c4a |
# Configure network settings using SLAAC from RAs and
|
|
Packit Service |
751c4a |
# optional info from dhcp server using DHCPv6
|
|
Packit Service |
751c4a |
iface_cfg['IPV6_AUTOCONF'] = True
|
|
Packit Service |
751c4a |
iface_cfg['DHCPV6C'] = True
|
|
Packit Service |
751c4a |
# Use Information-request to get only stateless
|
|
Packit Service |
751c4a |
# configuration parameters (i.e., without address).
|
|
Packit Service |
751c4a |
iface_cfg['DHCPV6C_OPTIONS'] = '-S'
|
|
Packit Service |
a04d08 |
elif subnet_type == 'ipv6_slaac':
|
|
Packit Service |
751c4a |
if flavor == 'suse':
|
|
Packit Service |
751c4a |
# User wants dhcp for both protocols
|
|
Packit Service |
751c4a |
if iface_cfg['BOOTPROTO'] == 'dhcp4':
|
|
Packit Service |
751c4a |
iface_cfg['BOOTPROTO'] = 'dhcp'
|
|
Packit Service |
751c4a |
else:
|
|
Packit Service |
751c4a |
# Only IPv6 is DHCP, IPv4 may be static
|
|
Packit Service |
751c4a |
iface_cfg['BOOTPROTO'] = 'dhcp6'
|
|
Packit Service |
751c4a |
iface_cfg['DHCLIENT6_MODE'] = 'info'
|
|
Packit Service |
751c4a |
else:
|
|
Packit Service |
751c4a |
iface_cfg['IPV6INIT'] = True
|
|
Packit Service |
751c4a |
# Configure network settings using SLAAC from RAs
|
|
Packit Service |
751c4a |
iface_cfg['IPV6_AUTOCONF'] = True
|
|
Packit Service |
a04d08 |
elif subnet_type in ['dhcp4', 'dhcp']:
|
|
Packit Service |
751c4a |
bootproto_in = iface_cfg['BOOTPROTO']
|
|
Packit Service |
a04d08 |
iface_cfg['BOOTPROTO'] = 'dhcp'
|
|
Packit Service |
751c4a |
if flavor == 'suse' and subnet_type == 'dhcp4':
|
|
Packit Service |
751c4a |
# If dhcp6 is already specified the user wants dhcp
|
|
Packit Service |
751c4a |
# for both protocols
|
|
Packit Service |
751c4a |
if bootproto_in != 'dhcp6':
|
|
Packit Service |
751c4a |
# Only IPv4 is DHCP, IPv6 may be static
|
|
Packit Service |
751c4a |
iface_cfg['BOOTPROTO'] = 'dhcp4'
|
|
Packit Service |
751c4a |
elif subnet_type in ['static', 'static6']:
|
|
Packit Service |
751c4a |
# RH info
|
|
Packit Service |
a04d08 |
# grep BOOTPROTO sysconfig.txt -A2 | head -3
|
|
Packit Service |
a04d08 |
# BOOTPROTO=none|bootp|dhcp
|
|
Packit Service |
a04d08 |
# 'bootp' or 'dhcp' cause a DHCP client
|
|
Packit Service |
a04d08 |
# to run on the device. Any other
|
|
Packit Service |
a04d08 |
# value causes any static configuration
|
|
Packit Service |
a04d08 |
# in the file to be applied.
|
|
Packit Service |
751c4a |
if subnet_is_ipv6(subnet) and flavor != 'suse':
|
|
Packit Service |
a04d08 |
mtu_key = 'IPV6_MTU'
|
|
Packit Service |
a04d08 |
iface_cfg['IPV6INIT'] = True
|
|
Packit Service |
a04d08 |
if 'mtu' in subnet:
|
|
Packit Service |
a04d08 |
mtu_mismatch = bool(mtu_key in iface_cfg and
|
|
Packit Service |
a04d08 |
subnet['mtu'] != iface_cfg[mtu_key])
|
|
Packit Service |
a04d08 |
if mtu_mismatch:
|
|
Packit Service |
a04d08 |
LOG.warning(
|
|
Packit Service |
a04d08 |
'Network config: ignoring %s device-level mtu:%s'
|
|
Packit Service |
a04d08 |
' because ipv4 subnet-level mtu:%s provided.',
|
|
Packit Service |
a04d08 |
iface_cfg.name, iface_cfg[mtu_key], subnet['mtu'])
|
|
Packit Service |
751c4a |
if subnet_is_ipv6(subnet):
|
|
Packit Service |
751c4a |
if flavor == 'suse':
|
|
Packit Service |
751c4a |
# TODO(rjschwei) write mtu setting to
|
|
Packit Service |
751c4a |
# /etc/sysctl.d/
|
|
Packit Service |
751c4a |
pass
|
|
Packit Service |
751c4a |
else:
|
|
Packit Service |
751c4a |
iface_cfg[mtu_key] = subnet['mtu']
|
|
Packit Service |
751c4a |
else:
|
|
Packit Service |
751c4a |
iface_cfg[mtu_key] = subnet['mtu']
|
|
Packit Service |
a04d08 |
elif subnet_type == 'manual':
|
|
Packit Service |
751c4a |
if flavor == 'suse':
|
|
Packit Service |
751c4a |
LOG.debug('Unknown subnet type setting "%s"', subnet_type)
|
|
Packit Service |
751c4a |
else:
|
|
Packit Service |
751c4a |
# If the subnet has an MTU setting, then ONBOOT=True
|
|
Packit Service |
751c4a |
# to apply the setting
|
|
Packit Service |
751c4a |
iface_cfg['ONBOOT'] = mtu_key in iface_cfg
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
raise ValueError("Unknown subnet type '%s' found"
|
|
Packit Service |
a04d08 |
" for interface '%s'" % (subnet_type,
|
|
Packit Service |
a04d08 |
iface_cfg.name))
|
|
Packit Service |
a04d08 |
if subnet.get('control') == 'manual':
|
|
Packit Service |
751c4a |
if flavor == 'suse':
|
|
Packit Service |
751c4a |
iface_cfg['STARTMODE'] = 'manual'
|
|
Packit Service |
751c4a |
else:
|
|
Packit Service |
751c4a |
iface_cfg['ONBOOT'] = False
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# set IPv4 and IPv6 static addresses
|
|
Packit Service |
a04d08 |
ipv4_index = -1
|
|
Packit Service |
a04d08 |
ipv6_index = -1
|
|
Packit Service |
a04d08 |
for i, subnet in enumerate(subnets, start=len(iface_cfg.children)):
|
|
Packit Service |
a04d08 |
subnet_type = subnet.get('type')
|
|
Packit Service |
a04d08 |
# metric may apply to both dhcp and static config
|
|
Packit Service |
a04d08 |
if 'metric' in subnet:
|
|
Packit Service |
751c4a |
if flavor != 'suse':
|
|
Packit Service |
751c4a |
iface_cfg['METRIC'] = subnet['metric']
|
|
Packit Service |
751c4a |
if subnet_type in ['dhcp', 'dhcp4']:
|
|
Packit Service |
751c4a |
# On SUSE distros 'DHCLIENT_SET_DEFAULT_ROUTE' is a global
|
|
Packit Service |
751c4a |
# setting in /etc/sysconfig/network/dhcp
|
|
Packit Service |
751c4a |
if flavor != 'suse':
|
|
Packit Service |
751c4a |
if has_default_route and iface_cfg['BOOTPROTO'] != 'none':
|
|
Packit Service |
751c4a |
iface_cfg['DHCLIENT_SET_DEFAULT_ROUTE'] = False
|
|
Packit Service |
a04d08 |
continue
|
|
Packit Service |
a04d08 |
elif subnet_type in IPV6_DYNAMIC_TYPES:
|
|
Packit Service |
a04d08 |
continue
|
|
Packit Service |
751c4a |
elif subnet_type in ['static', 'static6']:
|
|
Packit Service |
a04d08 |
if subnet_is_ipv6(subnet):
|
|
Packit Service |
a04d08 |
ipv6_index = ipv6_index + 1
|
|
Packit Service |
a04d08 |
ipv6_cidr = "%s/%s" % (subnet['address'], subnet['prefix'])
|
|
Packit Service |
a04d08 |
if ipv6_index == 0:
|
|
Packit Service |
751c4a |
if flavor == 'suse':
|
|
Packit Service |
751c4a |
iface_cfg['IPADDR6'] = ipv6_cidr
|
|
Packit Service |
751c4a |
else:
|
|
Packit Service |
751c4a |
iface_cfg['IPV6ADDR'] = ipv6_cidr
|
|
Packit Service |
a04d08 |
elif ipv6_index == 1:
|
|
Packit Service |
751c4a |
if flavor == 'suse':
|
|
Packit Service |
751c4a |
iface_cfg['IPADDR6_1'] = ipv6_cidr
|
|
Packit Service |
751c4a |
else:
|
|
Packit Service |
751c4a |
iface_cfg['IPV6ADDR_SECONDARIES'] = ipv6_cidr
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
751c4a |
if flavor == 'suse':
|
|
Packit Service |
751c4a |
iface_cfg['IPADDR6_%d' % ipv6_index] = ipv6_cidr
|
|
Packit Service |
751c4a |
else:
|
|
Packit Service |
751c4a |
iface_cfg['IPV6ADDR_SECONDARIES'] += \
|
|
Packit Service |
751c4a |
" " + ipv6_cidr
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
ipv4_index = ipv4_index + 1
|
|
Packit Service |
a04d08 |
suff = "" if ipv4_index == 0 else str(ipv4_index)
|
|
Packit Service |
a04d08 |
iface_cfg['IPADDR' + suff] = subnet['address']
|
|
Packit Service |
a04d08 |
iface_cfg['NETMASK' + suff] = \
|
|
Packit Service |
a04d08 |
net_prefix_to_ipv4_mask(subnet['prefix'])
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
751c4a |
if 'gateway' in subnet and flavor != 'suse':
|
|
Packit Service |
a04d08 |
iface_cfg['DEFROUTE'] = True
|
|
Packit Service |
a04d08 |
if is_ipv6_addr(subnet['gateway']):
|
|
Packit Service |
a04d08 |
iface_cfg['IPV6_DEFAULTGW'] = subnet['gateway']
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
iface_cfg['GATEWAY'] = subnet['gateway']
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
751c4a |
if 'dns_search' in subnet and flavor != 'suse':
|
|
Packit Service |
a04d08 |
iface_cfg['DOMAIN'] = ' '.join(subnet['dns_search'])
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
751c4a |
if 'dns_nameservers' in subnet and flavor != 'suse':
|
|
Packit Service |
a04d08 |
if len(subnet['dns_nameservers']) > 3:
|
|
Packit Service |
a04d08 |
# per resolv.conf(5) MAXNS sets this to 3.
|
|
Packit Service |
a04d08 |
LOG.debug("%s has %d entries in dns_nameservers. "
|
|
Packit Service |
a04d08 |
"Only 3 are used.", iface_cfg.name,
|
|
Packit Service |
a04d08 |
len(subnet['dns_nameservers']))
|
|
Packit Service |
a04d08 |
for i, k in enumerate(subnet['dns_nameservers'][:3], 1):
|
|
Packit Service |
a04d08 |
iface_cfg['DNS' + str(i)] = k
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@classmethod
|
|
Packit Service |
751c4a |
def _render_subnet_routes(cls, iface_cfg, route_cfg, subnets, flavor):
|
|
Packit Service |
751c4a |
# TODO(rjschwei): route configuration on SUSE distro happens via
|
|
Packit Service |
751c4a |
# ifroute-* files, see lp#1812117. SUSE currently carries a local
|
|
Packit Service |
751c4a |
# patch in their package.
|
|
Packit Service |
751c4a |
if flavor == 'suse':
|
|
Packit Service |
751c4a |
return
|
|
Packit Service |
a04d08 |
for _, subnet in enumerate(subnets, start=len(iface_cfg.children)):
|
|
Packit Service |
a04d08 |
subnet_type = subnet.get('type')
|
|
Packit Service |
a04d08 |
for route in subnet.get('routes', []):
|
|
Packit Service |
a04d08 |
is_ipv6 = subnet.get('ipv6') or is_ipv6_addr(route['gateway'])
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# Any dynamic configuration method, slaac, dhcpv6-stateful/
|
|
Packit Service |
a04d08 |
# stateless should get router information from router RA's.
|
|
Packit Service |
a04d08 |
if (_is_default_route(route) and subnet_type not in
|
|
Packit Service |
a04d08 |
IPV6_DYNAMIC_TYPES):
|
|
Packit Service |
a04d08 |
if (
|
|
Packit Service |
a04d08 |
(subnet.get('ipv4') and
|
|
Packit Service |
a04d08 |
route_cfg.has_set_default_ipv4) or
|
|
Packit Service |
a04d08 |
(subnet.get('ipv6') and
|
|
Packit Service |
a04d08 |
route_cfg.has_set_default_ipv6)
|
|
Packit Service |
a04d08 |
):
|
|
Packit Service |
a04d08 |
raise ValueError("Duplicate declaration of default "
|
|
Packit Service |
a04d08 |
"route found for interface '%s'"
|
|
Packit Service |
a04d08 |
% (iface_cfg.name))
|
|
Packit Service |
a04d08 |
# NOTE(harlowja): ipv6 and ipv4 default gateways
|
|
Packit Service |
a04d08 |
gw_key = 'GATEWAY0'
|
|
Packit Service |
a04d08 |
nm_key = 'NETMASK0'
|
|
Packit Service |
a04d08 |
addr_key = 'ADDRESS0'
|
|
Packit Service |
a04d08 |
# The owning interface provides the default route.
|
|
Packit Service |
a04d08 |
#
|
|
Packit Service |
a04d08 |
# TODO(harlowja): add validation that no other iface has
|
|
Packit Service |
a04d08 |
# also provided the default route?
|
|
Packit Service |
a04d08 |
iface_cfg['DEFROUTE'] = True
|
|
Packit Service |
751c4a |
if iface_cfg['BOOTPROTO'] in ('dhcp', 'dhcp4'):
|
|
Packit Service |
a04d08 |
iface_cfg['DHCLIENT_SET_DEFAULT_ROUTE'] = True
|
|
Packit Service |
a04d08 |
if 'gateway' in route:
|
|
Packit Service |
a04d08 |
if is_ipv6:
|
|
Packit Service |
a04d08 |
iface_cfg['IPV6_DEFAULTGW'] = route['gateway']
|
|
Packit Service |
a04d08 |
route_cfg.has_set_default_ipv6 = True
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
iface_cfg['GATEWAY'] = route['gateway']
|
|
Packit Service |
a04d08 |
route_cfg.has_set_default_ipv4 = True
|
|
Packit Service |
a04d08 |
if 'metric' in route:
|
|
Packit Service |
a04d08 |
iface_cfg['METRIC'] = route['metric']
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
gw_key = 'GATEWAY%s' % route_cfg.last_idx
|
|
Packit Service |
a04d08 |
nm_key = 'NETMASK%s' % route_cfg.last_idx
|
|
Packit Service |
a04d08 |
addr_key = 'ADDRESS%s' % route_cfg.last_idx
|
|
Packit Service |
a04d08 |
metric_key = 'METRIC%s' % route_cfg.last_idx
|
|
Packit Service |
a04d08 |
route_cfg.last_idx += 1
|
|
Packit Service |
a04d08 |
# add default routes only to ifcfg files, not
|
|
Packit Service |
a04d08 |
# to route-* or route6-*
|
|
Packit Service |
a04d08 |
for (old_key, new_key) in [('gateway', gw_key),
|
|
Packit Service |
a04d08 |
('metric', metric_key),
|
|
Packit Service |
a04d08 |
('netmask', nm_key),
|
|
Packit Service |
a04d08 |
('network', addr_key)]:
|
|
Packit Service |
a04d08 |
if old_key in route:
|
|
Packit Service |
a04d08 |
route_cfg[new_key] = route[old_key]
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@classmethod
|
|
Packit Service |
a04d08 |
def _render_bonding_opts(cls, iface_cfg, iface):
|
|
Packit Service |
a04d08 |
bond_opts = []
|
|
Packit Service |
a04d08 |
for (bond_key, value_tpl) in cls.bond_tpl_opts:
|
|
Packit Service |
a04d08 |
# Seems like either dash or underscore is possible?
|
|
Packit Service |
a04d08 |
bond_keys = [bond_key, bond_key.replace("_", "-")]
|
|
Packit Service |
a04d08 |
for bond_key in bond_keys:
|
|
Packit Service |
a04d08 |
if bond_key in iface:
|
|
Packit Service |
a04d08 |
bond_value = iface[bond_key]
|
|
Packit Service |
a04d08 |
if isinstance(bond_value, (tuple, list)):
|
|
Packit Service |
a04d08 |
bond_value = " ".join(bond_value)
|
|
Packit Service |
a04d08 |
bond_opts.append(value_tpl % (bond_value))
|
|
Packit Service |
a04d08 |
break
|
|
Packit Service |
a04d08 |
if bond_opts:
|
|
Packit Service |
a04d08 |
iface_cfg['BONDING_OPTS'] = " ".join(bond_opts)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@classmethod
|
|
Packit Service |
751c4a |
def _render_physical_interfaces(
|
|
Packit Service |
751c4a |
cls, network_state, iface_contents, flavor
|
|
Packit Service |
751c4a |
):
|
|
Packit Service |
a04d08 |
physical_filter = renderer.filter_by_physical
|
|
Packit Service |
a04d08 |
for iface in network_state.iter_interfaces(physical_filter):
|
|
Packit Service |
a04d08 |
iface_name = iface['name']
|
|
Packit Service |
a04d08 |
iface_subnets = iface.get("subnets", [])
|
|
Packit Service |
a04d08 |
iface_cfg = iface_contents[iface_name]
|
|
Packit Service |
a04d08 |
route_cfg = iface_cfg.routes
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
cls._render_subnets(
|
|
Packit Service |
751c4a |
iface_cfg, iface_subnets, network_state.has_default_route,
|
|
Packit Service |
751c4a |
flavor
|
|
Packit Service |
751c4a |
)
|
|
Packit Service |
751c4a |
cls._render_subnet_routes(
|
|
Packit Service |
751c4a |
iface_cfg, route_cfg, iface_subnets, flavor
|
|
Packit Service |
a04d08 |
)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@classmethod
|
|
Packit Service |
751c4a |
def _render_bond_interfaces(cls, network_state, iface_contents, flavor):
|
|
Packit Service |
a04d08 |
bond_filter = renderer.filter_by_type('bond')
|
|
Packit Service |
a04d08 |
slave_filter = renderer.filter_by_attr('bond-master')
|
|
Packit Service |
a04d08 |
for iface in network_state.iter_interfaces(bond_filter):
|
|
Packit Service |
a04d08 |
iface_name = iface['name']
|
|
Packit Service |
a04d08 |
iface_cfg = iface_contents[iface_name]
|
|
Packit Service |
a04d08 |
cls._render_bonding_opts(iface_cfg, iface)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# Ensure that the master interface (and any of its children)
|
|
Packit Service |
a04d08 |
# are actually marked as being bond types...
|
|
Packit Service |
a04d08 |
master_cfgs = [iface_cfg]
|
|
Packit Service |
a04d08 |
master_cfgs.extend(iface_cfg.children)
|
|
Packit Service |
a04d08 |
for master_cfg in master_cfgs:
|
|
Packit Service |
a04d08 |
master_cfg['BONDING_MASTER'] = True
|
|
Packit Service |
751c4a |
if flavor != 'suse':
|
|
Packit Service |
751c4a |
master_cfg.kind = 'bond'
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if iface.get('mac_address'):
|
|
Packit Service |
751c4a |
if flavor == 'suse':
|
|
Packit Service |
751c4a |
iface_cfg['LLADDR'] = iface.get('mac_address')
|
|
Packit Service |
751c4a |
else:
|
|
Packit Service |
751c4a |
iface_cfg['MACADDR'] = iface.get('mac_address')
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
iface_subnets = iface.get("subnets", [])
|
|
Packit Service |
a04d08 |
route_cfg = iface_cfg.routes
|
|
Packit Service |
a04d08 |
cls._render_subnets(
|
|
Packit Service |
751c4a |
iface_cfg, iface_subnets, network_state.has_default_route,
|
|
Packit Service |
751c4a |
flavor
|
|
Packit Service |
751c4a |
)
|
|
Packit Service |
751c4a |
cls._render_subnet_routes(
|
|
Packit Service |
751c4a |
iface_cfg, route_cfg, iface_subnets, flavor
|
|
Packit Service |
a04d08 |
)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# iter_interfaces on network-state is not sorted to produce
|
|
Packit Service |
a04d08 |
# consistent numbers we need to sort.
|
|
Packit Service |
a04d08 |
bond_slaves = sorted(
|
|
Packit Service |
a04d08 |
[slave_iface['name'] for slave_iface in
|
|
Packit Service |
a04d08 |
network_state.iter_interfaces(slave_filter)
|
|
Packit Service |
a04d08 |
if slave_iface['bond-master'] == iface_name])
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
for index, bond_slave in enumerate(bond_slaves):
|
|
Packit Service |
751c4a |
if flavor == 'suse':
|
|
Packit Service |
751c4a |
slavestr = 'BONDING_SLAVE_%s' % index
|
|
Packit Service |
751c4a |
else:
|
|
Packit Service |
751c4a |
slavestr = 'BONDING_SLAVE%s' % index
|
|
Packit Service |
a04d08 |
iface_cfg[slavestr] = bond_slave
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
slave_cfg = iface_contents[bond_slave]
|
|
Packit Service |
751c4a |
if flavor == 'suse':
|
|
Packit Service |
751c4a |
slave_cfg['BOOTPROTO'] = 'none'
|
|
Packit Service |
751c4a |
slave_cfg['STARTMODE'] = 'hotplug'
|
|
Packit Service |
751c4a |
else:
|
|
Packit Service |
751c4a |
slave_cfg['MASTER'] = iface_name
|
|
Packit Service |
751c4a |
slave_cfg['SLAVE'] = True
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@classmethod
|
|
Packit Service |
751c4a |
def _render_vlan_interfaces(cls, network_state, iface_contents, flavor):
|
|
Packit Service |
a04d08 |
vlan_filter = renderer.filter_by_type('vlan')
|
|
Packit Service |
a04d08 |
for iface in network_state.iter_interfaces(vlan_filter):
|
|
Packit Service |
a04d08 |
iface_name = iface['name']
|
|
Packit Service |
a04d08 |
iface_cfg = iface_contents[iface_name]
|
|
Packit Service |
751c4a |
if flavor == 'suse':
|
|
Packit Service |
751c4a |
vlan_id = iface.get('vlan_id')
|
|
Packit Service |
751c4a |
if vlan_id:
|
|
Packit Service |
751c4a |
iface_cfg['VLAN_ID'] = vlan_id
|
|
Packit Service |
751c4a |
iface_cfg['ETHERDEVICE'] = iface_name[:iface_name.rfind('.')]
|
|
Packit Service |
751c4a |
else:
|
|
Packit Service |
751c4a |
iface_cfg['VLAN'] = True
|
|
Packit Service |
751c4a |
iface_cfg['PHYSDEV'] = iface_name[:iface_name.rfind('.')]
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
iface_subnets = iface.get("subnets", [])
|
|
Packit Service |
a04d08 |
route_cfg = iface_cfg.routes
|
|
Packit Service |
a04d08 |
cls._render_subnets(
|
|
Packit Service |
751c4a |
iface_cfg, iface_subnets, network_state.has_default_route,
|
|
Packit Service |
751c4a |
flavor
|
|
Packit Service |
751c4a |
)
|
|
Packit Service |
751c4a |
cls._render_subnet_routes(
|
|
Packit Service |
751c4a |
iface_cfg, route_cfg, iface_subnets, flavor
|
|
Packit Service |
a04d08 |
)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@staticmethod
|
|
Packit Service |
a04d08 |
def _render_dns(network_state, existing_dns_path=None):
|
|
Packit Service |
a04d08 |
# skip writing resolv.conf if network_state doesn't include any input.
|
|
Packit Service |
a04d08 |
if not any([len(network_state.dns_nameservers),
|
|
Packit Service |
a04d08 |
len(network_state.dns_searchdomains)]):
|
|
Packit Service |
a04d08 |
return None
|
|
Packit Service |
a04d08 |
content = resolv_conf.ResolvConf("")
|
|
Packit Service |
a04d08 |
if existing_dns_path and os.path.isfile(existing_dns_path):
|
|
Packit Service |
a04d08 |
content = resolv_conf.ResolvConf(util.load_file(existing_dns_path))
|
|
Packit Service |
a04d08 |
for nameserver in network_state.dns_nameservers:
|
|
Packit Service |
a04d08 |
content.add_nameserver(nameserver)
|
|
Packit Service |
a04d08 |
for searchdomain in network_state.dns_searchdomains:
|
|
Packit Service |
a04d08 |
content.add_search_domain(searchdomain)
|
|
Packit Service |
a04d08 |
header = _make_header(';')
|
|
Packit Service |
a04d08 |
content_str = str(content)
|
|
Packit Service |
a04d08 |
if not content_str.startswith(header):
|
|
Packit Service |
a04d08 |
content_str = header + '\n' + content_str
|
|
Packit Service |
a04d08 |
return content_str
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@staticmethod
|
|
Packit Service |
a04d08 |
def _render_networkmanager_conf(network_state, templates=None):
|
|
Packit Service |
a04d08 |
content = networkmanager_conf.NetworkManagerConf("")
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# If DNS server information is provided, configure
|
|
Packit Service |
a04d08 |
# NetworkManager to not manage dns, so that /etc/resolv.conf
|
|
Packit Service |
a04d08 |
# does not get clobbered.
|
|
Packit Service |
a04d08 |
if network_state.dns_nameservers:
|
|
Packit Service |
a04d08 |
content.set_section_keypair('main', 'dns', 'none')
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if len(content) == 0:
|
|
Packit Service |
a04d08 |
return None
|
|
Packit Service |
a04d08 |
out = "".join([_make_header(), "\n", "\n".join(content.write()), "\n"])
|
|
Packit Service |
a04d08 |
return out
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@classmethod
|
|
Packit Service |
751c4a |
def _render_bridge_interfaces(cls, network_state, iface_contents, flavor):
|
|
Packit Service |
751c4a |
bridge_key_map = {
|
|
Packit Service |
751c4a |
old_k: new_k for old_k, new_k in cls.cfg_key_maps[flavor].items()
|
|
Packit Service |
751c4a |
if old_k.startswith('bridge')}
|
|
Packit Service |
a04d08 |
bridge_filter = renderer.filter_by_type('bridge')
|
|
Packit Service |
751c4a |
|
|
Packit Service |
a04d08 |
for iface in network_state.iter_interfaces(bridge_filter):
|
|
Packit Service |
a04d08 |
iface_name = iface['name']
|
|
Packit Service |
a04d08 |
iface_cfg = iface_contents[iface_name]
|
|
Packit Service |
751c4a |
if flavor != 'suse':
|
|
Packit Service |
751c4a |
iface_cfg.kind = 'bridge'
|
|
Packit Service |
751c4a |
for old_key, new_key in bridge_key_map.items():
|
|
Packit Service |
a04d08 |
if old_key in iface:
|
|
Packit Service |
a04d08 |
iface_cfg[new_key] = iface[old_key]
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
751c4a |
if flavor == 'suse':
|
|
Packit Service |
751c4a |
if 'BRIDGE_STP' in iface_cfg:
|
|
Packit Service |
751c4a |
if iface_cfg.get('BRIDGE_STP'):
|
|
Packit Service |
751c4a |
iface_cfg['BRIDGE_STP'] = 'on'
|
|
Packit Service |
751c4a |
else:
|
|
Packit Service |
751c4a |
iface_cfg['BRIDGE_STP'] = 'off'
|
|
Packit Service |
11b429 |
|
|
Packit Service |
751c4a |
if iface.get('mac_address'):
|
|
Packit Service |
751c4a |
key = 'MACADDR'
|
|
Packit Service |
751c4a |
if flavor == 'suse':
|
|
Packit Service |
751c4a |
key = 'LLADDRESS'
|
|
Packit Service |
751c4a |
iface_cfg[key] = iface.get('mac_address')
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
if flavor == 'suse':
|
|
Packit Service |
751c4a |
if iface.get('bridge_ports', []):
|
|
Packit Service |
751c4a |
iface_cfg['BRIDGE_PORTS'] = '%s' % " ".join(
|
|
Packit Service |
751c4a |
iface.get('bridge_ports')
|
|
Packit Service |
751c4a |
)
|
|
Packit Service |
a04d08 |
# Is this the right key to get all the connected interfaces?
|
|
Packit Service |
a04d08 |
for bridged_iface_name in iface.get('bridge_ports', []):
|
|
Packit Service |
a04d08 |
# Ensure all bridged interfaces are correctly tagged
|
|
Packit Service |
a04d08 |
# as being bridged to this interface.
|
|
Packit Service |
a04d08 |
bridged_cfg = iface_contents[bridged_iface_name]
|
|
Packit Service |
a04d08 |
bridged_cfgs = [bridged_cfg]
|
|
Packit Service |
a04d08 |
bridged_cfgs.extend(bridged_cfg.children)
|
|
Packit Service |
a04d08 |
for bridge_cfg in bridged_cfgs:
|
|
Packit Service |
751c4a |
bridge_value = iface_name
|
|
Packit Service |
751c4a |
if flavor == 'suse':
|
|
Packit Service |
751c4a |
bridge_value = 'yes'
|
|
Packit Service |
751c4a |
bridge_cfg['BRIDGE'] = bridge_value
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
iface_subnets = iface.get("subnets", [])
|
|
Packit Service |
a04d08 |
route_cfg = iface_cfg.routes
|
|
Packit Service |
a04d08 |
cls._render_subnets(
|
|
Packit Service |
751c4a |
iface_cfg, iface_subnets, network_state.has_default_route,
|
|
Packit Service |
751c4a |
flavor
|
|
Packit Service |
751c4a |
)
|
|
Packit Service |
751c4a |
cls._render_subnet_routes(
|
|
Packit Service |
751c4a |
iface_cfg, route_cfg, iface_subnets, flavor
|
|
Packit Service |
a04d08 |
)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@classmethod
|
|
Packit Service |
751c4a |
def _render_ib_interfaces(cls, network_state, iface_contents, flavor):
|
|
Packit Service |
a04d08 |
ib_filter = renderer.filter_by_type('infiniband')
|
|
Packit Service |
a04d08 |
for iface in network_state.iter_interfaces(ib_filter):
|
|
Packit Service |
a04d08 |
iface_name = iface['name']
|
|
Packit Service |
a04d08 |
iface_cfg = iface_contents[iface_name]
|
|
Packit Service |
a04d08 |
iface_cfg.kind = 'infiniband'
|
|
Packit Service |
a04d08 |
iface_subnets = iface.get("subnets", [])
|
|
Packit Service |
a04d08 |
route_cfg = iface_cfg.routes
|
|
Packit Service |
a04d08 |
cls._render_subnets(
|
|
Packit Service |
751c4a |
iface_cfg, iface_subnets, network_state.has_default_route,
|
|
Packit Service |
751c4a |
flavor
|
|
Packit Service |
751c4a |
)
|
|
Packit Service |
751c4a |
cls._render_subnet_routes(
|
|
Packit Service |
751c4a |
iface_cfg, route_cfg, iface_subnets, flavor
|
|
Packit Service |
a04d08 |
)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@classmethod
|
|
Packit Service |
751c4a |
def _render_sysconfig(cls, base_sysconf_dir, network_state, flavor,
|
|
Packit Service |
a04d08 |
templates=None):
|
|
Packit Service |
a04d08 |
'''Given state, return /etc/sysconfig files + contents'''
|
|
Packit Service |
a04d08 |
if not templates:
|
|
Packit Service |
a04d08 |
templates = cls.templates
|
|
Packit Service |
a04d08 |
iface_contents = {}
|
|
Packit Service |
a04d08 |
for iface in network_state.iter_interfaces():
|
|
Packit Service |
a04d08 |
if iface['type'] == "loopback":
|
|
Packit Service |
a04d08 |
continue
|
|
Packit Service |
a04d08 |
iface_name = iface['name']
|
|
Packit Service |
a04d08 |
iface_cfg = NetInterface(iface_name, base_sysconf_dir, templates)
|
|
Packit Service |
751c4a |
if flavor == 'suse':
|
|
Packit Service |
751c4a |
iface_cfg.drop('DEVICE')
|
|
Packit Service |
751c4a |
# If type detection fails it is considered a bug in SUSE
|
|
Packit Service |
751c4a |
iface_cfg.drop('TYPE')
|
|
Packit Service |
751c4a |
cls._render_iface_shared(iface, iface_cfg, flavor)
|
|
Packit Service |
a04d08 |
iface_contents[iface_name] = iface_cfg
|
|
Packit Service |
751c4a |
cls._render_physical_interfaces(network_state, iface_contents, flavor)
|
|
Packit Service |
751c4a |
cls._render_bond_interfaces(network_state, iface_contents, flavor)
|
|
Packit Service |
751c4a |
cls._render_vlan_interfaces(network_state, iface_contents, flavor)
|
|
Packit Service |
751c4a |
cls._render_bridge_interfaces(network_state, iface_contents, flavor)
|
|
Packit Service |
751c4a |
cls._render_ib_interfaces(network_state, iface_contents, flavor)
|
|
Packit Service |
a04d08 |
contents = {}
|
|
Packit Service |
a04d08 |
for iface_name, iface_cfg in iface_contents.items():
|
|
Packit Service |
a04d08 |
if iface_cfg or iface_cfg.children:
|
|
Packit Service |
a04d08 |
contents[iface_cfg.path] = iface_cfg.to_string()
|
|
Packit Service |
a04d08 |
for iface_cfg in iface_cfg.children:
|
|
Packit Service |
a04d08 |
if iface_cfg:
|
|
Packit Service |
a04d08 |
contents[iface_cfg.path] = iface_cfg.to_string()
|
|
Packit Service |
a04d08 |
if iface_cfg.routes:
|
|
Packit Service |
a04d08 |
for cpath, proto in zip([iface_cfg.routes.path_ipv4,
|
|
Packit Service |
a04d08 |
iface_cfg.routes.path_ipv6],
|
|
Packit Service |
a04d08 |
["ipv4", "ipv6"]):
|
|
Packit Service |
a04d08 |
if cpath not in contents:
|
|
Packit Service |
a04d08 |
contents[cpath] = iface_cfg.routes.to_string(proto)
|
|
Packit Service |
a04d08 |
return contents
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def render_network_state(self, network_state, templates=None, target=None):
|
|
Packit Service |
a04d08 |
if not templates:
|
|
Packit Service |
a04d08 |
templates = self.templates
|
|
Packit Service |
a04d08 |
file_mode = 0o644
|
|
Packit Service |
751c4a |
base_sysconf_dir = subp.target_path(target, self.sysconf_dir)
|
|
Packit Service |
a04d08 |
for path, data in self._render_sysconfig(base_sysconf_dir,
|
|
Packit Service |
751c4a |
network_state, self.flavor,
|
|
Packit Service |
a04d08 |
templates=templates).items():
|
|
Packit Service |
a04d08 |
util.write_file(path, data, file_mode)
|
|
Packit Service |
a04d08 |
if self.dns_path:
|
|
Packit Service |
751c4a |
dns_path = subp.target_path(target, self.dns_path)
|
|
Packit Service |
a04d08 |
resolv_content = self._render_dns(network_state,
|
|
Packit Service |
a04d08 |
existing_dns_path=dns_path)
|
|
Packit Service |
a04d08 |
if resolv_content:
|
|
Packit Service |
a04d08 |
util.write_file(dns_path, resolv_content, file_mode)
|
|
Packit Service |
a04d08 |
if self.networkmanager_conf_path:
|
|
Packit Service |
751c4a |
nm_conf_path = subp.target_path(target,
|
|
Packit Service |
a04d08 |
self.networkmanager_conf_path)
|
|
Packit Service |
a04d08 |
nm_conf_content = self._render_networkmanager_conf(network_state,
|
|
Packit Service |
a04d08 |
templates)
|
|
Packit Service |
a04d08 |
if nm_conf_content:
|
|
Packit Service |
a04d08 |
util.write_file(nm_conf_path, nm_conf_content, file_mode)
|
|
Packit Service |
a04d08 |
if self.netrules_path:
|
|
Packit Service |
a04d08 |
netrules_content = self._render_persistent_net(network_state)
|
|
Packit Service |
751c4a |
netrules_path = subp.target_path(target, self.netrules_path)
|
|
Packit Service |
a04d08 |
util.write_file(netrules_path, netrules_content, file_mode)
|
|
Packit Service |
a04d08 |
if available_nm(target=target):
|
|
Packit Service |
751c4a |
enable_ifcfg_rh(subp.target_path(target, path=NM_CFG_FILE))
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
751c4a |
sysconfig_path = subp.target_path(target, templates.get('control'))
|
|
Packit Service |
a04d08 |
# Distros configuring /etc/sysconfig/network as a file e.g. Centos
|
|
Packit Service |
a04d08 |
if sysconfig_path.endswith('network'):
|
|
Packit Service |
a04d08 |
util.ensure_dir(os.path.dirname(sysconfig_path))
|
|
Packit Service |
814755 |
netcfg = []
|
|
Packit Service |
814755 |
for line in util.load_file(sysconfig_path, quiet=True).split('\n'):
|
|
Packit Service |
814755 |
if 'cloud-init' in line:
|
|
Packit Service |
814755 |
break
|
|
Packit Service |
814755 |
if not line.startswith(('NETWORKING=',
|
|
Packit Service |
814755 |
'IPV6_AUTOCONF=',
|
|
Packit Service |
814755 |
'NETWORKING_IPV6=')):
|
|
Packit Service |
814755 |
netcfg.append(line)
|
|
Packit Service |
814755 |
# Now generate the cloud-init portion of sysconfig/network
|
|
Packit Service |
814755 |
netcfg.extend([_make_header(), 'NETWORKING=yes'])
|
|
Packit Service |
a04d08 |
if network_state.use_ipv6:
|
|
Packit Service |
a04d08 |
netcfg.append('NETWORKING_IPV6=yes')
|
|
Packit Service |
a04d08 |
netcfg.append('IPV6_AUTOCONF=no')
|
|
Packit Service |
a04d08 |
util.write_file(sysconfig_path,
|
|
Packit Service |
a04d08 |
"\n".join(netcfg) + "\n", file_mode)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def available(target=None):
|
|
Packit Service |
a04d08 |
sysconfig = available_sysconfig(target=target)
|
|
Packit Service |
a04d08 |
nm = available_nm(target=target)
|
|
Packit Service |
a04d08 |
return (util.system_info()['variant'] in KNOWN_DISTROS
|
|
Packit Service |
a04d08 |
and any([nm, sysconfig]))
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def available_sysconfig(target=None):
|
|
Packit Service |
a04d08 |
expected = ['ifup', 'ifdown']
|
|
Packit Service |
a04d08 |
search = ['/sbin', '/usr/sbin']
|
|
Packit Service |
a04d08 |
for p in expected:
|
|
Packit Service |
751c4a |
if not subp.which(p, search=search, target=target):
|
|
Packit Service |
a04d08 |
return False
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
expected_paths = [
|
|
Packit Service |
a04d08 |
'etc/sysconfig/network-scripts/network-functions',
|
|
Packit Service |
a04d08 |
'etc/sysconfig/config']
|
|
Packit Service |
a04d08 |
for p in expected_paths:
|
|
Packit Service |
751c4a |
if os.path.isfile(subp.target_path(target, p)):
|
|
Packit Service |
a04d08 |
return True
|
|
Packit Service |
a04d08 |
return False
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def available_nm(target=None):
|
|
Packit Service |
751c4a |
if not os.path.isfile(subp.target_path(target, path=NM_CFG_FILE)):
|
|
Packit Service |
a04d08 |
return False
|
|
Packit Service |
a04d08 |
return True
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# vi: ts=4 expandtab
|