2015-06-24 19:44:31 +00:00
|
|
|
# Copyright 2015 Canonical, Ltd.
|
|
|
|
#
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU Affero General Public License as
|
|
|
|
# published by the Free Software Foundation, either version 3 of the
|
|
|
|
# License, or (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU Affero General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
2016-11-07 00:35:42 +00:00
|
|
|
import copy
|
2015-09-10 18:25:54 +00:00
|
|
|
import ipaddress
|
2015-06-24 19:44:31 +00:00
|
|
|
import logging
|
2016-11-07 03:34:43 +00:00
|
|
|
from socket import AF_INET, AF_INET6
|
2016-09-16 02:28:40 +00:00
|
|
|
|
2018-05-24 21:06:09 +00:00
|
|
|
from subiquitycore import netplan
|
2016-09-16 02:28:40 +00:00
|
|
|
|
2015-06-24 19:44:31 +00:00
|
|
|
|
2016-08-22 03:49:28 +00:00
|
|
|
NETDEV_IGNORED_IFACE_NAMES = ['lo']
|
2016-09-05 09:21:05 +00:00
|
|
|
NETDEV_IGNORED_IFACE_TYPES = ['bridge', 'tun', 'tap', 'dummy', 'sit']
|
2016-06-30 18:17:01 +00:00
|
|
|
log = logging.getLogger('subiquitycore.models.network')
|
2015-07-21 15:55:02 +00:00
|
|
|
|
|
|
|
|
2016-11-07 00:35:42 +00:00
|
|
|
def ip_version(ip):
|
|
|
|
return ipaddress.ip_interface(ip).version
|
2016-08-15 21:05:19 +00:00
|
|
|
|
|
|
|
|
2016-11-07 00:35:42 +00:00
|
|
|
class Networkdev:
|
|
|
|
"""A Networkdev is console-conf's view of a network device.
|
|
|
|
|
|
|
|
The view code only ever sees objects of this type.
|
|
|
|
|
|
|
|
There are two 'sides' to a Networkdev: the state the device is
|
|
|
|
actually in, and the device's configuration. Where there is
|
|
|
|
abiguity (e.g. when it comes to IP addresses), the former has
|
2016-12-12 02:58:41 +00:00
|
|
|
attribute names like "actual_ip_addresses_for_version" and the
|
|
|
|
latter has names like "configured_ip_addresses_for_version".
|
2016-11-07 00:35:42 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, net_info, configuration):
|
|
|
|
self._net_info = net_info
|
|
|
|
self._configuration = configuration
|
2016-08-13 04:13:22 +00:00
|
|
|
|
|
|
|
def render(self):
|
2016-11-07 00:35:42 +00:00
|
|
|
if self.configured_ip_addresses or self.dhcp4 or self.dhcp6:
|
|
|
|
return {self.name: self._configuration}
|
|
|
|
else:
|
|
|
|
return {}
|
2016-06-22 19:19:54 +00:00
|
|
|
|
2016-12-20 20:42:37 +00:00
|
|
|
@property
|
|
|
|
def configured(self):
|
|
|
|
if self.configured_ip_addresses or self.dhcp4 or self.dhcp6:
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2015-11-06 15:19:44 +00:00
|
|
|
@property
|
2016-11-07 00:35:42 +00:00
|
|
|
def name(self):
|
|
|
|
return self._net_info.name
|
2015-11-06 15:19:44 +00:00
|
|
|
|
2016-11-07 02:03:27 +00:00
|
|
|
@property
|
|
|
|
def ifindex(self):
|
|
|
|
return self._net_info.ifindex
|
|
|
|
|
2015-11-06 15:19:44 +00:00
|
|
|
@property
|
|
|
|
def type(self):
|
2016-11-07 00:35:42 +00:00
|
|
|
return self._net_info.type
|
|
|
|
|
|
|
|
@property
|
|
|
|
def hwaddr(self):
|
|
|
|
return self._net_info.hwaddr
|
|
|
|
|
|
|
|
@property
|
|
|
|
def vendor(self):
|
|
|
|
return self._net_info.vendor
|
|
|
|
|
|
|
|
@property
|
|
|
|
def model(self):
|
2017-11-15 01:27:24 +00:00
|
|
|
return self._net_info.model
|
2016-11-07 00:35:42 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def is_connected(self):
|
|
|
|
return self._net_info.is_connected
|
|
|
|
|
|
|
|
@property
|
|
|
|
def is_bond_slave(self):
|
|
|
|
return self._net_info.bond['is_slave']
|
2015-11-06 15:19:44 +00:00
|
|
|
|
|
|
|
@property
|
2016-11-07 00:35:42 +00:00
|
|
|
def is_bond_master(self):
|
|
|
|
return self._net_info.bond['is_master']
|
|
|
|
|
|
|
|
@property
|
|
|
|
def is_bonded(self):
|
|
|
|
return self.is_bond_master or self.is_bond_slave
|
|
|
|
|
|
|
|
@property
|
|
|
|
def speed(self):
|
|
|
|
'''string'ify and bucketize iface speed:
|
|
|
|
1M, 10M, 1G, 10G, 40G, 100G
|
2015-11-06 15:19:44 +00:00
|
|
|
'''
|
2016-11-07 02:03:27 +00:00
|
|
|
hwattr = self._net_info.udev_data['attrs']
|
2016-11-07 00:35:42 +00:00
|
|
|
speed = hwattr.get('speed', 0)
|
|
|
|
if not speed:
|
|
|
|
return None
|
|
|
|
|
|
|
|
speed = int(speed)
|
|
|
|
if speed < 1000:
|
|
|
|
return "{}M".format(speed)
|
|
|
|
|
|
|
|
return "{}G".format(int(speed / 1000))
|
2015-11-06 15:19:44 +00:00
|
|
|
|
2016-12-12 02:54:33 +00:00
|
|
|
def dhcp_for_version(self, version):
|
2018-05-21 20:12:29 +00:00
|
|
|
dhcp_key = 'dhcp%s' % version
|
2016-12-12 02:54:33 +00:00
|
|
|
return self._configuration.get(dhcp_key, False)
|
|
|
|
|
2018-06-27 01:31:11 +00:00
|
|
|
def set_dhcp_for_version(self, version, val):
|
|
|
|
dhcp_key = 'dhcp%s' % version
|
|
|
|
self._configuration[dhcp_key] = val
|
|
|
|
|
2015-11-06 15:19:44 +00:00
|
|
|
@property
|
2016-11-07 00:35:42 +00:00
|
|
|
def dhcp4(self):
|
|
|
|
return self._configuration.get('dhcp4', False)
|
|
|
|
|
|
|
|
@dhcp4.setter
|
|
|
|
def dhcp4(self, val):
|
|
|
|
if val:
|
|
|
|
self._configuration['dhcp4'] = val
|
|
|
|
else:
|
|
|
|
self._configuration.pop('dhcp4', None)
|
2015-11-06 15:19:44 +00:00
|
|
|
|
|
|
|
@property
|
2016-11-07 00:35:42 +00:00
|
|
|
def dhcp6(self):
|
|
|
|
return self._configuration.get('dhcp6', False)
|
|
|
|
|
|
|
|
@dhcp6.setter
|
|
|
|
def dhcp6(self, val):
|
|
|
|
if val:
|
|
|
|
self._configuration['dhcp6'] = val
|
|
|
|
else:
|
|
|
|
self._configuration.pop('dhcp6', None)
|
2015-11-06 15:19:44 +00:00
|
|
|
|
2016-12-12 02:34:59 +00:00
|
|
|
def actual_ip_addresses_for_version(self, version):
|
|
|
|
if version == 4:
|
|
|
|
fam = AF_INET
|
|
|
|
elif version == 6:
|
|
|
|
fam = AF_INET6
|
2018-05-21 20:12:29 +00:00
|
|
|
return [addr.ip for _, addr in sorted(self._net_info.addresses.items())
|
|
|
|
if addr.family == fam]
|
2016-12-12 02:34:59 +00:00
|
|
|
|
2016-08-12 21:55:03 +00:00
|
|
|
@property
|
2016-11-07 00:35:42 +00:00
|
|
|
def actual_ip_addresses(self):
|
2018-05-21 20:12:29 +00:00
|
|
|
return (self.actual_ip_addresses_for_version(4) +
|
|
|
|
self.actual_ip_addresses_for_version(6))
|
2016-08-12 21:55:03 +00:00
|
|
|
|
2016-12-12 02:34:59 +00:00
|
|
|
def configured_ip_addresses_for_version(self, version):
|
2016-11-07 00:35:42 +00:00
|
|
|
r = []
|
|
|
|
for ip in self._configuration.get('addresses', []):
|
2016-12-12 02:34:59 +00:00
|
|
|
if ip_version(ip) == version:
|
2016-11-07 00:35:42 +00:00
|
|
|
r.append(ip)
|
|
|
|
return r
|
2016-08-12 21:55:03 +00:00
|
|
|
|
2017-02-16 23:38:14 +00:00
|
|
|
@property
|
|
|
|
def actual_global_ip_addresses(self):
|
2018-05-21 20:12:29 +00:00
|
|
|
return [addr.ip for _, addr in sorted(self._net_info.addresses.items())
|
|
|
|
if addr.scope == "global"]
|
2017-02-16 23:38:14 +00:00
|
|
|
|
2016-11-07 00:35:42 +00:00
|
|
|
@property
|
|
|
|
def configured_ip_addresses(self):
|
|
|
|
return self._configuration.setdefault('addresses', [])
|
|
|
|
|
2016-12-12 02:34:59 +00:00
|
|
|
def configured_gateway_for_version(self, version):
|
2018-05-21 20:12:29 +00:00
|
|
|
return self._configuration.get('gateway%s' % version, None)
|
2016-12-12 02:34:59 +00:00
|
|
|
|
|
|
|
def set_configured_gateway_for_version(self, version, gateway):
|
2018-05-21 20:12:29 +00:00
|
|
|
key = 'gateway%s' % version
|
2016-12-12 02:34:59 +00:00
|
|
|
if gateway is None:
|
|
|
|
self._configuration.pop(key, None)
|
|
|
|
else:
|
|
|
|
self._configuration[key] = gateway
|
|
|
|
|
2016-11-07 00:35:42 +00:00
|
|
|
@property
|
|
|
|
def configured_nameservers(self):
|
|
|
|
ns = self._configuration.setdefault('nameservers', {})
|
|
|
|
return ns.setdefault('addresses', [])
|
|
|
|
|
|
|
|
@property
|
|
|
|
def configured_searchdomains(self):
|
|
|
|
ns = self._configuration.setdefault('nameservers', {})
|
|
|
|
return ns.setdefault('search', [])
|
|
|
|
|
|
|
|
@property
|
|
|
|
def actual_ssid(self):
|
2016-11-07 02:03:27 +00:00
|
|
|
if self._net_info.ssid:
|
2017-11-15 01:27:24 +00:00
|
|
|
return self._net_info.ssid
|
2016-11-07 00:35:42 +00:00
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
2016-11-07 03:25:18 +00:00
|
|
|
@property
|
|
|
|
def actual_ssids(self):
|
2017-11-15 01:27:24 +00:00
|
|
|
return self._net_info.wlan['visible_ssids']
|
2016-11-07 03:25:18 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def scan_state(self):
|
2017-11-15 01:27:24 +00:00
|
|
|
return self._net_info.wlan['scan_state']
|
2016-11-07 03:25:18 +00:00
|
|
|
|
2016-11-07 00:35:42 +00:00
|
|
|
@property
|
|
|
|
def configured_ssid(self):
|
|
|
|
aps = list(self._configuration.get('access-points', {}).keys())
|
|
|
|
if len(aps) > 0:
|
|
|
|
return aps[0]
|
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
|
|
|
@property
|
|
|
|
def configured_wifi_psk(self):
|
|
|
|
aps = list(self._configuration.get('access-points', {}).keys())
|
|
|
|
if len(aps) > 0:
|
|
|
|
ap = self._configuration.get('access-points', {})[aps[0]]
|
|
|
|
return ap.get('password')
|
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
|
|
|
def set_ssid_psk(self, ssid, psk):
|
|
|
|
aps = self._configuration.setdefault('access-points', {})
|
|
|
|
aps.clear()
|
|
|
|
if ssid is not None:
|
|
|
|
aps[ssid] = {}
|
|
|
|
if psk is not None:
|
|
|
|
aps[ssid]['password'] = psk
|
2016-09-16 02:28:40 +00:00
|
|
|
|
2016-08-15 21:05:19 +00:00
|
|
|
def remove_networks(self):
|
2016-12-12 02:58:41 +00:00
|
|
|
self.remove_ip_networks_for_version(4)
|
|
|
|
self.remove_ip_networks_for_version(6)
|
2016-08-16 04:48:15 +00:00
|
|
|
|
2016-12-12 02:34:59 +00:00
|
|
|
def remove_ip_networks_for_version(self, version):
|
2018-05-21 20:12:29 +00:00
|
|
|
dhcp_key = 'dhcp%s' % version
|
2016-12-12 02:34:59 +00:00
|
|
|
setattr(self, dhcp_key, False)
|
2016-11-07 00:35:42 +00:00
|
|
|
addrs = []
|
|
|
|
for ip in self._configuration.get('addresses', []):
|
2016-12-12 02:34:59 +00:00
|
|
|
if ip_version(ip) != version:
|
2016-11-07 00:35:42 +00:00
|
|
|
addrs.append(ip)
|
|
|
|
self._configuration['addresses'] = addrs
|
2016-12-12 02:34:59 +00:00
|
|
|
self.set_configured_gateway_for_version(version, None)
|
|
|
|
|
2016-11-07 00:35:42 +00:00
|
|
|
def remove_nameservers(self):
|
|
|
|
self._configuration['nameservers'] = {}
|
2016-08-15 21:05:19 +00:00
|
|
|
|
2016-12-12 02:34:59 +00:00
|
|
|
def add_network(self, version, network):
|
2016-08-15 21:05:19 +00:00
|
|
|
# result = {
|
|
|
|
# 'network': self.subnet_input.value,
|
|
|
|
# 'address': self.address_input.value,
|
|
|
|
# 'gateway': self.gateway_input.value,
|
2016-11-07 00:35:42 +00:00
|
|
|
# 'nameserver': [nameservers],
|
|
|
|
# 'searchdomains': [searchdomains],
|
2016-08-15 21:05:19 +00:00
|
|
|
# }
|
|
|
|
address = network['address'].split('/')[0]
|
|
|
|
address += '/' + network['network'].split('/')[1]
|
2016-11-07 00:35:42 +00:00
|
|
|
self.configured_ip_addresses.append(address)
|
2016-12-12 02:34:59 +00:00
|
|
|
self.set_configured_gateway_for_version(version, network['gateway'])
|
2016-11-07 00:35:42 +00:00
|
|
|
self.configured_nameservers.extend(network['nameservers'])
|
|
|
|
self.configured_searchdomains.extend(network['searchdomains'])
|
2016-08-15 21:05:19 +00:00
|
|
|
|
2015-11-06 15:19:44 +00:00
|
|
|
|
|
|
|
def valid_ipv4_address(addr):
|
|
|
|
try:
|
|
|
|
ip = ipaddress.IPv4Address(addr)
|
|
|
|
except ipaddress.AddressValueError:
|
|
|
|
return False
|
|
|
|
|
|
|
|
return ip
|
|
|
|
|
|
|
|
|
2018-02-26 09:14:43 +00:00
|
|
|
def _sanitize_inteface_config(iface_config):
|
|
|
|
for ap, ap_config in iface_config.get('access-points', {}).items():
|
|
|
|
if 'password' in ap_config:
|
|
|
|
ap_config['password'] = '<REDACTED>'
|
|
|
|
|
2018-05-21 20:12:29 +00:00
|
|
|
|
2018-02-26 09:14:43 +00:00
|
|
|
def sanitize_interface_config(iface_config):
|
|
|
|
iface_config = copy.deepcopy(iface_config)
|
|
|
|
_sanitize_inteface_config(iface_config)
|
|
|
|
return iface_config
|
|
|
|
|
2018-05-21 20:12:29 +00:00
|
|
|
|
2018-02-26 09:14:43 +00:00
|
|
|
def sanitize_config(config):
|
|
|
|
"""Return a copy of config with passwords redacted."""
|
|
|
|
config = copy.deepcopy(config)
|
2018-05-21 20:12:29 +00:00
|
|
|
interfaces = config.get('network', {}).get('wifis', {}).items()
|
|
|
|
for iface, iface_config in interfaces:
|
2018-02-26 09:14:43 +00:00
|
|
|
_sanitize_inteface_config(iface_config)
|
|
|
|
return config
|
|
|
|
|
2018-05-21 20:12:29 +00:00
|
|
|
|
2017-01-13 01:55:20 +00:00
|
|
|
class NetworkModel(object):
|
2015-07-21 15:55:02 +00:00
|
|
|
""" Model representing network interfaces
|
|
|
|
"""
|
2018-05-22 00:36:57 +00:00
|
|
|
additional_options = []
|
2015-07-21 15:55:02 +00:00
|
|
|
|
2015-11-09 20:35:54 +00:00
|
|
|
# TODO: what is "linear" level?
|
|
|
|
bonding_modes = {
|
|
|
|
0: 'balance-rr',
|
|
|
|
1: 'active-backup',
|
|
|
|
2: 'balance-xor',
|
|
|
|
3: 'broadcast',
|
|
|
|
4: '802.3ad',
|
|
|
|
5: 'balance-tlb',
|
|
|
|
6: 'balance-alb',
|
|
|
|
}
|
|
|
|
|
2018-03-20 20:35:21 +00:00
|
|
|
def __init__(self, support_wlan=True):
|
2018-06-27 01:36:36 +00:00
|
|
|
self.support_wlan = True#support_wlan
|
2018-05-21 20:12:29 +00:00
|
|
|
self.devices = {} # Maps ifindex to Networkdev
|
|
|
|
self.devices_by_name = {} # Maps interface names to Networkdev
|
2016-08-16 16:59:19 +00:00
|
|
|
self.default_v4_gateway = None
|
|
|
|
self.default_v6_gateway = None
|
|
|
|
self.v4_gateway_dev = None
|
|
|
|
self.v6_gateway_dev = None
|
2016-08-18 23:34:46 +00:00
|
|
|
self.network_routes = {}
|
2015-07-21 15:55:02 +00:00
|
|
|
|
2017-11-14 21:30:09 +00:00
|
|
|
def parse_netplan_configs(self, netplan_root):
|
2018-05-24 21:06:09 +00:00
|
|
|
config = netplan.Config()
|
|
|
|
config.load_from_root(netplan_root)
|
|
|
|
self.config = config
|
2016-11-07 00:35:42 +00:00
|
|
|
|
2015-07-22 01:34:46 +00:00
|
|
|
def get_menu(self):
|
|
|
|
return self.additional_options
|
|
|
|
|
2016-11-07 02:03:27 +00:00
|
|
|
def new_link(self, ifindex, link):
|
|
|
|
if link.type in NETDEV_IGNORED_IFACE_TYPES:
|
|
|
|
return
|
2018-03-20 20:35:21 +00:00
|
|
|
if not self.support_wlan and link.type == "wlan":
|
|
|
|
return
|
2016-11-07 02:03:27 +00:00
|
|
|
if link.name in NETDEV_IGNORED_IFACE_NAMES:
|
|
|
|
return
|
|
|
|
if link.is_virtual:
|
|
|
|
return
|
|
|
|
config = self.config.config_for_device(link)
|
2018-05-21 20:12:29 +00:00
|
|
|
log.debug("new_link %s %s with config %s",
|
|
|
|
ifindex, link.name, sanitize_interface_config(config))
|
2016-11-07 02:03:27 +00:00
|
|
|
self.devices[ifindex] = Networkdev(link, config)
|
|
|
|
self.devices_by_name[link.name] = Networkdev(link, config)
|
|
|
|
|
|
|
|
def update_link(self, ifindex):
|
|
|
|
# This is pretty edge-casey as the fact that we wait for the
|
|
|
|
# udev queue to settle should mean we never see an interface
|
|
|
|
# be renamed. But just in case...
|
|
|
|
if ifindex not in self.devices:
|
|
|
|
return
|
|
|
|
dev = self.devices[ifindex]
|
|
|
|
for k, v in self.devices_by_name.items():
|
|
|
|
if v.ifindex == ifindex and k != dev.name:
|
|
|
|
log.debug("link renamed %s -> %s", k, dev.name)
|
|
|
|
del self.devices_by_name[k]
|
|
|
|
self.devices_by_name[dev.name] = dev
|
|
|
|
return
|
|
|
|
|
|
|
|
def del_link(self, ifindex):
|
|
|
|
if ifindex in self.devices:
|
|
|
|
dev = self.devices[ifindex]
|
|
|
|
del self.devices_by_name[dev.name]
|
|
|
|
del self.devices[ifindex]
|
2015-11-06 15:19:44 +00:00
|
|
|
|
2016-11-07 00:35:42 +00:00
|
|
|
def get_all_netdevs(self):
|
|
|
|
return [v for k, v in sorted(self.devices_by_name.items())]
|
2015-11-06 15:19:44 +00:00
|
|
|
|
2016-12-20 20:42:37 +00:00
|
|
|
def get_configured_interfaces(self):
|
|
|
|
return [dev for dev in self.get_all_netdevs() if dev.configured]
|
|
|
|
|
2016-11-07 00:35:42 +00:00
|
|
|
def get_netdev_by_name(self, name):
|
|
|
|
return self.devices_by_name[name]
|
2015-11-04 15:50:58 +00:00
|
|
|
|
2015-11-06 15:19:44 +00:00
|
|
|
def add_bond(self, ifname, interfaces, params=[], subnets=[]):
|
2016-11-07 00:35:42 +00:00
|
|
|
# This needs rewriting!
|
2015-11-09 20:35:54 +00:00
|
|
|
''' create a bond action and info dict from parameters '''
|
2016-08-16 04:48:15 +00:00
|
|
|
for iface in interfaces:
|
|
|
|
self.devices[iface].remove_networks()
|
|
|
|
self.devices[iface].dhcp4 = False
|
|
|
|
self.devices[iface].dhcp6 = False
|
|
|
|
self.devices[iface].switchport = True
|
|
|
|
|
2015-11-09 20:35:54 +00:00
|
|
|
info = {
|
|
|
|
"bond": {
|
|
|
|
"is_master": True,
|
|
|
|
"is_slave": False,
|
|
|
|
"mode": params['bond-mode'],
|
|
|
|
"slaves": interfaces,
|
|
|
|
},
|
|
|
|
"bridge": {
|
|
|
|
"interfaces": [],
|
|
|
|
"is_bridge": False,
|
|
|
|
"is_port": False,
|
|
|
|
"options": {}
|
|
|
|
},
|
|
|
|
"hardware": {
|
|
|
|
"INTERFACE": ifname,
|
|
|
|
'ID_MODEL_FROM_DATABASE': " + ".join(interfaces),
|
|
|
|
'attrs': {
|
|
|
|
'address': "00:00:00:00:00:00",
|
|
|
|
'speed': None,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"ip": {
|
|
|
|
"addr": "0.0.0.0",
|
|
|
|
"netmask": "0.0.0.0",
|
|
|
|
"source": None
|
|
|
|
},
|
|
|
|
"type": "bond"
|
2015-11-06 15:19:44 +00:00
|
|
|
}
|
2018-05-21 19:31:08 +00:00
|
|
|
bondinfo = info
|
2015-11-09 20:35:54 +00:00
|
|
|
bonddev = Networkdev(ifname, 'bond')
|
2016-08-16 04:48:15 +00:00
|
|
|
bonddev.configure(probe_info=bondinfo)
|
2015-11-09 20:35:54 +00:00
|
|
|
|
|
|
|
# update slave interface info
|
|
|
|
for bondifname in interfaces:
|
|
|
|
bondif = self.get_interface(bondifname)
|
|
|
|
bondif.info.bond['is_slave'] = True
|
|
|
|
log.debug('Marking {} as bond slave'.format(bondifname))
|
|
|
|
|
|
|
|
log.debug("add_bond: {} as netdev({})".format(
|
|
|
|
ifname, bonddev))
|
|
|
|
|
|
|
|
self.devices[ifname] = bonddev
|
|
|
|
self.info[ifname] = bondinfo
|
2015-11-06 15:52:05 +00:00
|
|
|
|
2016-08-16 16:59:19 +00:00
|
|
|
def clear_gateways(self):
|
|
|
|
log.debug("clearing default gateway")
|
|
|
|
self.default_v4_gateway = None
|
|
|
|
self.default_v6_gateway = None
|
|
|
|
|
|
|
|
def set_default_v4_gateway(self, ifname, gateway_input):
|
|
|
|
if gateway_input is None:
|
|
|
|
self.default_v4_gateway = None
|
|
|
|
self.v4_gateway_dev = None
|
|
|
|
return
|
2016-08-15 21:05:19 +00:00
|
|
|
|
2015-11-06 15:52:05 +00:00
|
|
|
addr = valid_ipv4_address(gateway_input)
|
|
|
|
if addr is False:
|
|
|
|
raise ValueError(('Invalid gateway IP ') + gateway_input)
|
|
|
|
|
2016-08-16 16:59:19 +00:00
|
|
|
self.default_v4_gateway = addr.compressed
|
|
|
|
self.v4_gateway_dev = ifname
|
|
|
|
|
|
|
|
def set_default_v6_gateway(self, ifname, gateway_input):
|
|
|
|
if gateway_input is None:
|
|
|
|
self.default_v6_gateway = None
|
|
|
|
self.v6_gateway_dev = None
|
|
|
|
return
|
|
|
|
|
|
|
|
# FIXME: validate v6 address.
|
|
|
|
self.default_v6_gateway = gateway_input
|
|
|
|
self.v6_gateway_dev = ifname
|
2016-08-13 04:13:22 +00:00
|
|
|
|
|
|
|
def render(self):
|
2016-11-07 00:35:42 +00:00
|
|
|
config = {
|
|
|
|
'network': {
|
|
|
|
'version': 2,
|
|
|
|
},
|
|
|
|
}
|
2016-08-13 04:13:22 +00:00
|
|
|
ethernets = {}
|
2016-08-16 04:48:15 +00:00
|
|
|
bonds = {}
|
2016-09-05 00:03:06 +00:00
|
|
|
wifis = {}
|
2016-11-07 00:35:42 +00:00
|
|
|
for dev in self.devices.values():
|
|
|
|
if dev.type == 'eth':
|
|
|
|
ethernets.update(dev.render())
|
|
|
|
if dev.type == 'bond':
|
|
|
|
bonds.update(dev.render())
|
|
|
|
if dev.type == 'wlan':
|
|
|
|
wifis.update(dev.render())
|
2016-08-16 16:59:52 +00:00
|
|
|
if any(ethernets):
|
|
|
|
config['network']['ethernets'] = ethernets
|
|
|
|
if any(bonds):
|
|
|
|
config['network']['bonds'] = bonds
|
2016-09-05 00:03:06 +00:00
|
|
|
if any(wifis):
|
|
|
|
config['network']['wifis'] = wifis
|
2016-08-16 16:59:52 +00:00
|
|
|
|
|
|
|
nw_routes = []
|
2016-11-07 00:35:42 +00:00
|
|
|
if self.default_v4_gateway:
|
2018-05-21 20:12:29 +00:00
|
|
|
nw_routes.append(
|
|
|
|
{'to': '0.0.0.0/0', 'via': self.default_v4_gateway})
|
2016-11-07 00:35:42 +00:00
|
|
|
if self.default_v6_gateway is not None:
|
2018-05-21 20:12:29 +00:00
|
|
|
nw_routes.append({'to': '::/0', 'via': self.default_v6_gateway})
|
2016-08-16 16:59:52 +00:00
|
|
|
if len(nw_routes) > 0:
|
|
|
|
config['network']['routes'] = nw_routes
|
2016-08-16 05:09:28 +00:00
|
|
|
|
2016-08-13 04:13:22 +00:00
|
|
|
return config
|