]> git.proxmox.com Git - mirror_ifupdown2.git/blobdiff - ifupdown2/addons/dhcp.py
Policy to wait for IPv6 link local address to be available
[mirror_ifupdown2.git] / ifupdown2 / addons / dhcp.py
index ed68466424bccc1cc9a20cacc542e161a47e583b..c3c125ca1d79749c3f187b8550ecacef5ba82570 100644 (file)
@@ -1,18 +1,33 @@
 #!/usr/bin/python
 #
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
 #
 
+import re
+import time
+
 try:
-    from ipaddr import IPNetwork
-    from sets import Set
+    import ifupdown2.ifupdown.policymanager as policymanager
+    import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
+
+    from ifupdown2.ifupdown.iface import *
+    from ifupdown2.ifupdown.utils import utils
+
+    from ifupdown2.ifupdownaddons.dhclient import dhclient
+    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdown2.ifupdownaddons.modulebase import moduleBase
+except ImportError:
+    import ifupdown.policymanager as policymanager
+    import ifupdown.ifupdownflags as ifupdownflags
+
     from ifupdown.iface import *
-    from ifupdownaddons.modulebase import moduleBase
+    from ifupdown.utils import utils
+
     from ifupdownaddons.dhclient import dhclient
-    from ifupdownaddons.iproute2 import iproute2
-except ImportError, e:
-    raise ImportError (str(e) + "- required module not found")
+    from ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdownaddons.modulebase import moduleBase
+
 
 class dhcp(moduleBase):
     """ ifupdown2 addon module to configure dhcp on interface """
@@ -22,68 +37,161 @@ class dhcp(moduleBase):
         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:
-            if ifaceobj.addr_family == 'inet':
-                # First release any existing dhclient processes
-                try:
-                    if not self.PERFMODE:
-                        self.dhclientcmd.stop(ifaceobj.name)
-                except:
-                    pass
-                self.dhclientcmd.start(ifaceobj.name)
-            elif ifaceobj.addr_family == 'inet6':
-                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)
+            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"
+            inet6_ll_wait = policymanager.policymanager_api.get_iface_default(module_name=self.__class__.__name__, \
+                ifname=ifaceobj.name, attr='inet6-ll-wait')
+            try:
+                ll_wait_time = int(inet6_ll_wait)
+            except:
+                ll_wait_time = 10
+                pass
+
+            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:
-                        self.dhclientcmd.stop6(ifaceobj.name)
+                        if not ifupdownflags.flags.PERFMODE:
+                            self.dhclientcmd.stop(ifaceobj.name)
                     except:
                         pass
-                self.dhclientcmd.start6(ifaceobj.name)
+                    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.
+                    if ll_wait_time:
+                       timeout = ll_wait_time
+                       time.sleep(1)
+                    else:
+                       timeout = ll_wait_time+1
+
+                    while timeout:
+                        addr_output = utils.exec_command('%s -6 addr show %s'
+                                                         %(utils.ip_cmd, 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
+                        timeout -= 1
+                        if timeout:
+                            time.sleep(1)
+
         except Exception, e:
-            self.log_error(str(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.dhclientcmd.release(ifaceobj.name)
+        self._dhcp_down(ifaceobj)
         self.ipcmd.link_down(ifaceobj.name)
 
     def _query_check(self, ifaceobj, ifaceobjcurr):
-        if self.dhclientcmd.is_running(ifaceobjcurr.name):
-            ifaceobjcurr.addr_family = 'inet'
-            if ifaceobj.addr_family != 'inet':
-                ifaceobjcurr.status = ifaceStatus.ERROR
+        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'
-            ifaceobjcurr.status = ifaceStatus.SUCCESS
-        elif self.dhclientcmd.is_running6(ifaceobjcurr.name):
-            ifaceobjcurr.addr_family = 'inet6'
-            if ifaceobj.addr_family != 'inet6':
-                ifaceobjcurr.status = ifaceStatus.ERROR
+        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.status = ifaceStatus.SUCCESS
-        else:
-            ifaceobjcurr.addr_family = None
-            ifaceobjcurr.status = ifaceStatus.ERROR
+        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 = 'inet'
+            ifaceobjrunning.addr_family.append('inet')
             ifaceobjrunning.addr_method = 'dhcp'
-        elif self.dhclientcmd.is_running6(ifaceobjrunning.name):
-            ifaceobjrunning.addr_family = 'inet6'
+        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 }
 
@@ -93,7 +201,7 @@ class dhcp(moduleBase):
 
     def _init_command_handlers(self):
         if not self.ipcmd:
-            self.ipcmd = iproute2(**self.get_flags())
+            self.ipcmd = LinkUtils()
 
     def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
         """ run dhcp configuration on the interface object passed as argument
@@ -117,11 +225,13 @@ class dhcp(moduleBase):
             return
         try:
             if (operation != 'query-running' and
-                   (ifaceobj.addr_method != 'dhcp' 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)