]> git.proxmox.com Git - mirror_ifupdown2.git/blame - ifupdown2/addons/dhcp.py
Policy to wait for IPv6 link local address to be available
[mirror_ifupdown2.git] / ifupdown2 / addons / dhcp.py
CommitLineData
15ef32ea
RP
1#!/usr/bin/python
2#
d486dd0d 3# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
15ef32ea
RP
4# Author: Roopa Prabhu, roopa@cumulusnetworks.com
5#
6
d486dd0d
JF
7import re
8import time
9
15ef32ea 10try:
d486dd0d
JF
11 import ifupdown2.ifupdown.policymanager as policymanager
12 import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
13
14 from ifupdown2.ifupdown.iface import *
15 from ifupdown2.ifupdown.utils import utils
16
17 from ifupdown2.ifupdownaddons.dhclient import dhclient
18 from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
19 from ifupdown2.ifupdownaddons.modulebase import moduleBase
20except ImportError:
6d359159 21 import ifupdown.policymanager as policymanager
fc5e1735 22 import ifupdown.ifupdownflags as ifupdownflags
d486dd0d
JF
23
24 from ifupdown.iface import *
20fd3a06 25 from ifupdown.utils import utils
d486dd0d
JF
26
27 from ifupdownaddons.dhclient import dhclient
28 from ifupdownaddons.LinkUtils import LinkUtils
29 from ifupdownaddons.modulebase import moduleBase
30
15ef32ea
RP
31
32class dhcp(moduleBase):
33 """ ifupdown2 addon module to configure dhcp on interface """
34
35 def __init__(self, *args, **kargs):
36 moduleBase.__init__(self, *args, **kargs)
37 self.dhclientcmd = dhclient(**kargs)
38 self.ipcmd = None
39
22b49c28
JF
40 def syntax_check(self, ifaceobj, ifaceobj_getfunc):
41 return self.is_dhcp_allowed_on(ifaceobj, syntax_check=True)
42
43 def is_dhcp_allowed_on(self, ifaceobj, syntax_check):
44 if ifaceobj.addr_method and 'dhcp' in ifaceobj.addr_method:
45 return utils.is_addr_ip_allowed_on(ifaceobj, syntax_check=True)
46 return True
47
15ef32ea 48 def _up(self, ifaceobj):
9e1b366e 49 # if dhclient is already running do not stop and start it
de4104b0
JF
50 dhclient4_running = self.dhclientcmd.is_running(ifaceobj.name)
51 dhclient6_running = self.dhclientcmd.is_running6(ifaceobj.name)
96dddc0e
JF
52
53 # today if we have an interface with both inet and inet6, if we
54 # remove the inet or inet6 or both then execute ifreload, we need
55 # to release/kill the appropriate dhclient(4/6) if they are running
56 self._down_stale_dhcp_config(ifaceobj, 'inet', dhclient4_running)
57 self._down_stale_dhcp_config(ifaceobj, 'inet6', dhclient6_running)
58
15ef32ea 59 try:
717cee31 60 dhclient_cmd_prefix = None
a0a8d7e0
SE
61 dhcp_wait = policymanager.policymanager_api.get_attr_default(
62 module_name=self.__class__.__name__, attr='dhcp-wait')
63 wait = not str(dhcp_wait).lower() == "no"
a252fb20
RD
64 inet6_ll_wait = policymanager.policymanager_api.get_iface_default(module_name=self.__class__.__name__, \
65 ifname=ifaceobj.name, attr='inet6-ll-wait')
66 try:
67 ll_wait_time = int(inet6_ll_wait)
68 except:
69 ll_wait_time = 10
70 pass
71
717cee31 72 vrf = ifaceobj.get_attr_value_first('vrf')
3cf287b8
RP
73 if (vrf and self.vrf_exec_cmd_prefix and
74 self.ipcmd.link_exists(vrf)):
717cee31
RP
75 dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix, vrf)
76
004d1e65 77 if 'inet' in ifaceobj.addr_family:
de4104b0
JF
78 if dhclient4_running:
79 self.logger.info('dhclient4 already running on %s. '
80 'Not restarting.' % ifaceobj.name)
81 else:
82 # First release any existing dhclient processes
15ef32ea 83 try:
de4104b0
JF
84 if not ifupdownflags.flags.PERFMODE:
85 self.dhclientcmd.stop(ifaceobj.name)
15ef32ea
RP
86 except:
87 pass
de4104b0
JF
88 self.dhclientcmd.start(ifaceobj.name, wait=wait,
89 cmd_prefix=dhclient_cmd_prefix)
90 if 'inet6' in ifaceobj.addr_family:
91 if dhclient6_running:
92 self.logger.info('dhclient6 already running on %s. '
93 'Not restarting.' % ifaceobj.name)
94 else:
95 accept_ra = ifaceobj.get_attr_value_first('accept_ra')
96 if accept_ra:
97 # XXX: Validate value
98 self.sysctl_set('net.ipv6.conf.%s' %ifaceobj.name +
99 '.accept_ra', accept_ra)
100 autoconf = ifaceobj.get_attr_value_first('autoconf')
101 if autoconf:
102 # XXX: Validate value
103 self.sysctl_set('net.ipv6.conf.%s' %ifaceobj.name +
104 '.autoconf', autoconf)
105 try:
106 self.dhclientcmd.stop6(ifaceobj.name)
107 except:
108 pass
109 #add delay before starting IPv6 dhclient to
110 #make sure the configured interface/link is up.
a252fb20
RD
111 if ll_wait_time:
112 timeout = ll_wait_time
113 time.sleep(1)
114 else:
115 timeout = ll_wait_time+1
116
de4104b0 117 while timeout:
d486dd0d
JF
118 addr_output = utils.exec_command('%s -6 addr show %s'
119 %(utils.ip_cmd, ifaceobj.name))
de4104b0
JF
120 r = re.search('inet6 .* scope link', addr_output)
121 if r:
122 self.dhclientcmd.start6(ifaceobj.name,
123 wait=wait,
124 cmd_prefix=dhclient_cmd_prefix)
125 return
a252fb20
RD
126 timeout -= 1
127 if timeout:
128 time.sleep(1)
b62a7e73 129
15ef32ea 130 except Exception, e:
bf3eda91 131 self.log_error(str(e), ifaceobj)
15ef32ea 132
96dddc0e
JF
133 def _down_stale_dhcp_config(self, ifaceobj, family, dhclientX_running):
134 addr_family = ifaceobj.addr_family
135 try:
136 if not family in ifaceobj.addr_family and dhclientX_running:
137 ifaceobj.addr_family = [family]
138 self._dhcp_down(ifaceobj)
139 except:
140 pass
141 finally:
142 ifaceobj.addr_family = addr_family
143
144 def _dhcp_down(self, ifaceobj):
717cee31
RP
145 dhclient_cmd_prefix = None
146 vrf = ifaceobj.get_attr_value_first('vrf')
3cf287b8
RP
147 if (vrf and self.vrf_exec_cmd_prefix and
148 self.ipcmd.link_exists(vrf)):
717cee31 149 dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix, vrf)
004d1e65 150 if 'inet6' in ifaceobj.addr_family:
20fd3a06 151 self.dhclientcmd.release6(ifaceobj.name, dhclient_cmd_prefix)
004d1e65 152 if 'inet' in ifaceobj.addr_family:
20fd3a06 153 self.dhclientcmd.release(ifaceobj.name, dhclient_cmd_prefix)
96dddc0e
JF
154
155 def _down(self, ifaceobj):
156 self._dhcp_down(ifaceobj)
15ef32ea
RP
157 self.ipcmd.link_down(ifaceobj.name)
158
159 def _query_check(self, ifaceobj, ifaceobjcurr):
004d1e65
JF
160 status = ifaceStatus.SUCCESS
161 dhcp_running = False
162
163 dhcp_v4 = self.dhclientcmd.is_running(ifaceobjcurr.name)
164 dhcp_v6 = self.dhclientcmd.is_running6(ifaceobjcurr.name)
165
166 if dhcp_v4:
167 dhcp_running = True
168 if 'inet' not in ifaceobj.addr_family and not dhcp_v6:
169 status = ifaceStatus.ERROR
15ef32ea 170 ifaceobjcurr.addr_method = 'dhcp'
004d1e65
JF
171 if dhcp_v6:
172 dhcp_running = True
173 if 'inet6' not in ifaceobj.addr_family and not dhcp_v4:
174 status = ifaceStatus.ERROR
15ef32ea 175 ifaceobjcurr.addr_method = 'dhcp'
004d1e65
JF
176 ifaceobjcurr.addr_family = ifaceobj.addr_family
177 if not dhcp_running:
178 ifaceobjcurr.addr_family = []
179 status = ifaceStatus.ERROR
180 ifaceobjcurr.status = status
15ef32ea
RP
181
182 def _query_running(self, ifaceobjrunning):
183 if not self.ipcmd.link_exists(ifaceobjrunning.name):
15ef32ea
RP
184 return
185 if self.dhclientcmd.is_running(ifaceobjrunning.name):
004d1e65 186 ifaceobjrunning.addr_family.append('inet')
15ef32ea 187 ifaceobjrunning.addr_method = 'dhcp'
004d1e65
JF
188 if self.dhclientcmd.is_running6(ifaceobjrunning.name):
189 ifaceobjrunning.addr_family.append('inet6')
15ef32ea
RP
190 ifaceobjrunning.addr_method = 'dhcp6'
191
192 _run_ops = {'up' : _up,
193 'down' : _down,
20fd3a06 194 'pre-down' : _down,
15ef32ea
RP
195 'query-checkcurr' : _query_check,
196 'query-running' : _query_running }
197
198 def get_ops(self):
199 """ returns list of ops supported by this module """
200 return self._run_ops.keys()
201
202 def _init_command_handlers(self):
203 if not self.ipcmd:
d486dd0d 204 self.ipcmd = LinkUtils()
15ef32ea 205
84ca006f 206 def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
15ef32ea
RP
207 """ run dhcp configuration on the interface object passed as argument
208
209 Args:
210 **ifaceobj** (object): iface object
211
212 **operation** (str): any of 'up', 'down', 'query-checkcurr',
213 'query-running'
214
215 Kwargs:
216 **query_ifaceobj** (object): query check ifaceobject. This is only
217 valid when op is 'query-checkcurr'. It is an object same as
218 ifaceobj, but contains running attribute values and its config
219 status. The modules can use it to return queried running state
220 of interfaces. status is success if the running state is same
221 as user required state in ifaceobj. error otherwise.
222 """
223 op_handler = self._run_ops.get(operation)
224 if not op_handler:
225 return
226 try:
227 if (operation != 'query-running' and
d486dd0d 228 (ifaceobj.addr_method != 'dhcp' and
15ef32ea
RP
229 ifaceobj.addr_method != 'dhcp6')):
230 return
231 except:
232 return
22b49c28
JF
233 if not self.is_dhcp_allowed_on(ifaceobj, syntax_check=False):
234 return
15ef32ea
RP
235 self._init_command_handlers()
236 if operation == 'query-checkcurr':
237 op_handler(self, ifaceobj, query_ifaceobj)
238 else:
239 op_handler(self, ifaceobj)