mirror of
https://github.com/CumulusNetworks/ifupdown2.git
synced 2024-05-06 15:54:50 +00:00
Ticket: CM-4178 Reviewed By: Testing Done: Tested vrr interface up and down Checking and deleting is expensive. so, ignore delete failures. Currently logs them at debug level.
286 lines
12 KiB
Python
286 lines
12 KiB
Python
#!/usr/bin/python
|
|
#
|
|
# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
|
|
# Author: Roopa Prabhu, roopa@cumulusnetworks.com
|
|
#
|
|
|
|
from ifupdown.iface import *
|
|
from ifupdownaddons.modulebase import moduleBase
|
|
from ifupdownaddons.iproute2 import iproute2
|
|
import ifupdown.rtnetlink_api as rtnetlink_api
|
|
import logging
|
|
import os
|
|
import glob
|
|
|
|
class addressvirtual(moduleBase):
|
|
""" ifupdown2 addon module to configure virtual addresses """
|
|
|
|
_modinfo = {'mhelp' : 'address module configures virtual addresses for ' +
|
|
'interfaces. It creates a macvlan interface for ' +
|
|
'every mac ip address-virtual line',
|
|
'attrs' : {
|
|
'address-virtual' :
|
|
{ 'help' : 'bridge router virtual mac and ip',
|
|
'example' : ['address-virtual 00:11:22:33:44:01 11.0.1.254/24 11.0.1.254/24']}
|
|
}}
|
|
|
|
|
|
def __init__(self, *args, **kargs):
|
|
moduleBase.__init__(self, *args, **kargs)
|
|
self.ipcmd = None
|
|
self._bridge_fdb_query_cache = {}
|
|
|
|
def _is_supported(self, ifaceobj):
|
|
if ifaceobj.get_attr_value_first('address-virtual'):
|
|
return True
|
|
return False
|
|
|
|
def _get_macvlan_prefix(self, ifaceobj):
|
|
return '%s-v' %ifaceobj.name[0:13].replace('.', '-')
|
|
|
|
def _add_addresses_to_bridge(self, ifaceobj, hwaddress):
|
|
# XXX: batch the addresses
|
|
if '.' in ifaceobj.name:
|
|
(bridgename, vlan) = ifaceobj.name.split('.')
|
|
if self.ipcmd.bridge_is_vlan_aware(bridgename):
|
|
[self.ipcmd.bridge_fdb_add(bridgename, addr,
|
|
vlan) for addr in hwaddress]
|
|
elif self.ipcmd.is_bridge(ifaceobj.name):
|
|
[self.ipcmd.bridge_fdb_add(ifaceobj.name, addr)
|
|
for addr in hwaddress]
|
|
|
|
def _remove_addresses_from_bridge(self, ifaceobj, hwaddress):
|
|
# XXX: batch the addresses
|
|
bridgename = None
|
|
if '.' in ifaceobj.name:
|
|
if self.ipcmd.bridge_is_vlan_aware(bridgename):
|
|
(bridgename, vlan) = ifaceobj.name.split('.')
|
|
elif self.ipcmd.is_bridge(ifaceobj.name):
|
|
vlan = None
|
|
bridgename = ifaceobj.name
|
|
if not bridgename:
|
|
return
|
|
for addr in hwaddress:
|
|
try:
|
|
self.ipcmd.bridge_fdb_del(bridgename, addr, vlan)
|
|
except Exception, e:
|
|
self.logger.debug("%s: %s" %(ifaceobj.name, str(e)))
|
|
pass
|
|
|
|
def _get_bridge_fdbs(self, bridgename, vlan):
|
|
fdbs = self._bridge_fdb_query_cache.get(bridgename)
|
|
if not fdbs:
|
|
fdbs = self.ipcmd.bridge_fdb_show_dev(bridgename)
|
|
if not fdbs:
|
|
return
|
|
self._bridge_fdb_query_cache[bridgename] = fdbs
|
|
return fdbs.get(vlan)
|
|
|
|
def _check_addresses_in_bridge(self, ifaceobj, hwaddress):
|
|
""" If the device is a bridge, make sure the addresses
|
|
are in the bridge """
|
|
if '.' in ifaceobj.name:
|
|
(bridgename, vlan) = ifaceobj.name.split('.')
|
|
if self.ipcmd.bridge_is_vlan_aware(bridgename):
|
|
fdb_addrs = self._get_bridge_fdbs(bridgename, vlan)
|
|
if not fdb_addrs or hwaddress not in fdb_addrs:
|
|
return False
|
|
return True
|
|
|
|
def _apply_address_config(self, ifaceobj, address_virtual_list):
|
|
purge_existing = False if self.PERFMODE else True
|
|
|
|
hwaddress = []
|
|
self.ipcmd.batch_start()
|
|
av_idx = 0
|
|
macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
|
|
for av in address_virtual_list:
|
|
av_attrs = av.split()
|
|
if len(av_attrs) < 2:
|
|
self.logger.warn("%s: incorrect address-virtual attrs '%s'"
|
|
%(ifaceobj.name, av))
|
|
av_idx += 1
|
|
continue
|
|
|
|
# Create a macvlan device on this device and set the virtual
|
|
# router mac and ip on it
|
|
macvlan_ifacename = '%s%d' %(macvlan_prefix, av_idx)
|
|
if not self.ipcmd.link_exists(macvlan_ifacename):
|
|
rtnetlink_api.rtnl_api.create_macvlan(macvlan_ifacename,
|
|
ifaceobj.name)
|
|
if av_attrs[0] != 'None':
|
|
self.ipcmd.link_set_hwaddress(macvlan_ifacename, av_attrs[0])
|
|
hwaddress.append(av_attrs[0])
|
|
self.ipcmd.addr_add_multiple(macvlan_ifacename, av_attrs[1:],
|
|
purge_existing)
|
|
av_idx += 1
|
|
self.ipcmd.batch_commit()
|
|
|
|
# if ifaceobj is a bridge and bridge is a vlan aware bridge
|
|
# add the vid to the bridge
|
|
self._add_addresses_to_bridge(ifaceobj, hwaddress)
|
|
|
|
def _remove_running_address_config(self, ifaceobj):
|
|
if not self.ipcmd.link_exists(ifaceobj.name):
|
|
return
|
|
hwaddress = []
|
|
self.ipcmd.batch_start()
|
|
macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
|
|
for macvlan_ifacename in glob.glob("/sys/class/net/%s-*" %macvlan_prefix):
|
|
macvlan_ifacename = os.path.basename(macvlan_ifacename)
|
|
if not self.ipcmd.link_exists(macvlan_ifacename):
|
|
continue
|
|
hwaddress.append(self.ipcmd.link_get_hwaddress(macvlan_ifacename))
|
|
self.ipcmd.link_delete(os.path.basename(macvlan_ifacename))
|
|
# XXX: Also delete any fdb addresses. This requires, checking mac address
|
|
# on individual macvlan interfaces and deleting the vlan from that.
|
|
self.ipcmd.batch_commit()
|
|
if any(hwaddress):
|
|
self._remove_addresses_from_bridge(ifaceobj, hwaddress)
|
|
|
|
def _remove_address_config(self, ifaceobj, address_virtual_list=None):
|
|
if not address_virtual_list:
|
|
self._remove_running_address_config(ifaceobj)
|
|
return
|
|
|
|
if not self.ipcmd.link_exists(ifaceobj.name):
|
|
return
|
|
hwaddress = []
|
|
self.ipcmd.batch_start()
|
|
av_idx = 0
|
|
macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
|
|
for av in address_virtual_list:
|
|
av_attrs = av.split()
|
|
if len(av_attrs) < 2:
|
|
self.logger.warn("%s: incorrect address-virtual attrs '%s'"
|
|
%(ifaceobj.name, av))
|
|
av_idx += 1
|
|
continue
|
|
|
|
# Delete the macvlan device on this device
|
|
macvlan_ifacename = '%s%d' %(macvlan_prefix, av_idx)
|
|
self.ipcmd.link_delete(os.path.basename(macvlan_ifacename))
|
|
if av_attrs[0] != 'None':
|
|
hwaddress.append(av_attrs[0])
|
|
av_idx += 1
|
|
self.ipcmd.batch_commit()
|
|
self._remove_addresses_from_bridge(ifaceobj, hwaddress)
|
|
|
|
def _up(self, ifaceobj):
|
|
address_virtual_list = ifaceobj.get_attr_value('address-virtual')
|
|
if not address_virtual_list:
|
|
# XXX: address virtual is not present. In which case,
|
|
# delete stale any macvlan devices.
|
|
self._remove_address_config(ifaceobj, address_virtual_list)
|
|
return
|
|
|
|
if not self.ipcmd.link_exists(ifaceobj.name):
|
|
#self.log_warn('%s: interface does not exist'
|
|
# %ifaceobj.name)
|
|
return
|
|
self._apply_address_config(ifaceobj, address_virtual_list)
|
|
|
|
def _down(self, ifaceobj):
|
|
try:
|
|
self._remove_address_config(ifaceobj,
|
|
ifaceobj.get_attr_value('address-virtual'))
|
|
except Exception, e:
|
|
self.log_warn(str(e))
|
|
|
|
def _query_check(self, ifaceobj, ifaceobjcurr):
|
|
address_virtual_list = ifaceobj.get_attr_value('address-virtual')
|
|
if not address_virtual_list:
|
|
return
|
|
if not self.ipcmd.link_exists(ifaceobj.name):
|
|
return
|
|
av_idx = 0
|
|
macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
|
|
for address_virtual in address_virtual_list:
|
|
av_attrs = address_virtual.split()
|
|
if len(av_attrs) < 2:
|
|
self.logger.warn("%s: incorrect address-virtual attrs '%s'"
|
|
%(ifaceobj.name, address_virtual))
|
|
av_idx += 1
|
|
continue
|
|
|
|
# Check if the macvlan device on this interface
|
|
macvlan_ifacename = '%s%d' %(macvlan_prefix, av_idx)
|
|
if not self.ipcmd.link_exists(macvlan_ifacename):
|
|
ifaceobjcurr.update_config_with_status('address-virtual',
|
|
'', 1)
|
|
av_idx += 1
|
|
continue
|
|
# Check mac and ip address
|
|
rhwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename)
|
|
raddrs = self.ipcmd.addr_get(macvlan_ifacename)
|
|
if not raddrs or not rhwaddress:
|
|
ifaceobjcurr.update_config_with_status('address-virtual', '', 1)
|
|
av_idx += 1
|
|
continue
|
|
raddrs = raddrs.keys()
|
|
if (rhwaddress == av_attrs[0] and raddrs == av_attrs[1:] and
|
|
self._check_addresses_in_bridge(ifaceobj, av_attrs[0])):
|
|
ifaceobjcurr.update_config_with_status('address-virtual',
|
|
address_virtual, 0)
|
|
else:
|
|
raddress_virtual = '%s %s' %(rhwaddress, ' '.join(raddrs))
|
|
ifaceobjcurr.update_config_with_status('address-virtual',
|
|
raddress_virtual, 1)
|
|
av_idx += 1
|
|
return
|
|
|
|
def _query_running(self, ifaceobjrunning):
|
|
macvlan_prefix = self._get_macvlan_prefix(ifaceobjrunning)
|
|
address_virtuals = glob.glob("/sys/class/net/%s*" %macvlan_prefix)
|
|
for av in address_virtuals:
|
|
macvlan_ifacename = os.path.basename(av)
|
|
rhwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename)
|
|
raddress = self.ipcmd.addr_get(macvlan_ifacename)
|
|
if not raddress:
|
|
self.logger.warn('%s: no running addresses'
|
|
%ifaceobjrunning.name)
|
|
raddress = []
|
|
ifaceobjrunning.update_config('address-virtual',
|
|
'%s %s' %(rhwaddress, ''.join(raddress)))
|
|
return
|
|
|
|
_run_ops = {'up' : _up,
|
|
'down' : _down,
|
|
'query-checkcurr' : _query_check,
|
|
'query-running' : _query_running}
|
|
|
|
def get_ops(self):
|
|
""" returns list of ops supported by this module """
|
|
return self._run_ops.keys()
|
|
|
|
def _init_command_handlers(self):
|
|
if not self.ipcmd:
|
|
self.ipcmd = iproute2(**self.get_flags())
|
|
|
|
def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
|
|
""" run vlan configuration on the interface object passed as argument
|
|
|
|
Args:
|
|
**ifaceobj** (object): iface object
|
|
|
|
**operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
|
|
'query-running'
|
|
Kwargs:
|
|
**query_ifaceobj** (object): query check ifaceobject. This is only
|
|
valid when op is 'query-checkcurr'. It is an object same as
|
|
ifaceobj, but contains running attribute values and its config
|
|
status. The modules can use it to return queried running state
|
|
of interfaces. status is success if the running state is same
|
|
as user required state in ifaceobj. error otherwise.
|
|
"""
|
|
if ifaceobj.type == ifaceType.BRIDGE_VLAN:
|
|
return
|
|
op_handler = self._run_ops.get(operation)
|
|
if not op_handler:
|
|
return
|
|
self._init_command_handlers()
|
|
if operation == 'query-checkcurr':
|
|
op_handler(self, ifaceobj, query_ifaceobj)
|
|
else:
|
|
op_handler(self, ifaceobj)
|