aif-ng/aif/network/networkmanager.py

160 lines
6.3 KiB
Python

import configparser
import datetime
import ipaddress
import os
import uuid
##
import aif.utils
# TODO: auto dev assignment
class Connection(object):
def __init__(self, iface_xml):
self.xml = iface_xml
self.id = self.xml.attrib['id']
self.device = self.xml.attrib['device']
self.is_defroute = aif.utils.xmlBool(self.xml.attrib.get('defroute', 'false'))
self.domain = self.xml.attrib.get('searchDomain', None)
self._cfg = None
self.connection_type = None
self.provider_type = 'NetworkManager'
self.addrs = {'ipv4': set(),
'ipv6': set()}
self.resolvers = []
self.uuid = uuid.uuid4()
self._initAddrs()
self._initResolvers()
def _initAddrs(self):
# These tuples follow either:
# ('dhcp'/'dhcp6'/'slaac', None, None) for auto configuration
# (ipaddress.IPv4/6Address(IP), CIDR, ipaddress.IPv4/6Address(GW)) for static configuration
for addrtype in ('ipv4', 'ipv6'):
for a in self.xml.findall('addresses/{0}/address'.format(addrtype)):
if a.text in ('dhcp', 'dhcp6', 'slaac'):
addr = a.text
net = None
gw = None
else:
components = a.text.split('/')
if len(components) > 2:
raise ValueError('Invalid IP/CIDR format: {0}'.format(a.text))
if len(components) == 1:
addr = components[0]
if addrtype == 'ipv4':
components.append('24')
elif addrtype == 'ipv6':
components.append('64')
addr = ipaddress.ip_address(components[0])
net = ipaddress.ip_network('/'.join(components), strict = False)
gw = ipaddress.ip_address(a.attrib.get('gateway'))
self.addrs[addrtype].add((addr, net, gw))
self.addrs[addrtype] = list(self.addrs[addrtype])
return()
def _initCfg(self):
self._cfg = configparser.ConfigParser()
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.
for addrtype, addrs in self.addrs.items():
self._cfg[addrtype] = {}
cidr_gws = {}
self._cfg[addrtype]['dns-search'] = (self.domain if self.domain else '')
if addrtype == 'ipv6':
self._cfg[addrtype]['addr-gen-mode'] = 'stable-privacy'
if not addrs:
self._cfg[addrtype]['method'] = 'ignore'
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
if addrtype == 'ipv4':
if ip == 'dhcp':
self._cfg[addrtype]['method'] = 'auto'
continue
elif addrtype == 'ipv6':
if ip == 'dhcp6':
self._cfg[addrtype]['method'] = 'dhcp'
continue
elif ip == 'slaac':
self._cfg[addrtype]['method'] = 'auto'
continue
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
for r in self.resolvers:
if addrtype == 'ipv{0}'.format(r.version):
if 'dns' not in self._cfg[addrtype]:
self._cfg[addrtype]['dns'] = []
self._cfg[addrtype]['dns'].append(str(r))
if 'dns' in self._cfg[addrtype].keys():
self._cfg[addrtype]['dns'] = '{0};'.format(';'.join(self._cfg[addrtype]['dns']))
self._initConnCfg()
return()
def _initConnCfg(self):
# A dummy method; this is overridden by the subclasses.
# It's honestly here to make my IDE stop complaining. :)
pass
return()
def _initResolvers(self):
for r in self.xml.findall('resolvers/resolver'):
resolver = ipaddress.ip_address(r.text)
if resolver not in self.resolvers:
self.resolvers.append(resolver)
return()
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)
return()
class Ethernet(Connection):
def __init__(self, iface_xml):
super().__init__(iface_xml)
self.connection_type = 'ethernet'
self._initCfg()
def _initConnCfg(self):
pass
class Wireless(Connection):
def __init__(self, iface_xml):
super().__init__(iface_xml)
self.connection_type = 'wireless'
self._initCfg()
def _initConnCfg(self):
pass