summaryrefslogtreecommitdiff
path: root/src/conf_mode/interface-bonding.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/conf_mode/interface-bonding.py')
-rwxr-xr-xsrc/conf_mode/interface-bonding.py526
1 files changed, 0 insertions, 526 deletions
diff --git a/src/conf_mode/interface-bonding.py b/src/conf_mode/interface-bonding.py
deleted file mode 100755
index 8a0f9f84d..000000000
--- a/src/conf_mode/interface-bonding.py
+++ /dev/null
@@ -1,526 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2019 VyOS maintainers and contributors
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 or later as
-# published by the Free Software Foundation.
-#
-# 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-import os
-
-from copy import deepcopy
-from sys import exit
-from netifaces import interfaces
-
-from vyos.ifconfig import BondIf, VLANIf
-from vyos.configdict import list_diff, vlan_to_dict
-from vyos.config import Config
-from vyos import ConfigError
-
-default_config_data = {
- 'address': [],
- 'address_remove': [],
- 'arp_mon_intvl': 0,
- 'arp_mon_tgt': [],
- 'description': '',
- 'deleted': False,
- 'dhcp_client_id': '',
- 'dhcp_hostname': '',
- 'dhcp_vendor_class_id': '',
- 'dhcpv6_prm_only': False,
- 'dhcpv6_temporary': False,
- 'disable': False,
- 'disable_link_detect': 1,
- 'hash_policy': 'layer2',
- 'ip_arp_cache_tmo': 30,
- 'ip_proxy_arp': 0,
- 'ip_proxy_arp_pvlan': 0,
- 'intf': '',
- 'mac': '',
- 'mode': '802.3ad',
- 'member': [],
- 'mtu': 1500,
- 'primary': '',
- 'vif_s': [],
- 'vif_s_remove': [],
- 'vif': [],
- 'vif_remove': []
-}
-
-
-def get_bond_mode(mode):
- if mode == 'round-robin':
- return 'balance-rr'
- elif mode == 'active-backup':
- return 'active-backup'
- elif mode == 'xor-hash':
- return 'balance-xor'
- elif mode == 'broadcast':
- return 'broadcast'
- elif mode == '802.3ad':
- return '802.3ad'
- elif mode == 'transmit-load-balance':
- return 'balance-tlb'
- elif mode == 'adaptive-load-balance':
- return 'balance-alb'
- else:
- raise ConfigError('invalid bond mode "{}"'.format(mode))
-
-
-def apply_vlan_config(vlan, config):
- """
- Generic function to apply a VLAN configuration from a dictionary
- to a VLAN interface
- """
-
- if type(vlan) != type(VLANIf("lo")):
- raise TypeError()
-
- # get DHCP config dictionary and update values
- opt = vlan.get_dhcp_options()
-
- if config['dhcp_client_id']:
- opt['client_id'] = config['dhcp_client_id']
-
- if config['dhcp_hostname']:
- opt['hostname'] = config['dhcp_hostname']
-
- if config['dhcp_vendor_class_id']:
- opt['vendor_class_id'] = config['dhcp_vendor_class_id']
-
- # store DHCP config dictionary - used later on when addresses are aquired
- vlan.set_dhcp_options(opt)
-
- # get DHCPv6 config dictionary and update values
- opt = vlan.get_dhcpv6_options()
-
- if config['dhcpv6_prm_only']:
- opt['dhcpv6_prm_only'] = True
-
- if config['dhcpv6_temporary']:
- opt['dhcpv6_temporary'] = True
-
- # store DHCPv6 config dictionary - used later on when addresses are aquired
- vlan.set_dhcpv6_options(opt)
-
- # update interface description used e.g. within SNMP
- vlan.set_alias(config['description'])
- # ignore link state changes
- vlan.set_link_detect(config['disable_link_detect'])
- # Maximum Transmission Unit (MTU)
- vlan.set_mtu(config['mtu'])
- # Change VLAN interface MAC address
- if config['mac']:
- vlan.set_mac(config['mac'])
-
- # enable/disable VLAN interface
- if config['disable']:
- vlan.set_state('down')
- else:
- vlan.set_state('up')
-
- # Configure interface address(es)
- # - not longer required addresses get removed first
- # - newly addresses will be added second
- for addr in config['address_remove']:
- vlan.del_addr(addr)
- for addr in config['address']:
- vlan.add_addr(addr)
-
-
-def get_config():
- # initialize kernel module if not loaded
- if not os.path.isfile('/sys/class/net/bonding_masters'):
- import syslog
- syslog.syslog(syslog.LOG_NOTICE, "loading bonding kernel module")
- if os.system('modprobe bonding max_bonds=0 miimon=250') != 0:
- syslog.syslog(syslog.LOG_NOTICE, "failed loading bonding kernel module")
- raise ConfigError("failed loading bonding kernel module")
-
- bond = deepcopy(default_config_data)
- conf = Config()
-
- # determine tagNode instance
- try:
- bond['intf'] = os.environ['VYOS_TAGNODE_VALUE']
- except KeyError as E:
- print("Interface not specified")
-
- # check if bond has been removed
- cfg_base = 'interfaces bonding ' + bond['intf']
- if not conf.exists(cfg_base):
- bond['deleted'] = True
- return bond
-
- # set new configuration level
- conf.set_level(cfg_base)
-
- # retrieve configured interface addresses
- if conf.exists('address'):
- bond['address'] = conf.return_values('address')
-
- # get interface addresses (currently effective) - to determine which
- # address is no longer valid and needs to be removed
- eff_addr = conf.return_effective_values('address')
- bond['address_remove'] = list_diff(eff_addr, bond['address'])
-
- # ARP link monitoring frequency in milliseconds
- if conf.exists('arp-monitor interval'):
- bond['arp_mon_intvl'] = int(conf.return_value('arp-monitor interval'))
-
- # IP address to use for ARP monitoring
- if conf.exists('arp-monitor target'):
- bond['arp_mon_tgt'] = conf.return_values('arp-monitor target')
-
- # retrieve interface description
- if conf.exists('description'):
- bond['description'] = conf.return_value('description')
-
- # get DHCP client identifier
- if conf.exists('dhcp-options client-id'):
- bond['dhcp_client_id'] = conf.return_value('dhcp-options client-id')
-
- # DHCP client host name (overrides the system host name)
- if conf.exists('dhcp-options host-name'):
- bond['dhcp_hostname'] = conf.return_value('dhcp-options host-name')
-
- # DHCP client vendor identifier
- if conf.exists('dhcp-options vendor-class-id'):
- bond['dhcp_vendor_class_id'] = conf.return_value('dhcp-options vendor-class-id')
-
- # DHCPv6 only acquire config parameters, no address
- if conf.exists('dhcpv6-options parameters-only'):
- bond['dhcpv6_prm_only'] = True
-
- # DHCPv6 temporary IPv6 address
- if conf.exists('dhcpv6-options temporary'):
- bond['dhcpv6_temporary'] = True
-
- # ignore link state changes
- if conf.exists('disable-link-detect'):
- bond['disable_link_detect'] = 2
-
- # disable bond interface
- if conf.exists('disable'):
- bond['disable'] = True
-
- # Bonding transmit hash policy
- if conf.exists('hash-policy'):
- bond['hash_policy'] = conf.return_value('hash-policy')
-
- # ARP cache entry timeout in seconds
- if conf.exists('ip arp-cache-timeout'):
- bond['ip_arp_cache_tmo'] = int(conf.return_value('ip arp-cache-timeout'))
-
- # Enable proxy-arp on this interface
- if conf.exists('ip enable-proxy-arp'):
- bond['ip_proxy_arp'] = 1
-
- # Enable private VLAN proxy ARP on this interface
- if conf.exists('ip proxy-arp-pvlan'):
- bond['ip_proxy_arp_pvlan'] = 1
-
- # Media Access Control (MAC) address
- if conf.exists('mac'):
- bond['mac'] = conf.return_value('mac')
-
- # Bonding mode
- if conf.exists('mode'):
- bond['mode'] = get_bond_mode(conf.return_value('mode'))
-
- # Maximum Transmission Unit (MTU)
- if conf.exists('mtu'):
- bond['mtu'] = int(conf.return_value('mtu'))
-
- # determine bond member interfaces (currently configured)
- if conf.exists('member interface'):
- bond['member'] = conf.return_values('member interface')
-
- # Primary device interface
- if conf.exists('primary'):
- bond['primary'] = conf.return_value('primary')
-
- # re-set configuration level to parse new nodes
- conf.set_level(cfg_base)
- # get vif-s interfaces (currently effective) - to determine which vif-s
- # interface is no longer present and needs to be removed
- eff_intf = conf.list_effective_nodes('vif-s')
- act_intf = conf.list_nodes('vif-s')
- bond['vif_s_remove'] = list_diff(eff_intf, act_intf)
-
- if conf.exists('vif-s'):
- for vif_s in conf.list_nodes('vif-s'):
- # set config level to vif-s interface
- conf.set_level(cfg_base + ' vif-s ' + vif_s)
- bond['vif_s'].append(vlan_to_dict(conf))
-
- # re-set configuration level to parse new nodes
- conf.set_level(cfg_base)
- # Determine vif interfaces (currently effective) - to determine which
- # vif interface is no longer present and needs to be removed
- eff_intf = conf.list_effective_nodes('vif')
- act_intf = conf.list_nodes('vif')
- bond['vif_remove'] = list_diff(eff_intf, act_intf)
-
- if conf.exists('vif'):
- for vif in conf.list_nodes('vif'):
- # set config level to vif interface
- conf.set_level(cfg_base + ' vif ' + vif)
- bond['vif'].append(vlan_to_dict(conf))
-
- return bond
-
-
-def verify(bond):
- if len (bond['arp_mon_tgt']) > 16:
- raise ConfigError('The maximum number of targets that can be specified is 16')
-
- if bond['primary']:
- if bond['mode'] not in ['active-backup', 'balance-tlb', 'balance-alb']:
- raise ConfigError('Mode dependency failed, primary not supported ' \
- 'in this mode.'.format())
-
- if bond['primary'] not in bond['member']:
- raise ConfigError('Interface "{}" is not part of the bond' \
- .format(bond['primary']))
-
-
- # DHCPv6 parameters-only and temporary address are mutually exclusive
- for vif_s in bond['vif_s']:
- if vif_s['dhcpv6_prm_only'] and vif_s['dhcpv6_temporary']:
- raise ConfigError('DHCPv6 temporary and parameters-only options are mutually exclusive!')
-
- for vif_c in vif_s['vif_c']:
- if vif_c['dhcpv6_prm_only'] and vif_c['dhcpv6_temporary']:
- raise ConfigError('DHCPv6 temporary and parameters-only options are mutually exclusive!')
-
- for vif in bond['vif']:
- if vif['dhcpv6_prm_only'] and vif['dhcpv6_temporary']:
- raise ConfigError('DHCPv6 temporary and parameters-only options are mutually exclusive!')
-
-
- for vif_s in bond['vif_s']:
- for vif in bond['vif']:
- if vif['id'] == vif_s['id']:
- raise ConfigError('Can not use identical ID on vif and vif-s interface')
-
-
- conf = Config()
- for intf in bond['member']:
- # a bonding member interface is only allowed to be assigned to one bond!
- all_bonds = conf.list_nodes('interfaces bonding')
- # We do not need to check our own bond
- all_bonds.remove(bond['intf'])
- for tmp in all_bonds:
- if conf.exists('interfaces bonding ' + tmp + ' member interface ' + intf):
- raise ConfigError('can not enslave interface {} which already ' \
- 'belongs to {}'.format(intf, tmp))
-
- # can not add interfaces with an assigned address to a bond
- if conf.exists('interfaces ethernet ' + intf + ' address'):
- raise ConfigError('can not enslave interface {} which has an address ' \
- 'assigned'.format(intf))
-
- # bond members are not allowed to be bridge members, too
- for tmp in conf.list_nodes('interfaces bridge'):
- if conf.exists('interfaces bridge ' + tmp + ' member interface ' + intf):
- raise ConfigError('can not enslave interface {} which belongs to ' \
- 'bridge {}'.format(intf, tmp))
-
- # bond members are not allowed to be vrrp members, too
- for tmp in conf.list_nodes('high-availability vrrp group'):
- if conf.exists('high-availability vrrp group ' + tmp + ' interface ' + intf):
- raise ConfigError('can not enslave interface {} which belongs to ' \
- 'VRRP group {}'.format(intf, tmp))
-
- # bond members are not allowed to be underlaying psuedo-ethernet devices
- for tmp in conf.list_nodes('interfaces pseudo-ethernet'):
- if conf.exists('interfaces pseudo-ethernet ' + tmp + ' link ' + intf):
- raise ConfigError('can not enslave interface {} which belongs to ' \
- 'pseudo-ethernet {}'.format(intf, tmp))
-
- # bond members are not allowed to be underlaying vxlan devices
- for tmp in conf.list_nodes('interfaces vxlan'):
- if conf.exists('interfaces vxlan ' + tmp + ' link ' + intf):
- raise ConfigError('can not enslave interface {} which belongs to ' \
- 'vxlan {}'.format(intf, tmp))
-
-
- if bond['primary']:
- if bond['primary'] not in bond['member']:
- raise ConfigError('primary interface must be a member interface of {}' \
- .format(bond['intf']))
-
- if bond['mode'] not in ['active-backup', 'balance-tlb', 'balance-alb']:
- raise ConfigError('primary interface only works for mode active-backup, ' \
- 'transmit-load-balance or adaptive-load-balance')
-
- if bond['arp_mon_intvl'] > 0:
- if bond['mode'] in ['802.3ad', 'balance-tlb', 'balance-alb']:
- raise ConfigError('ARP link monitoring does not work for mode 802.3ad, ' \
- 'transmit-load-balance or adaptive-load-balance')
-
- return None
-
-
-def generate(bond):
- return None
-
-
-def apply(bond):
- b = BondIf(bond['intf'])
-
- if bond['deleted']:
- # delete interface
- b.remove()
- else:
- # Some parameters can not be changed when the bond is up.
- # Always disable the bond prior changing anything
- b.set_state('down')
-
- # The bonding mode can not be changed when there are interfaces enslaved
- # to this bond, thus we will free all interfaces from the bond first!
- for intf in b.get_slaves():
- b.del_port(intf)
-
- # ARP link monitoring frequency, reset miimon when arp-montior is inactive
- # this is done inside BondIf automatically
- b.set_arp_interval(bond['arp_mon_intvl'])
-
- # ARP monitor targets need to be synchronized between sysfs and CLI.
- # Unfortunately an address can't be send twice to sysfs as this will
- # result in the following exception: OSError: [Errno 22] Invalid argument.
- #
- # We remove ALL adresses prior adding new ones, this will remove addresses
- # added manually by the user too - but as we are limited to 16 adresses
- # from the kernel side this looks valid to me. We won't run into an error
- # when a user added manual adresses which would result in having more
- # then 16 adresses in total.
- arp_tgt_addr = list(map(str, b.get_arp_ip_target().split()))
- for addr in arp_tgt_addr:
- b.set_arp_ip_target('-' + addr)
-
- # Add configured ARP target addresses
- for addr in bond['arp_mon_tgt']:
- b.set_arp_ip_target('+' + addr)
-
- # update interface description used e.g. within SNMP
- b.set_alias(bond['description'])
-
- # get DHCP config dictionary and update values
- opt = b.get_dhcp_options()
-
- if bond['dhcp_client_id']:
- opt['client_id'] = bond['dhcp_client_id']
-
- if bond['dhcp_hostname']:
- opt['hostname'] = bond['dhcp_hostname']
-
- if bond['dhcp_vendor_class_id']:
- opt['vendor_class_id'] = bond['dhcp_vendor_class_id']
-
- # store DHCP config dictionary - used later on when addresses are aquired
- b.set_dhcp_options(opt)
-
- # get DHCPv6 config dictionary and update values
- opt = b.get_dhcpv6_options()
-
- if bond['dhcpv6_prm_only']:
- opt['dhcpv6_prm_only'] = True
-
- if bond['dhcpv6_temporary']:
- opt['dhcpv6_temporary'] = True
-
- # store DHCPv6 config dictionary - used later on when addresses are aquired
- b.set_dhcpv6_options(opt)
-
- # ignore link state changes
- b.set_link_detect(bond['disable_link_detect'])
- # Bonding transmit hash policy
- b.set_hash_policy(bond['hash_policy'])
- # configure ARP cache timeout in milliseconds
- b.set_arp_cache_tmo(bond['ip_arp_cache_tmo'])
- # Enable proxy-arp on this interface
- b.set_proxy_arp(bond['ip_proxy_arp'])
- # Enable private VLAN proxy ARP on this interface
- b.set_proxy_arp_pvlan(bond['ip_proxy_arp_pvlan'])
-
- # Change interface MAC address
- if bond['mac']:
- b.set_mac(bond['mac'])
-
- # Bonding policy
- b.set_mode(bond['mode'])
- # Maximum Transmission Unit (MTU)
- b.set_mtu(bond['mtu'])
-
- # Primary device interface
- if bond['primary']:
- b.set_primary(bond['primary'])
-
- # Add (enslave) interfaces to bond
- for intf in bond['member']:
- b.add_port(intf)
-
- # As the bond interface is always disabled first when changing
- # parameters we will only re-enable the interface if it is not
- # administratively disabled
- if not bond['disable']:
- b.set_state('up')
-
- # Configure interface address(es)
- # - not longer required addresses get removed first
- # - newly addresses will be added second
- for addr in bond['address_remove']:
- b.del_addr(addr)
- for addr in bond['address']:
- b.add_addr(addr)
-
- # remove no longer required service VLAN interfaces (vif-s)
- for vif_s in bond['vif_s_remove']:
- b.del_vlan(vif_s)
-
- # create service VLAN interfaces (vif-s)
- for vif_s in bond['vif_s']:
- s_vlan = b.add_vlan(vif_s['id'], ethertype=vif_s['ethertype'])
- apply_vlan_config(s_vlan, vif_s)
-
- # remove no longer required client VLAN interfaces (vif-c)
- # on lower service VLAN interface
- for vif_c in vif_s['vif_c_remove']:
- s_vlan.del_vlan(vif_c)
-
- # create client VLAN interfaces (vif-c)
- # on lower service VLAN interface
- for vif_c in vif_s['vif_c']:
- c_vlan = s_vlan.add_vlan(vif_c['id'])
- apply_vlan_config(c_vlan, vif_c)
-
- # remove no longer required VLAN interfaces (vif)
- for vif in bond['vif_remove']:
- b.del_vlan(vif)
-
- # create VLAN interfaces (vif)
- for vif in bond['vif']:
- vlan = b.add_vlan(vif['id'])
- apply_vlan_config(vlan, vif)
-
- return None
-
-if __name__ == '__main__':
- try:
- c = get_config()
- verify(c)
- generate(c)
- apply(c)
- except ConfigError as e:
- print(e)
- exit(1)