1
0
mirror of https://github.com/CumulusNetworks/ifupdown2.git synced 2024-05-06 15:54:50 +00:00
Julien Fortin 96dddc0eaf addons: dhcp: release dhclient4/6 if it's still running but inet/inet6 is not configured
Ticket: CM-13817
Reviewed By: Roopa, Kanna, Nikhil G
Testing Done:

1) use inet and inet6 dhcp in interfaces file
2) do a ifup -v
3) make sure dhclient v4 and v6 is running
4) now remove inet6 dhcp section
5) ifreload -a -v (should kill dhclient6)
6) replace inet by inet6
7) ifreload -a -v (should kill dhclient4 and exec dhclient6)

etc.. I played with all possible combinations

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
2016-12-02 07:15:09 +01:00

218 lines
8.9 KiB
Python

#!/usr/bin/python
#
# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
# Author: Roopa Prabhu, roopa@cumulusnetworks.com
#
try:
import re
from ipaddr import IPNetwork
from sets import Set
from ifupdown.iface import *
import ifupdown.policymanager as policymanager
from ifupdownaddons.modulebase import moduleBase
from ifupdownaddons.dhclient import dhclient
from ifupdownaddons.iproute2 import iproute2
import ifupdown.ifupdownflags as ifupdownflags
from ifupdown.utils import utils
import time
from ifupdown.netlink import netlink
except ImportError, e:
raise ImportError (str(e) + "- required module not found")
class dhcp(moduleBase):
""" ifupdown2 addon module to configure dhcp on interface """
def __init__(self, *args, **kargs):
moduleBase.__init__(self, *args, **kargs)
self.dhclientcmd = dhclient(**kargs)
self.ipcmd = None
def syntax_check(self, ifaceobj, ifaceobj_getfunc):
return self.is_dhcp_allowed_on(ifaceobj, syntax_check=True)
def is_dhcp_allowed_on(self, ifaceobj, syntax_check):
if ifaceobj.addr_method and 'dhcp' in ifaceobj.addr_method:
return utils.is_addr_ip_allowed_on(ifaceobj, syntax_check=True)
return True
def _up(self, ifaceobj):
# if dhclient is already running do not stop and start it
dhclient4_running = self.dhclientcmd.is_running(ifaceobj.name)
dhclient6_running = self.dhclientcmd.is_running6(ifaceobj.name)
# today if we have an interface with both inet and inet6, if we
# remove the inet or inet6 or both then execute ifreload, we need
# to release/kill the appropriate dhclient(4/6) if they are running
self._down_stale_dhcp_config(ifaceobj, 'inet', dhclient4_running)
self._down_stale_dhcp_config(ifaceobj, 'inet6', dhclient6_running)
try:
dhclient_cmd_prefix = None
dhcp_wait = policymanager.policymanager_api.get_attr_default(
module_name=self.__class__.__name__, attr='dhcp-wait')
wait = not str(dhcp_wait).lower() == "no"
vrf = ifaceobj.get_attr_value_first('vrf')
if (vrf and self.vrf_exec_cmd_prefix and
self.ipcmd.link_exists(vrf)):
dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix, vrf)
if 'inet' in ifaceobj.addr_family:
if dhclient4_running:
self.logger.info('dhclient4 already running on %s. '
'Not restarting.' % ifaceobj.name)
else:
# First release any existing dhclient processes
try:
if not ifupdownflags.flags.PERFMODE:
self.dhclientcmd.stop(ifaceobj.name)
except:
pass
self.dhclientcmd.start(ifaceobj.name, wait=wait,
cmd_prefix=dhclient_cmd_prefix)
if 'inet6' in ifaceobj.addr_family:
if dhclient6_running:
self.logger.info('dhclient6 already running on %s. '
'Not restarting.' % ifaceobj.name)
else:
accept_ra = ifaceobj.get_attr_value_first('accept_ra')
if accept_ra:
# XXX: Validate value
self.sysctl_set('net.ipv6.conf.%s' %ifaceobj.name +
'.accept_ra', accept_ra)
autoconf = ifaceobj.get_attr_value_first('autoconf')
if autoconf:
# XXX: Validate value
self.sysctl_set('net.ipv6.conf.%s' %ifaceobj.name +
'.autoconf', autoconf)
try:
self.dhclientcmd.stop6(ifaceobj.name)
except:
pass
#add delay before starting IPv6 dhclient to
#make sure the configured interface/link is up.
time.sleep(2)
timeout = 10
while timeout:
timeout -= 2
addr_output = utils.exec_command('ip -6 addr show %s'
% ifaceobj.name)
r = re.search('inet6 .* scope link', addr_output)
if r:
self.dhclientcmd.start6(ifaceobj.name,
wait=wait,
cmd_prefix=dhclient_cmd_prefix)
return
time.sleep(2)
except Exception, e:
self.log_error(str(e), ifaceobj)
def _down_stale_dhcp_config(self, ifaceobj, family, dhclientX_running):
addr_family = ifaceobj.addr_family
try:
if not family in ifaceobj.addr_family and dhclientX_running:
ifaceobj.addr_family = [family]
self._dhcp_down(ifaceobj)
except:
pass
finally:
ifaceobj.addr_family = addr_family
def _dhcp_down(self, ifaceobj):
dhclient_cmd_prefix = None
vrf = ifaceobj.get_attr_value_first('vrf')
if (vrf and self.vrf_exec_cmd_prefix and
self.ipcmd.link_exists(vrf)):
dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix, vrf)
if 'inet6' in ifaceobj.addr_family:
self.dhclientcmd.release6(ifaceobj.name, dhclient_cmd_prefix)
if 'inet' in ifaceobj.addr_family:
self.dhclientcmd.release(ifaceobj.name, dhclient_cmd_prefix)
def _down(self, ifaceobj):
self._dhcp_down(ifaceobj)
self.ipcmd.link_down(ifaceobj.name)
def _query_check(self, ifaceobj, ifaceobjcurr):
status = ifaceStatus.SUCCESS
dhcp_running = False
dhcp_v4 = self.dhclientcmd.is_running(ifaceobjcurr.name)
dhcp_v6 = self.dhclientcmd.is_running6(ifaceobjcurr.name)
if dhcp_v4:
dhcp_running = True
if 'inet' not in ifaceobj.addr_family and not dhcp_v6:
status = ifaceStatus.ERROR
ifaceobjcurr.addr_method = 'dhcp'
if dhcp_v6:
dhcp_running = True
if 'inet6' not in ifaceobj.addr_family and not dhcp_v4:
status = ifaceStatus.ERROR
ifaceobjcurr.addr_method = 'dhcp'
ifaceobjcurr.addr_family = ifaceobj.addr_family
if not dhcp_running:
ifaceobjcurr.addr_family = []
status = ifaceStatus.ERROR
ifaceobjcurr.status = status
def _query_running(self, ifaceobjrunning):
if not self.ipcmd.link_exists(ifaceobjrunning.name):
return
if self.dhclientcmd.is_running(ifaceobjrunning.name):
ifaceobjrunning.addr_family.append('inet')
ifaceobjrunning.addr_method = 'dhcp'
if self.dhclientcmd.is_running6(ifaceobjrunning.name):
ifaceobjrunning.addr_family.append('inet6')
ifaceobjrunning.addr_method = 'dhcp6'
_run_ops = {'up' : _up,
'down' : _down,
'pre-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()
def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
""" run dhcp configuration on the interface object passed as argument
Args:
**ifaceobj** (object): iface object
**operation** (str): any of 'up', '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.
"""
op_handler = self._run_ops.get(operation)
if not op_handler:
return
try:
if (operation != 'query-running' and
(ifaceobj.addr_method != 'dhcp' and
ifaceobj.addr_method != 'dhcp6')):
return
except:
return
if not self.is_dhcp_allowed_on(ifaceobj, syntax_check=False):
return
self._init_command_handlers()
if operation == 'query-checkcurr':
op_handler(self, ifaceobj, query_ifaceobj)
else:
op_handler(self, ifaceobj)