aif-ng/aif/network/networkmanager.py

169 lines
7.6 KiB
Python

import configparser
import datetime
import logging
import os
import uuid
##
from . import _common
_logger = logging.getLogger(__name__)
class Connection(_common.BaseConnection):
def __init__(self, iface_xml):
super().__init__(iface_xml)
self.provider_type = 'NetworkManager'
self.packages = set('networkmanager')
self.services = {
('/usr/lib/systemd/system/NetworkManager.service'): ('etc/systemd/system/'
'multi-user.target.wants/'
'NetworkManager.service'),
('/usr/lib/systemd/system/NetworkManager-dispatcher.service'): ('etc/systemd/system/'
'dbus-org.freedesktop.'
'nm-dispatcher.service'),
('/usr/lib/systemd/system/NetworkManager-wait-online.service'): ('etc/systemd/'
'system/'
'network-online.target.wants/'
'NetworkManager-wait-online.service')}
self.uuid = uuid.uuid4()
def _initCfg(self):
_logger.info('Building config.')
if self.device == 'auto':
self.device = _common.getDefIface(self.connection_type)
self._cfg = configparser.ConfigParser(allow_no_value = True, interpolation = None)
self._cfg.optionxform = str
self._cfg['connection'] = {'id': self.id,
'uuid': self.uuid,
'type': self.connection_type,
'interface-name': self.device,
'permissions': '',
'timestamp': datetime.datetime.utcnow().timestamp()}
# We *theoretically* could do this in _initAddrs() but we do it separately so we can trim out duplicates.
# TODO: rework this? we technically don't need to split in ipv4/ipv6 since ipaddress does that for us.
for addrtype, addrs in self.addrs.items():
self._cfg[addrtype] = {}
cidr_gws = {}
# Routing
if not self.is_defroute:
self._cfg[addrtype]['never-default'] = 'true'
if not self.auto['routes'][addrtype]:
self._cfg[addrtype]['ignore-auto-routes'] = 'true'
# DNS
self._cfg[addrtype]['dns-search'] = (self.domain if self.domain else '')
if not self.auto['resolvers'][addrtype]:
self._cfg[addrtype]['ignore-auto-dns'] = 'true'
# Address handling
if addrtype == 'ipv6':
self._cfg[addrtype]['addr-gen-mode'] = 'stable-privacy'
if not addrs and not self.auto['addresses'][addrtype]:
self._cfg[addrtype]['method'] = 'ignore'
elif self.auto['addresses'][addrtype]:
if addrtype == 'ipv4':
self._cfg[addrtype]['method'] = 'auto'
else:
self._cfg[addrtype]['method'] = ('auto' if self.auto['addresses'][addrtype] == 'slaac'
else 'dhcp6')
else:
self._cfg[addrtype]['method'] = 'manual'
for idx, (ip, cidr, gw) in enumerate(addrs):
if cidr not in cidr_gws.keys():
cidr_gws[cidr] = gw
new_cidr = True
else:
new_cidr = False
addrnum = idx + 1
addr_str = '{0}/{1}'.format(str(ip), str(cidr.prefixlen))
if new_cidr:
addr_str = '{0},{1}'.format(addr_str, str(gw))
self._cfg[addrtype]['address{0}'.format(addrnum)] = addr_str
# Resolvers
for resolver in self.resolvers:
if addrtype == 'ipv{0}'.format(resolver.version):
if 'dns' not in self._cfg[addrtype]:
self._cfg[addrtype]['dns'] = []
self._cfg[addrtype]['dns'].append(str(resolver))
if 'dns' in self._cfg[addrtype].keys():
self._cfg[addrtype]['dns'] = '{0};'.format(';'.join(self._cfg[addrtype]['dns']))
# Routes
for idx, (dest, net, gw) in self.routes[addrtype]:
routenum = idx + 1
self._cfg[addrtype]['route{0}'.format(routenum)] = '{0}/{1},{2}'.format(str(dest),
str(net.prefixlen),
str(gw))
self._initConnCfg()
_logger.info('Config built successfully.')
# TODO: does this render correctly?
# This is only for debug logging.
_logout = {}
for s in self._cfg.sections():
_logout[s] = dict(self._cfg[s])
_logger.debug('Config: {0}'.format(_logout))
return(None)
def writeConf(self, chroot_base):
cfgroot = os.path.join(chroot_base, 'etc', 'NetworkManager')
cfgdir = os.path.join(cfgroot, 'system-connections')
cfgpath = os.path.join(cfgdir, '{0}.nmconnection'.format(self.id))
os.makedirs(cfgdir, exist_ok = True)
with open(cfgpath, 'w') as fh:
self._cfg.write(fh, space_around_delimiters = False)
for root, dirs, files in os.walk(cfgroot):
os.chown(root, 0, 0)
for d in dirs:
dpath = os.path.join(root, d)
os.chown(dpath, 0, 0)
for f in files:
fpath = os.path.join(root, f)
os.chown(fpath, 0, 0)
os.chmod(cfgroot, 0o0755)
os.chmod(cfgdir, 0o0700)
os.chmod(cfgpath, 0o0600)
_logger.info('Wrote: {0}'.format(cfgpath))
return(None)
class Ethernet(Connection):
def __init__(self, iface_xml):
super().__init__(iface_xml)
self.connection_type = 'ethernet'
self._initCfg()
def _initConnCfg(self):
self._cfg[self.connection_type] = {'mac-address-blacklist': ''}
return(None)
class Wireless(Connection):
def __init__(self, iface_xml):
super().__init__(iface_xml)
self.connection_type = 'wireless'
self._initCfg()
def _initConnCfg(self):
self._cfg['wifi'] = {'mac-address-blacklist': '',
'mode': 'infrastructure',
'ssid': self.xml.attrib['essid']}
try:
bssid = self.xml.attrib.get('bssid').strip()
except AttributeError:
bssid = None
if bssid:
bssid = _common.canonizeEUI(bssid)
self._cfg['wifi']['bssid'] = bssid
self._cfg['wifi']['seen-bssids'] = '{0};'.format(bssid)
crypto = self.xml.find('encryption')
if crypto:
self.packages.add('wpa_supplicant')
self._cfg['wifi-security'] = {}
crypto = _common.convertWifiCrypto(crypto, self._cfg['wifi']['ssid'])
# if crypto['type'] in ('wpa', 'wpa2', 'wpa3'):
if crypto['type'] in ('wpa', 'wpa2'):
# TODO: WPA2 enterprise
self._cfg['wifi-security']['key-mgmt'] = 'wpa-psk'
# if crypto['type'] in ('wep', 'wpa', 'wpa2', 'wpa3'):
if crypto['type'] in ('wpa', 'wpa2'):
self._cfg['wifi-security']['psk'] = crypto['auth']['psk']
return(None)